metkit-1.19.2/0000775000175000017500000000000015232567717013304 5ustar alastairalastairmetkit-1.19.2/VERSION0000664000175000017500000000000615232567717014350 0ustar alastairalastair1.19.2metkit-1.19.2/NOTICE0000664000175000017500000000063515232567717014214 0ustar alastairalastairmetkit ====== Copyright 1996- ECMWF This product is developed by the Development Section, European Centre for Medium-Range Weather Forecasts (ECMWF) - http://www.ecmwf.int Below is a list of software packages which are used inside: - No external package in currently included Please also read the cmake/NOTICE file bundled in the distribution packages, which lists the included third-party CMake macros. metkit-1.19.2/tests/0000775000175000017500000000000015232567717014446 5ustar alastairalastairmetkit-1.19.2/tests/pl.grib0000664000175000017500000000606015232567717015730 0ustar alastairalastairGRIBb! 0001H Ĵ0ĴC1-1-"dCg졀 I;2SfaNbZ'K=5D211-&FT"Arc0}~>yf.JT6 5E?zI$HH>8_+k`Wgcuvz!fS@ZQ`re`_`Z_YiO>~g}j;izŁ$|bvKuvz!}Lяz}Ux:8I`5?wn|Ez={vjmsy{.Z~~~%zmPmj c _dpj;o?rQyO}~|yut]syu,nkKk>oQtv{kTelˋX~wpkJh-gimBr0w~gwۆ7777GRIBb! 0001H Ĵ0ĴC1-1-"dCg졀 I;2SfaNbZ'K=5D211-&FT"Arc0}~>yf.JT6 5E?zI$HH>8_+k`Wgcuvz!fS@ZQ`re`_`Z_YiO>~g}j;izŁ$|bvKuvz!}Lяz}Ux:8I`5?wn|Ez={vjmsy{.Z~~~%zmPmj c _dpj;o?rQyO}~|yut]syu,nkKk>oQtv{kTelˋX~wpkJh-gimBr0w~gwۆ7777GRIBb! 0001H Ĵ0ĴC1-1-"dCg졀 I;2SfaNbZ'K=5D211-&FT"Arc0}~>yf.JT6 5E?zI$HH>8_+k`Wgcuvz!fS@ZQ`re`_`Z_YiO>~g}j;izŁ$|bvKuvz!}Lяz}Ux:8I`5?wn|Ez={vjmsy{.Z~~~%zmPmj c _dpj;o?rQyO}~|yut]syu,nkKk>oQtv{kTelˋX~wpkJh-gimBr0w~gwۆ7777GRIBb! 0001H Ĵ0ĴC1-1-"dCg졀 I;2SfaNbZ'K=5D211-&FT"Arc0}~>yf.JT6 5E?zI$HH>8_+k`Wgcuvz!fS@ZQ`re`_`Z_YiO>~g}j;izŁ$|bvKuvz!}Lяz}Ux:8I`5?wn|Ez={vjmsy{.Z~~~%zmPmj c _dpj;o?rQyO}~|yut]syu,nkKk>oQtv{kTelˋX~wpkJh-gimBr0w~gwۆ7777GRIBb! 0001H Ĵ0ĴC1-1-"dCg졀 I;2SfaNbZ'K=5D211-&FT"Arc0}~>yf.JT6 5E?zI$HH>8_+k`Wgcuvz!fS@ZQ`re`_`Z_YiO>~g}j;izŁ$|bvKuvz!}Lяz}Ux:8I`5?wn|Ez={vjmsy{.Z~~~%zmPmj c _dpj;o?rQyO}~|yut]syu,nkKk>oQtv{kTelˋX~wpkJh-gimBr0w~gwۆ7777GRIBb! 0001H Ĵ0ĴC1-1-"dN Cg졀 I;2SfaNbZ'K=5D211-&FT"Arc0}~>yf.JT6 5E?zI$HH>8_+k`Wgcuvz!fS@ZQ`re`_`Z_YiO>~g}j;izŁ$|bvKuvz!}Lяz}Ux:8I`5?wn|Ez={vjmsy{.Z~~~%zmPmj c _dpj;o?rQyO}~|yut]syu,nkKk>oQtv{kTelˋX~wpkJh-gimBr0w~gwۆ7777metkit-1.19.2/tests/test_odbsplitter.cc0000664000175000017500000000752215232567717020355 0ustar alastairalastair/* * (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 "eckit/io/BufferList.h" #include "eckit/io/FileHandle.h" #include "eckit/message/Reader.h" #include "eckit/testing/Test.h" #include using namespace eckit::testing; namespace metkit { namespace grib { namespace test { //---------------------------------------------------------------------------------------------------------------------- class NonSeekFileHandle : public eckit::FileHandle { using eckit::FileHandle::FileHandle; bool canSeek() const override { return false; } eckit::Offset seek(const eckit::Offset&) override { NOTIMP; } }; //---------------------------------------------------------------------------------------------------------------------- CASE("read multiple matched odb frames") { // This ODB is formed of two frames with matched metadata. We should read both frames into // one message. eckit::Buffer msgdata; { NonSeekFileHandle fh("multiodb.odb"); fh.openForRead(); eckit::AutoClose closer(fh); eckit::message::Reader reader(fh); eckit::message::Message msg; msg = reader.next(); ASSERT(msg); msgdata = eckit::Buffer{msg.data(), msg.length()}; // There is only one Message in the file msg = reader.next(); EXPECT(!msg); } // And read the data in in one blob eckit::Buffer comparedata; { NonSeekFileHandle fh("multiodb.odb"); fh.openForRead(); eckit::AutoClose closer(fh); size_t expected_size = fh.size(); comparedata.resize(expected_size); EXPECT(expected_size > 0); EXPECT(fh.read(comparedata.data(), expected_size) == expected_size); } EXPECT(comparedata.size() == msgdata.size()); EXPECT(::memcmp(comparedata.data(), msgdata.data(), comparedata.size()) == 0); eckit::Log::info() << "odb size: " << msgdata.size() << std::endl; } CASE("read multiple matched odb frames") { // This ODB file is formed of four frames. Two pairs of matched metadata. This should be deconstructed into // two messages. eckit::BufferList msgdata; { eckit::FileHandle fh("multiodb2.odb"); fh.openForRead(); eckit::AutoClose closer(fh); eckit::message::Reader reader(fh); eckit::message::Message msg; for (int i = 0; i < 2; ++i) { msg = reader.next(); ASSERT(msg); msgdata.append(eckit::Buffer{msg.data(), msg.length()}); } // There is only one Message in the file msg = reader.next(); EXPECT(!msg); } // And read the data in in one blob eckit::Buffer comparedata; { eckit::FileHandle fh("multiodb2.odb"); fh.openForRead(); eckit::AutoClose closer(fh); size_t expected_size = fh.size(); comparedata.resize(expected_size); EXPECT(expected_size > 0); EXPECT(fh.read(comparedata.data(), expected_size) == expected_size); } eckit::Buffer combineddata = msgdata.consolidate(); EXPECT(comparedata.size() == combineddata.size()); EXPECT(::memcmp(comparedata.data(), combineddata.data(), combineddata.size()) == 0); eckit::Log::info() << "odb size: " << combineddata.size() << std::endl; } //---------------------------------------------------------------------------------------------------------------------- } // namespace test } // namespace grib } // namespace metkit int main(int argc, char** argv) { return run_tests(argc, argv); } metkit-1.19.2/tests/netcdf/0000775000175000017500000000000015232567717015711 5ustar alastairalastairmetkit-1.19.2/tests/netcdf/signature_merge_1.sh0000775000175000017500000000214715232567717021654 0ustar alastairalastair#!/bin/bash set -eaux rm -f *.nc ncgen -b - <CCCCmetkit-1.19.2/tests/netcdf/simple_three_way_merge.sh0000775000175000017500000000155315232567717022773 0ustar alastairalastair#!/bin/bash set -eaux ncgen -b - < #include #include #include "eckit/testing/Test.h" #include "eckit/value/Value.h" #include "metkit/mars/TypeDate.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars::test { //----------------------------------------------------------------------------- CASE("test_list_types") { std::stringstream ss; TypesFactory::list(ss); std::cout << ss.str() << std::endl; EXPECT(ss.str() == std::string("[any,date,enum,expver,float,integer,lowercase,mixed,param,range,regex,time,to-by-" "list,to-by-list-float,to-by-list-quantile]")); } CASE("test_build") { eckit::ValueMap settings; settings["type"] = "date"; Type* t1(TypesFactory::build("abcd", eckit::Value(settings))); EXPECT(t1 != 0); t1->attach(); // Check that we have obtained the correct type EXPECT(dynamic_cast(t1) != 0); // Clean up, taking into account that ~Type is protected. t1->detach(); } } // namespace metkit::mars::test //----------------------------------------------------------------------------- int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_expand.cc0000664000175000017500000017475415232567717017315 0ustar alastairalastair/* * (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. */ /// @file test_expand.cc /// @date Jan 2016 /// @author Florian Rathgeber /// @author Emanuele Danovaro #include #include #include #include "eckit/filesystem/LocalPathName.h" #include "eckit/filesystem/StdDir.h" #include "eckit/types/Date.h" #include "eckit/utils/StringTools.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/MarsParser.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Type.h" #include "eckit/testing/Test.h" #include "eckit/utils/Tokenizer.h" using namespace eckit::testing; namespace metkit::mars::test { namespace { using ExpectedVals = std::map>; /** * @brief We can specify a set of keys which should be ignored when not specified in the user request. * In case the user requests defines them, check them regardless. This is mean to ignore auto-injected * keys e.g. for resol. */ std::set ignore_{"repres", "resol"}; std::string date(long d) { if (d <= 0) { eckit::Date day(d); d = day.yyyymmdd(); } return std::to_string(d); } std::string date(std::string d) { if (d == "0" || d[0] == '-') { long dd = std::stol(d); eckit::Date day(dd); return std::to_string(day.yyyymmdd()); } return d; } struct ExpectedRequest { ExpectedRequest() = default; ExpectedRequest(std::string vrb, ExpectedVals vv, std::vector dd) : verb(vrb), vals(vv), dates(dd) {} ExpectedRequest(std::string vrb, ExpectedVals vv, std::vector dd) : verb(vrb), vals(vv) { dates.reserve(dd.size()); for (long d : dd) { dates.push_back(date(d)); } } std::string verb; ExpectedVals vals; std::vector dates; }; } // namespace //----------------------------------------------------------------------------- void expand(const MarsRequest& r, const ExpectedRequest& expected, std::set ignore) { try { EXPECT_EQUAL(expected.verb, r.verb()); for (const auto& e : expected.vals) { if (!r.has(e.first)) { if (ignore.find(e.first) != ignore.end()) { continue; } std::cerr << eckit::Colour::red << "Missing keyword: " << e.first << eckit::Colour::reset << std::endl; } EXPECT(r.has(e.first)); auto vv = r.values(e.first); if (e.first != "date") { // dates are verified at a later stage EXPECT_EQUAL(e.second.size(), vv.size()); } for (int i = 0; i < vv.size(); i++) { if (e.first == "grid") { EXPECT_EQUAL(eckit::StringTools::upper(e.second.at(i)), vv.at(i)); } else { if (e.first == "resol") { EXPECT_EQUAL(e.second.at(i), eckit::StringTools::lower(vv.at(i))); } else { EXPECT_EQUAL(e.second.at(i), vv.at(i)); } } } } if (expected.dates.size() > 0) { EXPECT(r.has("date")); auto dd = r.values("date"); EXPECT_EQUAL(expected.dates.size(), dd.size()); for (int i = 0; i < expected.dates.size(); i++) { EXPECT_EQUAL(date(expected.dates.at(i)), dd.at(i)); } } } catch (...) { std::cerr << "Error comparing " << r << " with " << expected.vals << " dates " << expected.dates << std::endl; throw; } } void expand(const MarsRequest& r, const std::string& verb, const ExpectedVals& expected, std::vector& dates, std::set ignore) { expand(r, {verb, expected, dates}, ignore); } void expand(const std::string& text, const std::string& verb, const ExpectedVals& expected, std::vector dates, bool strict = false) { MarsRequest r = MarsRequest::parse(text, strict); std::set ignore; std::string tt = eckit::StringTools::lower(text); for (const auto& i : ignore_) { auto idx = tt.find(i); if (idx == std::string::npos) { ignore.insert(i); } } expand(r, {verb, expected, dates}, ignore); } void parse(const std::string& req, ExpectedRequest& expected) { eckit::Tokenizer c(","); eckit::Tokenizer e("="); eckit::Tokenizer s("/"); eckit::StringList tokens; c(req, tokens); for (const auto& t : tokens) { auto tt = eckit::StringTools::trim(t); if (expected.verb.empty()) { expected.verb = eckit::StringTools::lower(tt); continue; } eckit::StringList kv; e(tt, kv); EXPECT_EQUAL(2, kv.size()); auto key = eckit::StringTools::lower(eckit::StringTools::trim(kv[0])); if (key == "date") { EXPECT_EQUAL(0, expected.dates.size()); } eckit::StringList vals; s(kv[1], vals); std::vector vv; for (auto v : vals) { auto val = eckit::StringTools::trim(v); if (key != "source" && key != "target" && key != "intgrid" && key != "grid") { val = eckit::StringTools::lower(val); } if (key == "date") { expected.dates.push_back(date(val)); } else { vv.push_back(val); } } if (key != "date") { expected.vals.emplace(key, vv); } } } void expand(const std::string& text, const std::string& expected, bool strict = false, std::vector dates = {}) { ExpectedRequest out; out.dates.reserve(dates.size()); for (long d : dates) { out.dates.push_back(date(d)); } parse(expected, out); MarsRequest r = MarsRequest::parse(text, strict); std::set ignore; std::string tt = eckit::StringTools::lower(text); for (const auto& i : ignore_) { auto idx = tt.find(i); if (idx == std::string::npos) { ignore.insert(i); } } expand(r, out, ignore); } void expand(const std::string& text, const std::vector& expected, bool strict = false, std::vector> dates = {}) { ExpectedVals out; std::istringstream in(text); std::vector reqs = MarsRequest::parse(in, strict); std::set ignore; std::string tt = eckit::StringTools::lower(text); for (const auto& i : ignore_) { auto idx = tt.find(i); if (idx == std::string::npos) { ignore.insert(i); } } ASSERT(dates.size() == 0 || dates.size() == expected.size()); EXPECT_EQUAL(expected.size(), reqs.size()); for (size_t i = 0; i < expected.size(); i++) { ExpectedRequest expectedReq; if (dates.size() != 0) { expectedReq.dates.reserve(dates.at(i).size()); for (long d : dates.at(i)) { expectedReq.dates.push_back(date(d)); } } parse(expected.at(i), expectedReq); expand(reqs.at(i), expectedReq, ignore); } } CASE("test_metkit_expand_1") { const char* text = "ret,date=-5/to/-1"; ExpectedVals expected{{"class", {"od"}}, {"domain", {"g"}}, {"expver", {"0001"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"levtype", {"pl"}}, {"param", {"129"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1200"}}, {"type", {"an"}}}; expand(text, "retrieve", expected, {-5, -4, -3, -2, -1}); const char* text2 = "ret,date=-5/to/-1."; expand(text2, "retrieve", expected, {-5, -4, -3, -2, -1}); const char* expectedStr = "RETRIEVE,CLASS=OD,TYPE=AN,STREAM=OPER,EXPVER=0001,REPRES=SH,LEVTYPE=PL,LEVELIST=1000/850/700/500/400/" "300,PARAM=129,TIME=1200,STEP=0,DOMAIN=G"; expand(text, expectedStr, false, {-5, -4, -3, -2, -1}); } CASE("test_metkit_expand_2") { { const char* text = "ret,date=-1"; ExpectedVals expected{{"class", {"od"}}, {"domain", {"g"}}, {"expver", {"0001"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"levtype", {"pl"}}, {"param", {"129"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1200"}}, {"type", {"an"}}}; expand(text, "retrieve", expected, {-1}); } { const char* text = "ret,levtype=ml"; ExpectedVals expected{{"class", {"od"}}, {"domain", {"g"}}, {"expver", {"0001"}}, {"levelist", {"1"}}, {"levtype", {"ml"}}, {"param", {"129"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1200"}}, {"type", {"an"}}}; expand(text, "retrieve", expected, {-1}); } } CASE("test_metkit_expand_3") { const char* text = "ret,date=-5/to/-1,grid=n640"; ExpectedVals expected{{"class", {"od"}}, {"domain", {"g"}}, {"expver", {"0001"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"levtype", {"pl"}}, {"param", {"129"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1200"}}, {"type", {"an"}}, {"grid", {"N640"}}}; expand(text, "retrieve", expected, {-5, -4, -3, -2, -1}); } CASE("test_metkit_expand_4") { const char* text = "ret,date=-5/to/-1,grid=o640"; ExpectedVals expected{{"class", {"od"}}, {"domain", {"g"}}, {"expver", {"0001"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"levtype", {"pl"}}, {"param", {"129"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1200"}}, {"type", {"an"}}, {"grid", {"O640"}}}; expand(text, "retrieve", expected, {-5, -4, -3, -2, -1}); } CASE("test_metkit_expand_5") { const char* text = "retrieve,class=od,date=20050601,diagnostic=1,expver=1,iteration=0,levelist=1,levtype=ml,param=155.129,stream=" "sens,time=1200,type=sg"; ExpectedVals expected{{"class", {"od"}}, {"diagnostic", {"1"}}, {"domain", {"g"}}, {"expver", {"0001"}}, {"iteration", {"0"}}, {"levelist", {"1"}}, {"levtype", {"ml"}}, {"param", {"129155"}}, {"step", {"0"}}, {"stream", {"sens"}}, {"time", {"1200"}}, {"type", {"sg"}}}; expand(text, "retrieve", expected, {20050601}); } CASE("test_metkit_expand_6") { const char* text = "retrieve,class=rd,expver=hl1m,stream=oper,date=20000801,time=0000,domain=g,type=fc,levtype=pl,step=24,param=" "129,levelist=1/to/31"; ExpectedVals expected{{"class", {"rd"}}, {"expver", {"hl1m"}}, {"stream", {"oper"}}, {"time", {"0000"}}, {"domain", {"g"}}, {"type", {"fc"}}, {"levtype", {"pl"}}, {"step", {"24"}}, {"param", {"129"}}}; std::vector levelist; for (int i = 1; i <= 31; i++) { levelist.push_back(std::to_string(i)); } expected["levelist"] = levelist; expand(text, "retrieve", expected, {20000801}); } CASE("test_metkit_expand_7") { const char* text = "retrieve,class=rd,expver=hl1m,stream=oper,date=20000801,time=0000,domain=g,type=fc,levtype=pl,step=24,param=" "129,levelist=0.01/0.7"; ExpectedVals expected{{"class", {"rd"}}, {"expver", {"hl1m"}}, {"stream", {"oper"}}, {"time", {"0000"}}, {"domain", {"g"}}, {"type", {"fc"}}, {"levtype", {"pl"}}, {"step", {"24"}}, {"param", {"129"}}, {"levelist", {".01", ".7"}}}; expand(text, "retrieve", expected, {20000801}); } CASE("test_metkit_expand_8") { const char* text = "retrieve,class=rd,expver=hl1m,stream=oper,date=20000801,time=0000,domain=g,type=fc,levtype=pl,step=24,param=" "129,levelist=0.1/to/0.7/by/0.2"; ExpectedVals expected{{"class", {"rd"}}, {"expver", {"hl1m"}}, {"stream", {"oper"}}, {"time", {"0000"}}, {"domain", {"g"}}, {"type", {"fc"}}, {"levtype", {"pl"}}, {"step", {"24"}}, {"param", {"129"}}, {"levelist", {".1", ".3", ".5", ".7"}}}; expand(text, "retrieve", expected, {20000801}); } CASE("test_metkit_expand_9_strict") { const char* text = "retrieve,class=rd,expver=hm1u,stream=weeh,time=0000,date=20210101,domain=g,hdate=20190101"; { std::istringstream in(text); MarsParser parser(in); MarsExpansion expand(false, false); std::vector v = expand.expand(parser.parse()); EXPECT_EQUAL(v.size(), 1); } { std::istringstream in(text); MarsParser parser(in); MarsExpansion expand(false, true); std::vector v = expand.expand(parser.parse()); EXPECT_EQUAL(v.size(), 1); } { const char* text = "retrieve,class=od,expver=1,date=20240304,time=0,type=fc,levtype=sfc,levelist=1/2/" "3,step=0,param=2t,target=out"; EXPECT_THROWS_AS(MarsRequest::parse(text, true), eckit::UserError); const char* expected = "RETRIEVE,CLASS=OD,TYPE=FC,STREAM=OPER,EXPVER=0001,REPRES=GG,LEVTYPE=SFC,PARAM=167,DATE=20240304,TIME=0000," "STEP=0,DOMAIN=G,TARGET=out"; expand(text, expected, false); } { const char* text = "retrieve,class=od,expver=1,stream=enfo,date=20240304,time=0,type=cf,levtype=sfc,levelist=1/2/" "3,step=0,param=2t,number=1/2/3,target=out"; EXPECT_THROWS_AS(MarsRequest::parse(text, true), eckit::UserError); const char* expected = "RETRIEVE,CLASS=OD,TYPE=CF,STREAM=ENFO,EXPVER=0001,REPRES=SH,LEVTYPE=SFC,PARAM=167,DATE=20240304,TIME=0000," "STEP=0,DOMAIN=G,TARGET=out"; expand(text, expected, false); } } CASE("test_metkit_expand_10_strict") { const char* text = "retrieve,class=rd,expver=hm1u,stream=wees,time=0000,date=20210101,domain=g,hdate=20190101"; { std::istringstream in(text); MarsParser parser(in); MarsExpansion expand(false, false); std::vector v = expand.expand(parser.parse()); EXPECT_EQUAL(v.size(), 1); } } CASE("test_metkit_expand_multirequest-1") { const std::string text = "ret,date=-5/to/-2.\nret,date=-1"; std::istringstream in(text); std::vector reqs = MarsRequest::parse(in, false); EXPECT_EQUAL(reqs.size(), 2); ExpectedVals expected{{"class", {"od"}}, {"domain", {"g"}}, {"expver", {"0001"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"levtype", {"pl"}}, {"param", {"129"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1200"}}, {"type", {"an"}}}; expand(reqs.at(0), {"retrieve", expected, std::vector{-5, -4, -3, -2}}, ignore_); expand(reqs.at(1), {"retrieve", expected, std::vector{-1}}, ignore_); } CASE("test_metkit_expand_multirequest-2") { const std::string text = "ret,date=-5/to/-2.\nret,date=-1"; std::string expectedStr = "retrieve,class=od,domain=g,expver=0001,levelist=1000/850/700/500/400/" "300,levtype=pl,param=129,step=0,stream=oper,time=1200,type=an"; expand(text, {expectedStr, expectedStr}, false, {{-5, -4, -3, -2}, {-1}}); } CASE("test_metkit_expand_multirequest-3") { const char* text = "retrieve,accuracy=16,area=14.8/-19.6/-14.5/19.8,class=od,date=20230810,expver=1,grid=0.09/0.09,levelist=1/" "to/137,levtype=ml,number=-1,param=z,process=local,rotation=-78.8/-61.0,step=000,stream=scda,time=18," "type=an,target=\"reference.ect1qF.data\"\n" "ret,rotation=off,target=\"out\""; const char* expected1 = "RETRIEVE,CLASS=OD,TYPE=AN,STREAM=SCDA,EXPVER=0001,REPRES=SH,LEVTYPE=ML,LEVELIST=1/2/3/4/5/6/7/8/9/10/11/" "12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/" "47/48/49/50/51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/" "82/83/84/85/86/87/88/89/90/91/92/93/94/95/96/97/98/99/100/101/102/103/104/105/106/107/108/109/110/111/112/" "113/114/115/116/117/118/119/120/121/122/123/124/125/126/127/128/129/130/131/132/133/134/135/136/137," "PARAM=129,DATE=20230810,TIME=1800,STEP=0,DOMAIN=G,TARGET=reference.ect1qF.data,RESOL=AUTO,ACCURACY=16," "AREA=14.8/-19.6/-14.5/19.8,ROTATION=-78.8/-61,GRID=.09/.09,PROCESS=LOCAL"; const char* expected2 = "RETRIEVE,CLASS=OD,TYPE=AN,STREAM=SCDA,EXPVER=0001,REPRES=SH,LEVTYPE=ML,LEVELIST=1/2/3/4/5/6/7/8/9/10/11/" "12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/" "47/48/49/50/51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/" "82/83/84/85/86/87/88/89/90/91/92/93/94/95/96/97/98/99/100/101/102/103/104/105/106/107/108/109/110/111/112/" "113/114/115/116/117/118/119/120/121/122/123/124/125/126/127/128/129/130/131/132/133/134/135/136/137," "PARAM=129,DATE=20230810,TIME=1800,STEP=0,DOMAIN=G,TARGET=out,RESOL=AUTO,ACCURACY=16," "AREA=14.8/-19.6/-14.5/19.8,GRID=.09/.09,PROCESS=LOCAL"; expand(text, std::vector{expected1, expected2}); } void expandKeyThrows(const std::string& key, std::vector values) { static metkit::mars::MarsLanguage language("retrieve"); metkit::mars::Type* t = language.type(key); EXPECT_THROWS_AS(t->expand(values), eckit::BadValue); } void expandKey(const std::string& key, std::vector values, std::vector expected) { static metkit::mars::MarsLanguage language("retrieve"); metkit::mars::Type* t = language.type(key); t->expand(values); EXPECT_EQUAL(expected, values); } void quantileThrows(std::vector values) { expandKeyThrows("quantile", values); } void quantile(std::vector values, std::vector expected) { expandKey("quantile", values, expected); } CASE("test_metkit_expand_11_quantile") { quantileThrows({"-1:5"}); quantileThrows({"0:-5"}); quantileThrows({"6:5"}); quantileThrows({"0:12"}); quantile({"2:5"}, {"2:5"}); quantile({"0:2", "1:2", "2:2"}, {"0:2", "1:2", "2:2"}); quantile({"0:2", "1:3", "2:5"}, {"0:2", "1:3", "2:5"}); quantileThrows({"to", "5:10"}); quantileThrows({"3:5", "to"}); quantileThrows({"3:5", "to", "5:10"}); quantileThrows({"1:5", "to", "3:5", "by"}); quantile({"0:5", "to", "0:5"}, {"0:5"}); quantile({"3:3", "to", "3:3"}, {"3:3"}); quantile({"0:5", "to", "5:5"}, {"0:5", "1:5", "2:5", "3:5", "4:5", "5:5"}); quantile({"0:5", "to", "5:5", "by", "1"}, {"0:5", "1:5", "2:5", "3:5", "4:5", "5:5"}); quantile({"0:5", "to", "5:5", "by", "2"}, {"0:5", "2:5", "4:5"}); quantile({"0:5", "to", "5:5", "by", "3"}, {"0:5", "3:5"}); quantile({"0:5", "to", "5:5", "by", "5"}, {"0:5", "5:5"}); quantile({"0:5", "to", "5:5", "by", "6"}, {"0:5"}); quantile({"2:5", "to", "5:5", "by", "2"}, {"2:5", "4:5"}); quantile({"3:5", "to", "5:5", "by", "2"}, {"3:5", "5:5"}); quantile({"4:5", "to", "5:5", "by", "2"}, {"4:5"}); quantile({"0:10", "3:10", "to", "7:10", "by", "2", "10:10"}, {"0:10", "3:10", "5:10", "7:10", "10:10"}); quantile({"3:5", "to", "2:5"}, {"3:5", "2:5"}); } void timeThrows(std::vector values) { expandKeyThrows("time", values); } void time(std::vector values, std::vector expected) { expandKey("time", values, expected); } CASE("test_metkit_expand_12_time") { timeThrows({"87"}); timeThrows({"000012"}); timeThrows({"0:0:12"}); timeThrows({"12s"}); time({"0"}, {"0000"}); time({"0", "1", "6", "12", "18"}, {"0000", "0100", "0600", "1200", "1800"}); time({"0", "1", "12"}, {"0000", "0100", "1200"}); time({"0030"}, {"0030"}); time({"30m"}, {"0030"}); time({"00:30", "1:30", "02:50"}, {"0030", "0130", "0250"}); time({"0h", "3h", "120m", "170m"}, {"0000", "0300", "0200", "0250"}); timeThrows({"to", "5"}); timeThrows({"3", "to"}); timeThrows({"1", "to", "3", "by"}); time({"0", "to", "0"}, {"0000"}); time({"12", "to", "12"}, {"1200"}); time({"0", "to", "12"}, {"0000", "0600", "1200"}); time({"3", "to", "2"}, {"0300"}); time({"12", "to", "4"}, {"1200", "0600"}); time({"0", "to", "6", "by", "1"}, {"0000", "0100", "0200", "0300", "0400", "0500", "0600"}); time({"0", "to", "6", "by", "2"}, {"0000", "0200", "0400", "0600"}); time({"0", "to", "6", "by", "3"}, {"0000", "0300", "0600"}); time({"0", "to", "6", "by", "4"}, {"0000", "0400"}); time({"0", "to", "6", "by", "5"}, {"0000", "0500"}); time({"0", "to", "6", "by", "6"}, {"0000", "0600"}); time({"6", "to", "18"}, {"0600", "1200", "1800"}); time({"1", "to", "6", "by", "1"}, {"0100", "0200", "0300", "0400", "0500", "0600"}); time({"1", "to", "6", "by", "2"}, {"0100", "0300", "0500"}); time({"1", "to", "6", "by", "3"}, {"0100", "0400"}); time({"1", "to", "6", "by", "4"}, {"0100", "0500"}); time({"1", "to", "6", "by", "5"}, {"0100", "0600"}); time({"1", "to", "6", "by", "6"}, {"0100"}); time({"1", "to", "3h", "by", "30m"}, {"0100", "0130", "0200", "0230", "0300"}); } void stepThrows(std::vector values) { expandKeyThrows("step", values); } void step(std::string valuesStr, std::string expectedStr) { eckit::Tokenizer parse("/"); std::vector values; std::vector expected; parse(valuesStr, values); parse(expectedStr, expected); expandKey("step", values, expected); } CASE("test_metkit_expand_13_step") { step("0", "0"); step("1", "1"); step("24", "24"); step("144", "144"); step("012", "12"); step("12:30", "12h30m"); step("1:00", "1"); step("1:0:0", "1"); step("1h", "1"); step("60m", "1"); step("1h60m", "2"); step("0:5", "5m"); step("0:05", "5m"); step("0:05:0", "5m"); step("0:06", "6m"); step("0:10", "10m"); step("0:12", "12m"); step("0:15", "15m"); step("0:20", "20m"); step("0:25", "25m"); step("0:30", "30m"); step("0:35", "35m"); step("0:40", "40m"); step("0:45", "45m"); step("0:50", "50m"); step("0:55", "55m"); step("0-24", "0-24"); step("0-24s", "0-24s"); step("0-120s", "0-2m"); step("0s-120m", "0-2"); step("1-2", "1-2"); step("30m-1", "30m-1"); step("30m-90m", "30m-1h30m"); step("0/to/24/by/3", "0/3/6/9/12/15/18/21/24"); step("12/to/48/by/12", "12/24/36/48"); step("12/to/47/by/12", "12/24/36"); step("40m/to/2/by/10m", "40m/50m/1/1h10m/1h20m/1h30m/1h40m/1h50m/2"); step("0/to/6/by/30m", "0/30m/1/1h30m/2/2h30m/3/3h30m/4/4h30m/5/5h30m/6"); step("0-6/to/18-24/by/6", "0-6/6-12/12-18/18-24"); step("0-24/to/48-72/by/24", "0-24/24-48/48-72"); step("0/to/24/by/3/0-6/to/18-24/by/6", "0/3/6/9/12/15/18/21/24/0-6/6-12/12-18/18-24"); step("0-24/to/24-48/by/90m", "0-24/1h30m-25h30m/3-27/4h30m-28h30m/6-30/7h30m-31h30m/9-33/10h30m-34h30m/12-36/13h30m-37h30m/15-39/" "16h30m-40h30m/18-42/19h30m-43h30m/21-45/22h30m-46h30m/24-48"); } void activity(std::vector values, std::vector expected) { expandKey("activity", values, expected); } void experiment(std::vector values, std::vector expected) { expandKey("experiment", values, expected); } void model(std::vector values, std::vector expected) { expandKey("model", values, expected); } CASE("test_metkit_expand_lowercase") { activity({"ScenarioMIP"}, {"scenariomip"}); activity({"CMIP6"}, {"cmip6"}); activity({"ScenarioMIP"}, {"scenariomip"}); activity({"cmip6"}, {"cmip6"}); experiment({"SSP3-7.0"}, {"ssp3-7.0"}); experiment({"ssp3-7.0"}, {"ssp3-7.0"}); experiment({"hist"}, {"hist"}); model({"IFS-NEMO"}, {"ifs-nemo"}); model({"IFS"}, {"ifs"}); model({"ifs-nemo"}, {"ifs-nemo"}); model({"ifs"}, {"ifs"}); model({"ICON"}, {"icon"}); model({"icon"}, {"icon"}); } CASE("test_metkit_expand_param") { { const char* text = "retrieve,class=od,expver=0079,stream=enfo,date=-1,time=00/12,type=pf,levtype=sfc,step=24,number=1/to/" "2,param=mucin/mucape/tprate"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 3); EXPECT_EQUAL(params[0], "228236"); EXPECT_EQUAL(params[1], "228235"); EXPECT_EQUAL(params[2], "260048"); } { const char* text = "retrieve,class=od,expver=0079,stream=enfh,date=-1,time=00/12,type=fcmean,levtype=sfc,step=24,number=1/to/" "2,param=mucin/mucape/tprate"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 3); EXPECT_EQUAL(params[0], "228236"); EXPECT_EQUAL(params[1], "228235"); EXPECT_EQUAL(params[2], "172228"); } { const char* text = "retrieve,class=od,expver=1,stream=wave,date=-1,time=00/12,type=an,levtype=sfc,step=24,param=2dfd "; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 1); EXPECT_EQUAL(params[0], "140251"); } { const char* text = "retrieve,class=od,expver=1,stream=enwh,date=-1,time=00/12,type=cf,levtype=sfc,step=24,param=tmax"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 1); EXPECT_EQUAL(params[0], "140217"); } { const char* text = "retrieve,class=ai,expver=1,stream=oper,model=aifs-singlw,date=-1,time=00/" "12,type=pf,levtype=pl,step=24,param=t"; EXPECT_THROWS_AS(MarsRequest::parse(text, true), eckit::UserError); } { const char* text = "retrieve,class=ai,expver=1,stream=oper,model=aifs-single,date=-1,time=00/" "12,type=pf,levtype=pl,step=24,param=t"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 1); EXPECT_EQUAL(params[0], "130"); } { const char* text = "retrieve,class=od,date=20240723,domain=g,expver=0079,levtype=sfc,param=asn/cp/lsp/sf/tcc/" "tp,step=0,stream=oper,time=0000,type=fc"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 6); EXPECT_EQUAL(params[0], "32"); EXPECT_EQUAL(params[1], "143"); EXPECT_EQUAL(params[2], "142"); EXPECT_EQUAL(params[3], "144"); EXPECT_EQUAL(params[4], "164"); EXPECT_EQUAL(params[5], "228"); } { const char* text = "retrieve,class=od,expver=1,stream=msmm,date=-1,time=0000,type=em,levtype=sfc,step=24,param=e"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 1); EXPECT_EQUAL(params[0], "172182"); } { const char* text = "retrieve,class=od,expver=1,stream=msmm,date=-1,time=0000,type=em,levtype=sfc,step=24,param=e/erate"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 2); EXPECT_EQUAL(params[0], "172182"); EXPECT_EQUAL(params[1], "172182"); } { const char* text = "retrieve,class=od,expver=1,stream=enwh,date=-1,time=0000,type=pf,levtype=sfc,step=24,param=sh10"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 1); EXPECT_EQUAL(params[0], "140120"); } { const char* text = "retrieve,class=od,expver=1,stream=enwh,date=-1,time=0000,type=pf,levtype=sfc,step=24,param=p1ww"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 1); EXPECT_EQUAL(params[0], "140223"); } { const char* text = "retrieve,class=od,expver=1,stream=waef,date=-1,time=0000,type=cf,levtype=sfc,step=24,param=WSK/MWP"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 2); EXPECT_EQUAL(params[0], "140252"); EXPECT_EQUAL(params[1], "140232"); } { const char* text = "retrieve,class=od,expver=1,stream=eefo,date=-1,time=0000,type=fcmean,levtype=sfc,step=24,param=MSL"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 1); EXPECT_EQUAL(params[0], "151"); } { const char* text = "retrieve,class=od,expver=1,stream=eefo,date=-1,time=0000,type=fcmean,levtype=sfc,step=24,param=strda"; MarsRequest r = MarsRequest::parse(text); auto params = r.values("param"); EXPECT_EQUAL(params.size(), 1); EXPECT_EQUAL(params[0], "171175"); } } CASE("test_metkit_expand_d1") { { const char* text = "retrieve,class=d1,dataset=extremes-dt,date=-1"; ExpectedVals expected{{"class", {"d1"}}, {"dataset", {"extremes-dt"}}, {"expver", {"0001"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"levtype", {"pl"}}, {"param", {"129"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1200"}}, {"type", {"an"}}}; expand(text, "retrieve", expected, {-1}); } { const char* text = "retrieve,class=d1,dataset=climate-dt,levtype=pl,date=20000101,activity=CMIP6,experiment=hist,model=IFS-" "NEMO,generation=1,realization=1,resolution=high,stream=clte,type=fc,param=134/137"; ExpectedVals expected{{"class", {"d1"}}, {"dataset", {"climate-dt"}}, {"activity", {"cmip6"}}, {"experiment", {"hist"}}, {"model", {"ifs-nemo"}}, {"generation", {"1"}}, {"realization", {"1"}}, {"resolution", {"high"}}, {"expver", {"0001"}}, {"time", {"1200"}}, {"stream", {"clte"}}, {"type", {"fc"}}, {"levtype", {"pl"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"param", {"134", "137"}}}; expand(text, "retrieve", expected, {20000101}); } { const char* text = "retrieve,date=20120515,time=0000,dataset=climate-dt,activity=cmip6,experiment=hist,generation=1,model=" "icon,realization=1,georef=acbdef,resolution=high,class=d1,expver=0001,type=fc,stream=clte,levelist=1," "levtype=o3d,param=263500"; const char* text2 = "retrieve,date=20120515,time=0000,dataset=climate-dt,activity=cmip6,experiment=hist,generation=1,model=" "icon,realization=1,resolution=high,class=d1,expver=0001,type=fc,stream=clte,levelist=1," "levtype=o3d,param=263500"; ExpectedVals expected{{"class", {"d1"}}, {"dataset", {"climate-dt"}}, {"activity", {"cmip6"}}, {"experiment", {"hist"}}, {"model", {"icon"}}, {"generation", {"1"}}, {"realization", {"1"}}, {"resolution", {"high"}}, {"expver", {"0001"}}, {"time", {"0000"}}, {"stream", {"clte"}}, {"type", {"fc"}}, {"levtype", {"o3d"}}, {"levelist", {"1"}}, {"param", {"263500"}}}; expand(text, "retrieve", expected, {20120515}); expand(text2, "retrieve", expected, {20120515}); } } CASE("test_metkit_expand_ng") { { const char* text = "retrieve,class=ng,date=20000101,activity=CMIP6,experiment=hist,model=IFS-NEMO,generation=1,realization=1," "resolution=high,stream=clte,type=fc,levtype=pl,param=134/137"; ExpectedVals expected{{"class", {"ng"}}, {"levtype", {"pl"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"activity", {"cmip6"}}, {"experiment", {"hist"}}, {"model", {"ifs-nemo"}}, {"generation", {"1"}}, {"realization", {"1"}}, {"resolution", {"high"}}, {"expver", {"0001"}}, {"date", {"20000101"}}, {"time", {"1200"}}, {"stream", {"clte"}}, {"type", {"fc"}}, {"param", {"134", "137"}}}; expand(text, "retrieve", expected, {20000101}); } } CASE("test_metkit_expand_ai") { { const char* text = "retrieve,class=ai,date=20250208,time=1800,expver=9999,model=aifs-single,type=fc,levtype=sfc,param=169"; ExpectedVals expected{{"class", {"ai"}}, {"domain", {"g"}}, {"expver", {"9999"}}, {"levtype", {"sfc"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1800"}}, {"type", {"fc"}}, {"model", {"aifs-single"}}, {"param", {"169"}}}; expand(text, "retrieve", expected, {20250208}); } } CASE("test_metkit_expand_list") { { const char* text = "list,date=20250105,domain=g,levtype=pl,expver=" "0001,step=0,stream=oper,levelist=1000/850/700/500/400/" "300,time=1200,type=an,param=129"; ExpectedVals expected{{"class", {"od"}}, {"date", {"20250105"}}, {"domain", {"g"}}, {"levtype", {"pl"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"expver", {"0001"}}, {"time", {"1200"}}, {"stream", {"oper"}}, {"type", {"an"}}, {"param", {"129"}}}; expand(text, "list", expected, {20250105}); } { const char* text = "list,class=tr,date=20250105"; ExpectedVals expected{{"class", {"tr"}}, {"date", {"20250105"}}}; expand(text, "list", expected, {20250105}); } } CASE("test_metkit_expand_read") { { const char* text = "read,class=tr,date=20250105,domain=g,levtype=pl,expver=0001,step=0,stream=oper," "levelist=1000/850/700/500/400/300,time=1200,type=an,param=129"; ExpectedVals expected{{"class", {"tr"}}, {"date", {"20250105"}}, {"domain", {"g"}}, {"levtype", {"pl"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"expver", {"0001"}}, {"time", {"1200"}}, {"stream", {"oper"}}, {"type", {"an"}}, {"param", {"129"}}}; expand(text, "read", expected, {20250105}); } { const char* text = "read,date=20250105,param=129"; ExpectedVals expected{{"date", {"20250105"}}, {"param", {"129"}}}; expand(text, "read", expected, {20250105}); } } CASE("test_metkit_expand_clmn") { { const char* text = "retrieve,class=d1,expver=1,dataset=climate-dt,activity=story-nudging,experiment=Tplus2.0K,generation=1," "model=IFS-FESOM,realization=1,stream=clmn,year=2024,month=october,resolution=standard,type=fc,levtype=sfc," "param=144"; ExpectedVals expected{{"class", {"d1"}}, {"dataset", {"climate-dt"}}, {"activity", {"story-nudging"}}, {"experiment", {"tplus2.0k"}}, {"generation", {"1"}}, {"model", {"ifs-fesom"}}, {"realization", {"1"}}, {"expver", {"0001"}}, {"stream", {"clmn"}}, {"year", {"2024"}}, {"month", {"10"}}, {"resolution", {"standard"}}, {"type", {"fc"}}, {"levtype", {"sfc"}}, {"param", {"144"}}}; expand(text, "retrieve", expected, {}); } } CASE("test_metkit_expand_chem") { const char* text = "retrieve,chem=co/no2/no/so2,class=mc,date=2006-12-18,expver=9001,levtype=sfc,param=402000,step=12,stream=oper," "time=00:00:00,type=fc,target=\"output\""; const char* expected = "RETRIEVE,CLASS=MC,TYPE=FC,STREAM=OPER,EXPVER=9001,LEVTYPE=SFC,PARAM=402000,CHEM=2/17/27/233,DATE=20061218," "TIME=0000,STEP=12,DOMAIN=G,TARGET=output"; expand(text, expected); } CASE("test_metkit_expand_frequency") { const char* text = "retrieve,class=od,date=20250401,direction=31,domain=g,expver=0001,frequency=7,levtype=sfc,param=140217,step=0," "stream=wave,time=1200,type=an"; const char* expected = "RETRIEVE,CLASS=OD,TYPE=AN,STREAM=WAVE,EXPVER=0001,REPRES=SH,LEVTYPE=SFC,PARAM=140217,DATE=20250401,TIME=1200," "STEP=0,DOMAIN=G,FREQUENCY=7,DIRECTION=31"; expand(text, expected); } CASE("test_metkit_expand_obscutoff1") { const char* text = "retrieve,class=rd,date=20201204,expver=hk3a,obsgroup=22,reportype=21001,obstype=1,stream=lwda,obscutoff=2," "time=1200,type=mfb," "target=\"reference.vYyJf6.data\""; /// @todo DUPLICATES const char* expected = "RETRIEVE,CLASS=RD,TYPE=MFB,STREAM=LWDA,EXPVER=hk3a,REPRES=BU,OBSGROUP=AMSUA_AS,REPORTYPE=21001,obstype=1," "DATE=20201204,TIME=1200,DOMAIN=G,TARGET=reference.vYyJf6.data,DUPLICATES=KEEP"; expand(text, expected); } CASE("test_metkit_expand_obscutoff2") { const char* text = "retrieve,class=od,date=20201204,expver=1,obsgroup=22,reportype=21001,obstype=1,stream=xwda,obscutoff=2,time=" "1200,type=mfb," "target=\"reference.vYyJf6.data\""; /// @todo DUPLICATES const char* expected = "RETRIEVE,CLASS=OD,TYPE=MFB,STREAM=XWDA,obscutoff=0200,EXPVER=0001,REPRES=BU,OBSGROUP=AMSUA_AS,REPORTYPE=21001," "obstype=1," "DATE=20201204,TIME=1200,DOMAIN=G,TARGET=reference.vYyJf6.data,DUPLICATES=KEEP"; expand(text, expected); } CASE("test_metkit_expand_obscutoff3") { const char* text = "retrieve,class=od,date=20201204,expver=1,obsgroup=22,reportype=21001,obstype=1,stream=xwda,obscutoff=2h,time=" "1200,type=mfb," "target=\"reference.vYyJf6.data\""; /// @todo DUPLICATES const char* expected = "RETRIEVE,CLASS=OD,TYPE=MFB,STREAM=XWDA,obscutoff=0200,EXPVER=0001,REPRES=BU,OBSGROUP=AMSUA_AS,REPORTYPE=21001," "obstype=1," "DATE=20201204,TIME=1200,DOMAIN=G,TARGET=reference.vYyJf6.data,DUPLICATES=KEEP"; expand(text, expected); } CASE("test_metkit_expand_obscutoff4") { const char* text = "retrieve,class=od,date=20201204,expver=1,obsgroup=22,reportype=21001,obstype=1,stream=xwda,obscutoff=120m," "time=1200,type=mfb," "target=\"reference.vYyJf6.data\""; /// @todo DUPLICATES const char* expected = "RETRIEVE,CLASS=OD,TYPE=MFB,STREAM=XWDA,obscutoff=0200,EXPVER=0001,REPRES=BU,OBSGROUP=AMSUA_AS,REPORTYPE=21001," "obstype=1," "DATE=20201204,TIME=1200,DOMAIN=G,TARGET=reference.vYyJf6.data,DUPLICATES=KEEP"; expand(text, expected); } // issues from https://confluence.ecmwf.int/pages/viewpage.action?pageId=496866851 CASE("test_metkit_expand_MARSC-218") { // https://jira.ecmwf.int/browse/MARSC-218 const char* text = "retrieve,accuracy=10,class=ea,date=1969-03-28,expver=11,grid=0.25/" "0.25,levtype=sfc,packing=si,param=142.128/143.128/151.128/165.128/166.128,step=0/6/12/18/24/30/36/42/48/" "54/60/66/72/78/84/90/96/102/108/114/120/132/144/156/168/180/192/204/216/228/" "240,stream=oper,time=00:00:00,type=fc,target=\"reference.rFP7XB.data\""; const char* expected = "RETRIEVE,CLASS=EA,TYPE=FC,STREAM=OPER,EXPVER=0011,LEVTYPE=SFC,PARAM=142/143/151/165/" "166,DATE=19690328,TIME=0000,STEP=0/6/12/18/24/30/36/42/48/54/60/66/72/78/84/90/96/102/108/114/120/132/144/" "156/168/180/192/204/216/228/240,DOMAIN=G,TARGET=reference.rFP7XB.data,RESOL=AUTO,ACCURACY=10,GRID=.25/" ".25,PACKING=SIMPLE"; expand(text, expected); } CASE("test_metkit_expand_MARSC-221") { // https://jira.ecmwf.int/browse/MARSC-221 const char* text = "retrieve,accuracy=12,area=90.0/0.0/-90.0/359.5,date=20240102,domain=g,grid=0.5/" "0.5,leve=off,levtype=sfc,padding=0,param=134/137/165/166/167/168/" "235,stream=da,style=dissemination,time=00,type=an,target=\"reference.tzpUX7.data\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=AN,STREAM=OPER,EXPVER=0001,REPRES=GG,LEVTYPE=SFC,PARAM=134/137/165/166/167/168/" "235,DATE=20240102,TIME=0000,STEP=0,DOMAIN=G,TARGET=reference.tzpUX7.data,RESOL=AV,ACCURACY=12,STYLE=" "DISSEMINATION,AREA=90/0/-90/359.5,GRID=.5/.5,PADDING=0"; expand(text, expected); } CASE("test_metkit_expand_MARSC-212") { // https://jira.ecmwf.int/browse/MARSC-212 const char* text = "retrieve,accuracy=16,area=14.8/-19.6/-14.5/19.8,class=od,date=20230810,expver=1,grid=0.09/0.09,levelist=1/" "to/137,levtype=ml,number=-1,param=z,process=local,rotation=-78.8/-61.0,step=000,stream=scda,time=18," "type=an,target=\"reference.ect1qF.data\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=AN,STREAM=SCDA,EXPVER=0001,REPRES=SH,LEVTYPE=ML,LEVELIST=1/2/3/4/5/6/7/8/9/10/11/" "12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/" "47/48/49/50/51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/" "82/83/84/85/86/87/88/89/90/91/92/93/94/95/96/97/98/99/100/101/102/103/104/105/106/107/108/109/110/111/112/" "113/114/115/116/117/118/119/120/121/122/123/124/125/126/127/128/129/130/131/132/133/134/135/136/137," "PARAM=129,DATE=20230810,TIME=1800,STEP=0,DOMAIN=G,TARGET=reference.ect1qF.data,RESOL=AUTO,ACCURACY=16," "AREA=14.8/-19.6/-14.5/19.8,ROTATION=-78.8/-61,GRID=.09/.09,PROCESS=LOCAL"; expand(text, expected); } CASE("test_metkit_expand_MARSC-210") { // https://jira.ecmwf.int/browse/MARSC-210 const char* text = "retrieve,accuracy=24,area=90.0/-179.0/-90.0/180.0,class=od,dataset=none,date=20231231/to/20231231,expver=1," "grid=off,levelist=1,levtype=ml,number=off,padding=0,param=152.128,resol=255,step=00,stream=oper,time=00/12," "type=an,target=reference.data"; const char* expected = "RETRIEVE,DATASET=none,CLASS=OD,TYPE=AN,STREAM=OPER,EXPVER=0001,REPRES=SH,LEVTYPE=ML,LEVELIST=1,PARAM=152," "DATE=20231231,TIME=0000/1200,STEP=0,DOMAIN=G,TARGET=reference.data,RESOL=255,ACCURACY=24," "AREA=90/-179/-90/180,PADDING=0"; expand(text, expected); } CASE("test_metkit_expand_MARSC-220") { // https://jira.ecmwf.int/browse/MARSC-220 // the original request contains an_offet which is not supported, and has been replaced by anoffset const char* text = "retrieve,anoffset=9,class=rd,date=20210828,expver=i8k5,gaussian=regular,grid=4000,levtype=sfc,param=151." "128/165.128/166.128,step=15,stream=da,time=00,type=fc,target=\"reference.6Zr8N7.data\""; const char* expected = "RETRIEVE,CLASS=RD,TYPE=FC,STREAM=OPER,EXPVER=i8k5,REPRES=SH,LEVTYPE=SFC,PARAM=151/165/" "166,DATE=20210828,TIME=0000,STEP=15,DOMAIN=G,TARGET=reference.6Zr8N7.data,RESOL=AUTO,GRID=F4000"; expand(text, expected); } CASE("test_metkit_expand_MARSC-214") { // https://jira.ecmwf.int/browse/MARSC-214 const char* text = "retrieve,anoffset=90,class=ce,database=marser,date=20240102,domain=g,expver=0001,level=3,levtype=sol," "model=lisflood,origin=ecmf,param=260199,step=6/to/72/by/" "6,stream=efas,time=0000,type=sfo,expect=any,target=\"reference.43PsBL.data\""; const char* expected = "RETRIEVE,CLASS=CE,TYPE=SFO,STREAM=EFAS,EXPVER=0001,MODEL=lisflood,REPRES=SH,LEVTYPE=SOL,LEVELIST=3,PARAM=" "260199,DATE=20240102,TIME=0000,STEP=6/12/18/24/30/36/42/48/54/60/66/" "72,ANOFFSET=90,DOMAIN=G,ORIGIN=ECMF,EXPECT=ANY,TARGET=reference.43PsBL.data,DATABASE=marser"; expand(text, expected); } CASE("test_metkit_expand_MARSC-222") { // https://jira.ecmwf.int/browse/MARSC-222 const char* text = "retrieve,class=rd,date=20201204,expver=hk3a,obsgroup=22,reportype=21001,stream=lwda,time=1200,type=mfb," "target=\"reference.vYyJf6.data\""; /// @todo DUPLICATES const char* expected = "RETRIEVE,CLASS=RD,TYPE=MFB,STREAM=LWDA,EXPVER=hk3a,REPRES=BU,OBSGROUP=AMSUA_AS,REPORTYPE=21001,obstype=1," "DATE=20201204,TIME=1200,DOMAIN=G,TARGET=reference.vYyJf6.data,DUPLICATES=KEEP"; // expand(text, expected); } CASE("test_metkit_expand_MARSC-219") { // https://jira.ecmwf.int/browse/MARSC-219 const char* text = "retrieve,class=od,date=20231205,expver=0001,obstype=gpsro,stream=lwda,time=18,type=ai,target=\"reference." "E2RRc8.data\""; const char* expected = // "RETRIEVE,CLASS=OD,TYPE=AI,STREAM=LWDA,EXPVER=0001,REPRES=BU,OBSTYPE=250,DATE=20231205,TIME=1800,DOMAIN=G," // "TARGET=reference.E2RRc8.data,DUPLICATES=KEEP"; "RETRIEVE,CLASS=OD,TYPE=AI,STREAM=LWDA,EXPVER=0001,OBSTYPE=250,DATE=20231205,TIME=1800,DOMAIN=G," "TARGET=reference.E2RRc8.data,DUPLICATES=KEEP"; expand(text, expected); } CASE("test_metkit_expand_MARSC-213") { // https://jira.ecmwf.int/browse/MARSC-213 const char* text = "retrieve,class=od,date=20230821,expect=any,expver=0001,levtype=sfc,param=70.228/71.228/72.228/73.228/" "74.228,stream=da,time=12,type=gai,target=\"reference.1e7AY1.data\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=GAI,STREAM=OPER,EXPVER=0001,REPRES=GG,LEVTYPE=SFC,PARAM=228070/228071/228072/" "228073/228074,DATE=20230821,TIME=1200,STEP=0,DOMAIN=G,TARGET=reference.1e7AY1.data,EXPECT=ANY"; expand(text, expected); } CASE("test_metkit_expand_MARSC-211") { // https://jira.ecmwf.int/browse/MARSC-211 const char* text = "retrieve,class=ce,database=marser,date=20240102,domain=g,expver=0001,level=2,levtype=sol,model=lisflood," "origin=ecmf,param=260199,step=6/to/240/by/" "6,stream=efas,time=0000,type=fc,target=\"reference.JkqWoW.data\""; const char* expected = "RETRIEVE,CLASS=CE,TYPE=FC,STREAM=EFAS,EXPVER=0001,MODEL=lisflood,REPRES=SH,LEVTYPE=SOL,LEVELIST=2,PARAM=" "260199,DATE=20240102,TIME=0000,STEP=6/12/18/24/30/36/42/48/54/60/66/72/78/84/90/96/102/108/114/120/126/" "132/138/144/150/156/162/168/174/180/186/192/198/204/210/216/222/228/234/" "240,DOMAIN=G,ORIGIN=ECMF,TARGET=reference.JkqWoW.data"; expand(text, expected); } CASE("test_metkit_expand_MARSC-243") { // https://jira.ecmwf.int/browse/MARSC-243 const char* text = "retrieve,DOMAIN=G,LEVTYPE=PL,LEVELIST=200,DATE=20040114,TIME=0000,PARAM=155.128,CLASS=OD,TYPE=FCMEAN,STREAM=" "MOFM,EXPVER=0001,NUMBER=0,SYSTEM=2,METHOD=1,FCPERIOD=5-11,target=\"reference.GP8yZ1.data\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=FCMEAN,STREAM=MOFM,EXPVER=0001,REPRES=SH,LEVTYPE=PL,LEVELIST=200,PARAM=155,DATE=" "20040114,FCPERIOD=5-11,TIME=0000,NUMBER=0,DOMAIN=G,SYSTEM=2,METHOD=1,TARGET=reference.GP8yZ1.data"; expand(text, expected); } CASE("test_metkit_expand_MARSC-246") { // https://jira.ecmwf.int/browse/MARSC-246 const char* text = "retrieve,DOMAIN=G,LEVTYPE=DP,DATE=20120201,TIME=0000,STEP=288,PARAM=175.151,CLASS=OD,TYPE=OF,STREAM=OCEA," "EXPVER=0001,NUMBER=0,SYSTEM=3,METHOD=1,PRODUCT=TIMS,SECTION=Z,LEVELIST=0.000,LATITUDE=-9.967,RANGE=264,target=" "\"reference.t2APXu.data\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=OF,STREAM=OCEA,EXPVER=0001,REPRES=SH,LEVTYPE=DP,LEVELIST=0,PARAM=151175,DATE=20120201," "TIME=0000,RANGE=264,STEP=288,NUMBER=0,DOMAIN=G,SYSTEM=3,METHOD=1,PRODUCT=TIMS,SECTION=Z,LATITUDE=-9.967," "TARGET=reference.t2APXu.data"; expand(text, expected); } CASE("test_metkit_expand_MARSC-253") { // https://jira.ecmwf.int/browse/MARSC-253 const char* text = "retrieve,class=od,type=tf,stream=enfo,expver=0001,repres=bu,obstype=32,date=20060112,time=0000/" "1200,domain=g,duplicates=keep,target=\"data.reference\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=TF,STREAM=ENFO,EXPVER=0001,REPRES=BU,OBSTYPE=32,DATE=20060112,TIME=0000/" "1200,STEP=0,DOMAIN=G,TARGET=data.reference,DUPLICATES=KEEP"; expand(text, expected); } CASE("test_metkit_expand_MARSC-254") { // https://jira.ecmwf.int/browse/MARSC-254 const char* text = "retrieve,type=ob,date=20250414,time=00,range=1439,target=\"data.reference\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=OB,STREAM=OPER,EXPVER=0001,REPRES=BU,OBSTYPE=1,DATE=20250414,TIME=0000,RANGE=1439," "DOMAIN=G,TARGET=data.reference,DUPLICATES=KEEP"; expand(text, expected); } // https://jira.ecmwf.int/browse/MARSC-306 CASE("test_metkit_expand_MARSC-306") { const std::string text = "retrieve,CLASS=OD,TYPE=PF,STREAM=EF,EXPVER=0001,REPRES=SH,LEVTYPE=SFC,PARAM=167/165/166/164/" "228,DATE=20250523,TIME=0000,STEP=6/12,NUMBER=1/2,DOMAIN=G,RESOL=N128,AREA=70.5/-21.0/30.0/40.5,GRID=1.5/" "1.5,target=\"target.vareps.test\",RESOL=N128"; // N128 needs to be forwarded and is therefore not manipulated const std::string expected = "retrieve,class=od,type=pf,stream=enfo,levtype=sfc,date=20250523,time=0000,step=6/" "12,expver=0001,domain=g,number=1/2,param=167/165/166/164/228,target=target.vareps.test,grid=1.5/1.5,area=70.5/" "-21/30/40.5,repres=sh,resol=N128"; expand(text, expected); } // https://jira.ecmwf.int/browse/MARSC-433 CASE("test_metkit_expand_MARSC-433") { const std::string text = "retrieve,CLASS=EA,DATE=20061212,DOMAIN=G,EXPVER=0001,LEVELIST=1/2/" "3,LEVTYPE=ML,PARAM=152,REPRES=SH,RESOL=199,STREAM=DA,TIME=00/06/12/18,TYPE=AN"; const std::string expected = "retrieve,CLASS=EA,DATE=20061212,DOMAIN=G,EXPVER=0001,LEVELIST=1/2/" "3,LEVTYPE=ML,PARAM=152,REPRES=SH,truncation=199,STREAM=oper,TIME=0000/0600/1200/1800,TYPE=AN"; expand(text, expected); } CASE("test_metkit_expand_resol") { { const std::string text = "retrieve,class=ea,date=20061212,domain=g,expver=1,levelist=1/2/" "3,levtype=ml,param=152,repres=sh,resol=av,stream=da,time=00/06/12/18,type=an"; const std::string expected = "retrieve,class=ea,date=20061212,domain=g,expver=0001,levelist=1/2/" "3,levtype=ml,param=152,repres=sh,intgrid=source,truncation=none,stream=oper,time=0000/0600/1200/" "1800,type=an"; expand(text, expected); } { const std::string text = "retrieve,class=ea,date=20061213,domain=g,expver=1,levelist=1/2/" "3,levtype=ml,param=152,repres=sh,resol=N128,stream=da,time=00/06/12/18,type=an"; const std::string expected = "retrieve,class=ea,date=20061213,domain=g,expver=0001,levelist=1/2/" "3,levtype=ml,param=152,repres=sh,intgrid=N128,stream=oper,time=0000/0600/1200/1800,type=an"; expand(text, expected); } } CASE("test_metkit_expand_coeffindex") { { const char* text = "retrieve,class=rd,coeffindex=1/to/3,date=2026-01-01,expver=1,levelist=1,levtype=ml,param=130/131/132/138/" "139/152/155,step=0,number=1,stream=oper,time=09:00:00,type=me,target=output"; const char* expected = "retrieve,class=rd,coeffindex=1/2/3,date=20260101,expver=0001,levelist=1,levtype=ml,param=130/131/132/138/" "139/152/155,step=0,number=1,stream=oper,time=0900,type=me,target=output"; expand(text, expected); } { const char* text = "retrieve,class=od,coeffindex=1/to/3,date=2026-01-01,expver=1,levelist=1,levtype=ml,param=130/131/132/138/" "139/152/155,step=0,number=1,stream=oper,time=09:00:00,type=pf,target=output"; const char* expected = "retrieve,class=od,date=20260101,expver=0001,levelist=1,levtype=ml,param=130/131/132/138/139/152/" "155,step=0,number=1,stream=oper,time=0900,type=pf,target=output"; expand(text, expected); } } CASE("PGEN-566 class=ai") { const char* text = "retrieve,date=20260506,class=ai,stream=oper,expver=0001,domain=g,type=fc,levtype=sfc,param=sd/swh,time=0000/" "0600/1200/1800,step=0"; const char* expected = "retrieve,date=20260506,class=ai,type=fc,stream=oper,levtype=sfc,time=0000/0600/1200/" "1800,step=0,expver=0001,domain=g,param=228141/140229"; expand(text, expected); } CASE("PGEN-566 od") { const char* text = "retrieve,date=20260506,class=od,stream=oper,expver=0001,domain=g,type=fc,levtype=sfc,param=sd/swh,time=0000/" "0600/1200/1800,step=0"; const char* expected = "retrieve,date=20260506,class=od,type=fc,stream=oper,levtype=sfc,time=0000/0600/1200/" "1800,step=0,expver=0001,domain=g,param=141/3100"; expand(text, expected); } CASE("test_metkit_expand_A") { const char* text = "retrieve,accuracy=16,area=14.8/-19.6/-14.5/19.8,class=od,date=20230810,expver=1,grid=0.09/0.09,levelist=1/" "to/137,levtype=ml,number=-1,param=z,process=local,rotation=-78.8/" "-61.0,step=000,stream=scda,time=18,type=an,target=\"reference.ect1qF.data\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=AN,STREAM=SCDA,EXPVER=0001,REPRES=SH,LEVTYPE=ML,LEVELIST=1/2/3/4/5/6/7/8/9/10/11/" "12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/" "47/48/49/50/51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/" "82/83/84/85/86/87/88/89/90/91/92/93/94/95/96/97/98/99/100/101/102/103/104/105/106/107/108/109/110/111/112/" "113/114/115/116/117/118/119/120/121/122/123/124/125/126/127/128/129/130/131/132/133/134/135/136/" "137,PARAM=129,DATE=20230810,TIME=1800,STEP=0,DOMAIN=G,TARGET=reference.ect1qF.data,RESOL=AUTO,ACCURACY=16," "AREA=14.8/-19.6/-14.5/19.8,ROTATION=-78.8/-61,GRID=.09/.09,PROCESS=LOCAL"; expand(text, expected); } CASE("test_metkit_expand_B") { const char* text = "retrieve,accuracy=16,area=60.0/-60.0/-60.0/60.0,class=ea,date=20101029,expver=1,grid=0.30/0.30,levelist=1/" "to/137,levtype=ml,number=-1,param=q/t/u/v/lnsp/z,rotation=0.0/" "0.0,step=000,stream=oper,time=15:00:00,type=an,target=\"reference.1OEDK0.data\""; const char* expected = "RETRIEVE,CLASS=EA,TYPE=AN,STREAM=OPER,EXPVER=0001,LEVTYPE=ML,LEVELIST=1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/" "16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/47/48/49/50/" "51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/82/83/84/85/" "86/87/88/89/90/91/92/93/94/95/96/97/98/99/100/101/102/103/104/105/106/107/108/109/110/111/112/113/114/115/" "116/117/118/119/120/121/122/123/124/125/126/127/128/129/130/131/132/133/134/135/136/137,PARAM=133/130/131/" "132/152/129,TIME=1500,STEP=0,DOMAIN=G,TARGET=reference.1OEDK0.data,RESOL=AUTO,ACCURACY=16,AREA=60/-60/-60/" "60,ROTATION=0/0,GRID=.3/.3,DATE=20101029"; expand(text, expected); } CASE("test_metkit_expand_C") { const char* text = "retrieve,accuracy=12,area=90.0/0.0/-90.0/359.5,date=20240102,domain=g,grid=0.5/" "0.5,leve=off,levtype=sfc,padding=0,param=134/137/165/166/167/168/" "235,stream=da,style=dissemination,time=00,type=an,target=\"reference.tzpUX7.data\""; const char* expected = "RETRIEVE,CLASS=OD,TYPE=AN,STREAM=OPER,EXPVER=0001,REPRES=GG,LEVTYPE=SFC,PARAM=134/137/165/166/167/168/" "235,DATE=20240102,TIME=0000,STEP=0,DOMAIN=G,TARGET=reference.tzpUX7.data,RESOL=AV,ACCURACY=12,STYLE=" "DISSEMINATION,AREA=90/0/-90/359.5,GRID=.5/.5,PADDING=0"; expand(text, expected); } CASE("test_metkit_disseminate") { const char* text = "disseminate,option=normal,target=NTI:HT,date=20250506,time=0/12,stream=oper,levtype=sfc,type=an,param=lsm," "step=0,direction=off,frequency=off,area=54.5/1.5/51/8,grid=.1/.1,packing=simple\n" "disseminate,stream=wave,param=2dfd,direction=1/to/36,frequency=1/to/29,packing=off\n" "disseminate,type=fc,step=3/to/144/by/3\n" "disseminate,stream=oper,type=an,param=lsm,step=0,direction=off,frequency=off,area=64/-15/43/13," "grid=.25/.25,packing=simple\n" "disseminate,stream=wave,param=tmax/hmax/swh/mwd/pp1d/mwp/shww/mdww/mpww/shts/mdts/mpts/sh10\n" "disseminate,type=fc,step=3/to/72/by/3\n" "disseminate,step=78/to/240/by/6"; std::vector expected = { "disseminate,stream=oper,type=an,param=172,step=0,area=54.5/1.5/51/8,grid=.1/.1,packing=simple,class=od," "date=20250506,domain=g,expver=0001,levtype=sfc,option=normal,target=NTI:HT,time=0000/1200", "disseminate,stream=wave,param=140251,direction=1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/" "25/26/27/28/29/30/31/32/33/34/35/36,frequency=1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/" "25/26/27/28/29,area=54.5/1.5/51/8,class=od,date=20250506,domain=g,expver=0001,grid=.1/.1,levtype=sfc," "option=normal,step=0,target=NTI:HT,time=0000/1200,type=an", "disseminate,type=fc,step=3/6/9/12/15/18/21/24/27/30/33/36/39/42/45/48/51/54/57/60/63/66/69/72/75/78/81/84/87/" "90/93/96/99/102/105/108/111/114/117/120/123/126/129/132/135/138/141/144,area=54.5/1.5/51/" "8,class=od,date=20250506,direction=1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/" "29/30/31/32/33/34/35/36,domain=g,expver=0001,frequency=1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/" "22/23/24/25/26/27/28/29,grid=.1/.1,levtype=sfc,option=normal,param=140251,stream=wave,target=NTI:HT,time=0000/" "1200", "disseminate,stream=oper,type=an,param=172,step=0,area=64/-15/43/13,grid=.25/" ".25,packing=simple,class=od,date=20250506,domain=g,expver=0001,levtype=sfc,option=normal,target=NTI:HT,time=" "0000/1200", "disseminate,stream=wave,param=140217/140218/140229/140230/140231/140232/140234/140235/140236/140237/140238/" "140239/140120,area=64/-15/43/13,class=od,date=20250506,domain=g,expver=0001,grid=.25/" ".25,levtype=sfc,option=normal,packing=simple,step=0,target=NTI:HT,time=0000/1200,type=an", "disseminate,type=fc,step=3/6/9/12/15/18/21/24/27/30/33/36/39/42/45/48/51/54/57/60/63/66/69/72,area=64/-15/43/" "13,class=od,date=20250506,domain=g,expver=0001,grid=.25/" ".25,levtype=sfc,option=normal,packing=simple,param=140217/140218/140229/140230/140231/140232/140234/140235/" "140236/140237/140238/140239/140120,stream=wave,target=NTI:HT,time=0000/1200", "disseminate,step=78/84/90/96/102/108/114/120/126/132/138/144/150/156/162/168/174/180/186/192/198/204/210/216/" "222/228/234/240,area=64/-15/43/13,class=od,date=20250506,domain=g,expver=0001,grid=.25/" ".25,levtype=sfc,option=normal,packing=simple,param=140217/140218/140229/140230/140231/140232/140234/140235/" "140236/140237/140238/140239/140120,stream=wave,target=NTI:HT,time=0000/1200,type=fc"}; expand(text, expected, false); } CASE("test_metkit_disseminate") { const char* text = "disseminate,stream=waef,levtype=sfc,param=131074,class=od,type=ep,date=20250918,time=1200,step=12,expver=0001," "domain=g,area=90/-180/-90/179.6,grid=.4/.4,target=OPN:DT"; std::vector expected = {text}; expand(text, expected, false); } CASE("test_metkit_disseminate_params-static") { const char* text = "disseminate,target=CAM:TT,option=normal,expver=0001,class=mc,stream=oper,date=20250807,time=0000,type=an,step=" "0,levtype=sfc,param=asn/hcc/mcc/sp/t/tcc"; std::vector expected = { "disseminate,class=mc,type=an,stream=oper,levtype=sfc,time=0000,step=0,expver=0001,param=32/188/187/134/130/" "164,option=normal,target=CAM:TT,date=20250807,domain=g"}; expand(text, expected, false); } CASE("test_metkit_files") { eckit::LocalPathName testFolder{"expand"}; ASSERT(testFolder.exists()); eckit::StdDir d(testFolder); for (;;) { struct dirent* e = d.dirent(); if (e == nullptr) { break; } if (::strstr(e->d_name, ".req")) { try { // look for the corresponding .expected file std::string reqFileName{testFolder / e->d_name}; eckit::PathName expFileName{reqFileName.substr(0, reqFileName.find_last_of('.')) + ".expected"}; ASSERT(expFileName.exists()); std::ifstream reqFile(reqFileName); std::stringstream req; req << reqFile.rdbuf(); std::ifstream expFile(expFileName); std::stringstream exp; exp << expFile.rdbuf(); expand(req.str(), exp.str()); } catch (...) { std::cerr << "ERROR processing file: " << e->d_name << std::endl; throw; } } } } //----------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return run_tests(argc, argv); } metkit-1.19.2/tests/test_integer_range.cc0000664000175000017500000001224115232567717020625 0ustar alastairalastair/* * (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. */ /// @file test_integer_day.cc /// @author Metin Cakircali /// @date March 2025 #include #include #include "eckit/testing/Test.h" #include "eckit/value/Value.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/TypeInteger.h" namespace metkit::mars::test { using ::eckit::ValueList; using ::eckit::ValueMap; //---------------------------------------------------------------------------------------------------------------------- CASE("Test TypeInteger expansion range=[1,100]") { ValueMap settings; settings["range"] = ValueList{1, 100}; TypeInteger type("day", settings); Type& tday = type; // in range for (int i = 1; i < 101; ++i) { auto num = std::to_string(i); std::string value = num; EXPECT(tday.expand(value)); EXPECT_EQUAL(value, num); } // out of range { std::string value = "0"; EXPECT(!tday.expand(value)); } { std::string value = "101"; EXPECT(!tday.expand(value)); } } //---------------------------------------------------------------------------------------------------------------------- CASE("Test TypeInteger expansion range=[1,1]") { ValueMap settings; settings["range"] = ValueList{1, 1}; TypeInteger type("day", settings); Type& tday = type; { std::string value = "1"; EXPECT(tday.expand(value)); EXPECT_EQUAL("1", value); } { std::string value = "2"; EXPECT(!tday.expand(value)); } } //---------------------------------------------------------------------------------------------------------------------- CASE("Test TypeInteger day expansion range=[-1,1]") { ValueMap settings; settings["range"] = ValueList{-1, 1}; TypeInteger type("day", settings); Type& tday = type; { std::string value = "-2"; EXPECT(!tday.expand(value)); } { std::string value = "-1"; EXPECT(tday.expand(value)); EXPECT_EQUAL("-1", value); } { std::string value = "0"; EXPECT(tday.expand(value)); EXPECT_EQUAL("0", value); } { std::string value = "1"; EXPECT(tday.expand(value)); EXPECT_EQUAL("1", value); } { std::string value = "2"; EXPECT(!tday.expand(value)); } } //---------------------------------------------------------------------------------------------------------------------- CASE("Test disseminate day expansion default by/1") { std::vector expected; for (int i = 1; i < 32; ++i) { expected.push_back(std::to_string(i)); } const auto* text = R"(disseminate, class = od, expver = 1, levtype = sfc, time = 0, stream = eefo, type = fcmean, param = sd/mn2t6/mx2t6/mtsfr/tcc/stl1/msdr/tprate/msl/rsn/2d/2t/10u/10v, step = 0-168/168-336/336-504/504-672, use = monday, day = 1/to/31, number = 0/to/10, area = 90/-180/-90/179.5, grid = .5/.5, packing = simple )"; auto request = MarsRequest::parse(text); auto days = request.values("day"); EXPECT_EQUAL(days, expected); } CASE("Test disseminate day expansion") { const auto expected = std::vector{"1", "3", "5", "7", "9", "11", "13", "15", "17", "19", "21", "23", "25", "27", "29", "31"}; const auto* text = R"(disseminate, class = od, expver = 1, levtype = sfc, time = 0, stream = eefo, type = fcmean, param = sd/mn2t6/mx2t6/mtsfr/tcc/stl1/msdr/tprate/msl/rsn/2d/2t/10u/10v, step = 0-168/168-336/336-504/504-672, use = monday, day = 1/to/31/by/2, number = 0/to/10, area = 90/-180/-90/179.5, grid = .5/.5, packing = simple )"; auto request = MarsRequest::parse(text); auto days = request.values("day"); EXPECT_EQUAL(days, expected); } CASE("Test disseminate day expansion fails outside range") { const auto* text = R"(disseminate, class = od, expver = 1, levtype = sfc, time = 0, day = 1/to/48, packing = simple )"; EXPECT_THROWS_AS(MarsRequest::parse(text), eckit::UserError); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_mars_language_strict.cc0000664000175000017500000000142115232567717022207 0ustar alastairalastair#include #include "eckit/testing/Test.h" #include "metkit/mars/MarsLanguage.h" namespace metkit::mars::test { CASE("retrieve_best_match_param_not_matching") { const auto language = MarsLanguage("retrieve"); // Strict is defaulted to true and this is not matching EXPECT_THROWS(language.bestMatch("param", {"parameter"}, false, false, true, {})); }; CASE("retrieve_best_match_param_matching") { const auto language = MarsLanguage("retrieve"); // Strict is defaulted to true and this is not matching auto match = language.bestMatch("param", {"parameter", "param"}, false, false, true, {}); EXPECT(match == "param"); }; } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/regressions/0000775000175000017500000000000015232567717017011 5ustar alastairalastairmetkit-1.19.2/tests/regressions/METK-89/0000775000175000017500000000000015232567717020007 5ustar alastairalastairmetkit-1.19.2/tests/regressions/METK-89/METK-89.sh.in0000775000175000017500000000375115232567717021757 0ustar alastairalastair#!/usr/bin/env bash set -eux parse="$" srcdir=@CMAKE_CURRENT_SOURCE_DIR@ bindir=@CMAKE_CURRENT_BINARY_DIR@ wdir=$bindir/METK-89 export ECCODES_DEFINITION_PATH="@ECCODES_DEFINITION_PATH@" ### cleanup and prepare test cat > req < req < req < req.porcelain < req.json < list < content #cmp list content metkit-1.19.2/tests/regressions/METK-89/CMakeLists.txt0000664000175000017500000000027215232567717022550 0ustar alastairalastair if ( HAVE_GRIB AND HAVE_BUILD_TOOLS ) ecbuild_configure_file( METK-89.sh.in METK-89.sh @ONLY ) ecbuild_add_test( TYPE SCRIPT COMMAND METK-89.sh ) endif() metkit-1.19.2/tests/regressions/METK-103/0000775000175000017500000000000015232567717020052 5ustar alastairalastairmetkit-1.19.2/tests/regressions/METK-103/ok0000664000175000017500000107734015232567717020422 0ustar alastairalastairBUFRb 4~hd!^ jL!bFV%R .dEF F (!I.@@ @@ @` @`!Aa@!@A@a@@@@D!Aa!a!Aa!Aa!AaĂBb"Bb"Bb@B@b@@@@AA"ABAbAAAABCbCCCCDD"DBDbDDb€"Bb"Bb‚"Bbƒ"Bbb"Bb"Bb‚¢"BbÂâ"BbĂģc#Cc#Cc#Cc#Cc@c@@@@AA#ACAcAAAABB#BCBcBBBBCC#CCCcCCCCDD#DCDcDDDÀcÀ#CcÁ#CcÂ#CcÃ#Ccc#Cc#Ccƒ£#CcÃã#Ccăģd$Dd$Dd$Dd$Dd@d@@@@AA$ADAdAAAABB$BDBdBBBBCC$CDCdCCCCDD$DDDdDDDDdĀ$Ddā$DdĂ$Ddă$DdĄDd$Dd$Dd„¤$DdÄä$DdĄĤEe%Ee%Ee%Ee%Ee%@e@@@@AA%AEAeAAAABB%BEBeBBBBCC%CECeCCCCDD%DEDeDDDDEE%EEeEeUŀ%EeŁ%Eeł%EeŃ%Eeń%EUe%Ee%Ee…¥%EeÅå%Eeąĥ%Eeuu&Ff&Ff&Ff&Ff&Ff%%@@AA&AFAfAAAABB&BFBfBBBBCC&CFCfCCCCDD&DFDfDDDDEE&EFEfEEe&FfƁ&FfƂ&Ffƃ&FfƄ&Ffƥ&Ff&Ff†¦&FfÆæ&FfĆĦ&FfņŦ'Gg'Gg'Gg'Gg'Gg%W%gA'AGAgAAAABB'BGBgBBBBCC'CGCgCCCCDD'DGDgDDDDDDEEE'E7EGEWEgEwEEEEEEFFF'dddddddeee'e7eGeWegeweeeeeeefff'f7fGGgǁ'Ggǂ'Ggǃ'GWgwDŽׄ'7GWgwDžׅ'7GW'7GWgwǤפ'7GWgwǥץ'7GWgg'Gg‡§'GgÇç'7GWgwćėħķ'7GWgwŇŗŧŷ'7GWgw'7GWgw'7GWgw'7GWgwh(Hh(Hh(8HXhx(8HXhx(8HXhx#######$$$($8$H$X$h$x$$$$$$$$%%%(%8%H%X%h%x%%%%%%%%&&&(&8&H&X&h&x&&&&AAAABB(BHBhBBBBCC(CHChCxCCCCCCCCDDD(D8DHDXDhDxDDDDDDDDEEE(E8EHEXEhExEEEEEEEEFFF(F8FHFXFhFxFFFFFchcxccccccccddd(d8dHdXdhdxddddddddeee(e8eHeXehexeeeeeeeefff(f8fHfXfhfxffffff؁ȁ(HhȂ(HXhxȃ؃(8HXhxȄ؄(8HXhxȅ؅(8HXhxȆ؆8HXhxȣأ(8HXhxȤؤ(8HXhxȥإ(8HXhxȦئ(Hhˆ¨(8HXhxÈØèø(8HXhxĈĘĨĸ(8HXhxňŘŨŸ(8HXhxƈƘƨƸ((8HXhx(8HXhx(8HXhx(8HXhx(9 )Ii )9IYiy )9IYiy )9IYiy )9"""""""# ##)#9#I#Y#i#y########$ $$)$9$I$Y$i$y$$$$$$$$% %%)%9%I%Y%i%y%%%AAB B)BIBiByBBBBBBBBC CC)C9CICYCiCyCCCCCCCCD DD)D9DIDYDiDyDDDDDDDDE EE)E9EIEYEibibybbbbbbbbc cc)c9cIcYcicyccccccccd dd)d9dIdYdidydddddddde ee)e9Ɂ )Iiyɂق )9IYiyɃك )9IYiyɄل iyɢ٢ )9IYiyɣ٣ )9IYiyɤ٤)IYiy‰™©¹ )9IYiyÉÙéù )9IYiyĉęĩĹYiy )9IYiy )9IYiyz *:JZjz *:JZjz"""""""# ##*#:#J#Z#j#z########$ $$*$:$J$Z$j$z$$$$$BBBBBC CC*C:CJCZCjCzCCCCCCCCD DD*D:DJDZDjDzDDDDbbbc cc*c:cJcZcjczccccccccd dd*d:dJdZdjdzddd *:JZjzʃڃ *:JZjz *:JZjzʣڣ *:JZjz*:JZjzÊÚêú *:JZjzĊĚĪ:JZjz *:JZjzK[k{ +;K[k{#[#k#{########$ $$+$;$K$[$k${$$CkC{CCCCCCCCD DD+D;DKD[DkD{DDc{ccccccccd dd+d;dKd[dkd{dd˃ۃ +;K[k{ˣۣ +;K[k{û +;K[k{ċě +;K[k{ ,<L\l|##$ $$,$<$L$\$l$|$$$CD DD,DmB* XhX "n`7J,8](AD+h ¡>A(XH0@z"8&gAiT u$1< \|_h\M70)Be: fm"zS Qq U pZ/>RA O &dž8~Y~5Auqƚ &/PC(2&Ԃ1ܴ`DB\~PE(3^{D")Q݉c N}QбQ/7 i#gkq=;^'?P,P'tV `cDP>4P5DB/WP}:)1[ I]ϰƳ7M莫xx)ĈdpRD]Kpl=i6[\Ch#͡, 4Qɂ@#021[f /l f>r3AH`*q= aiÅ,ؠXOXE߂J (6+fhʋ`";*8!T| CV b@ݮRqK8B2п)e6ϬVqB #+Qbx1LQ+l+ׇ>ׂB  |`,Tp| 'E!U6 bMPap L_ >e4͍lm#v;pK=:zkABLа7OR"HH^I Q*""o~r,m/d^C8vV3q= Oll-n}G/:QWycNExAdMH1"!GbFg sv*A }pWT&!K 5`u9{YAb#jD'IA >SbjŦJ.aw La$!e~N4 nC8rV;Q4O|./BY\#M I("P IK"m|װ+*6AX{YzBe60p fE#F@7ĊZ8t#80G>~q 43!^1bȎ/ Vdg#00Ft6kA#hqSNJ=0=8t$ 4 #dpC&kčvI$$b >6J]9mE 6ؔc&!< L5&"aÉ hJb^\@@ *)@P WP:/Ōle5A26rC}8N=qʹz 1 nHnDM)R@d{v$HI%HK _5 'd20&j u{$; 0\n\z K?;A~&"&I("V#8xD(M'$VB½rֈ/q Nd(.s6L)4qCh=A72' H_CR%ct $y#9J\3Hq8 nHnze؇?Ԣ 8ȉi$"'ىtPMQx,ؤE@*]$XZX"1 hO#P F8`Îhu҃'MGɎ?%58\$(t!E6h錐ĕ%9,>}LÒlD p^) @{Jp%Hk _2jp9ߡܐO} !'숵GYH`'E dTI Pn.{ bW 2n(Ƒ5c1֍ocfi<&qu}` 0BtB0(ed# "IT܄Ғ');tI,갮|. gYTb(lYJhA>`(*abL ~_P@̠8az= >{|EB9;01Ĵ&A>*SBx,T-t``C,t&4za n}Z-L\;<;8{#q@H%(AP@_$a2 SrV2"at _pn`CD0?B +Ged$1& lM"upB<*1\ Z"zqȽ|4 o;Qx)[8>̶';@ ;$Sb,E-v!PbP2eyL!ց M@M@`/G! hh`B qa ! IFC}D'C% OtT*⮸7`3O $(A. Re­H41A =mMC,pH? Ȉ.\6#$ZLbqE3*vPr)h xIi$@҅t-S!w ,hdC@ܐݮ": !qdlH7OO倚H(PQI?h a xKdu@|.~! ]fECL@;,;,$Z!wHbUvހc $3 S¸лg28A# en"O )? ؉gf^$ &nMv_GXPR,CAo b/TU9D}}!1 G$Fj{,'MM ZBD3PKw5k1tN %Bۢ$p%a3܄'!E hX;ܞĕZ4 sósPdABS(%.)!Y 8]e6 @wڸ(VDbB00nL-azk ?eWE0,T; P2!ahHa=b WVn1U803m pCx|@BS yP7y-xy? 5eCC(P+&:A`!*dY `_C\ĆӨ8Iazf YA zXE!] T]C *0\7_Y`xCĈcDϡb _{08΀zPF Caˈ{-$Av  dQC=LÐ:y2V!x1;!: &hl(>`B(`u 5U!#, tCݐ"lBz CewFz .H;AmpK  8oD5&AtaýĐ2 y8o"tn?A =m@Ä?b ln[T?Zhs^E:!x(s)C vpd=@*b/ xR(<8)q D`W(@@@8| k!FH@9D`  pti '/A5. K O3`\i@'gРx,\ TЂ@`!7 K gh3 cs`\x/Du [)P=E$րEo!P@Y˒:0p׃~$=H5 +h?9@=1WAn _7X:,_ ؐUà '8 KHk8 =GTw{U`l209A @nʃl"84 ew!Ġ /x [YఐD!Іl*!%(@0~d Ь^8*aS XS`(AC O{ݴ&!3: KY^$va!tG=5<O"4Ar [xH,c Wj_*S S0 (B OxlτH&gA1 wpK%U!0 P`=.u p\hBߌېⅦ,e PXZ¾Ӏd*T T*`(aC> hkC?rih3 Hd"&Ʈ-11 -`|$/4aw H\,-f "Xᐭg4*ậ mCj; 5 Tj3MQў$3ᝓ fq/n]Gl1 ]biW@/A| x^*BjG`Ap:@[ts5Ö*<8Koyܛl6᳥ k[P4! g;Ն]2R `c$ĈtqsÇp7a0 mh!P5w =iqGB`w5Ba8~0Eӄ>g]|{|ؒVL@h;ܩըv#íhJprC`9ߡ"a@v#î"QLDb#=!a fCB`"f /A?"3 DL k?a}Dp =i[<yoǔ0 GB>jЎ҄sB#'Fb1m}XX"wDb#0D[PJSĜ$%h$hO9Bx:P@D'8(MkVMhD̶&I1kK]İ%h*cKLJ""OiHӄ${"HMb@+)KWDR"У(E &pPbc&4'?|O^yMx@'$Q8AhMk$K&ja2ɉLz"b?%/,uKZDj%mA+LN"v_'DF'-8ÜNo pRؠ&5G Mh3dd&@11 hL/"`uPEW*1Tn T,O?\)N:kS*#%)G 7QŠ}F$(aBPn`4'? pO{A_<'x;U ׈N"t9D'q8 \Mmh 4)1L! ]R&\)$HB,QBo@;(QE%%DQ"4sE (=A| \P#nh'Q>60O[yh!DH,Ia X t+q[ V"\*Uh T/pIH*7QN TbPE:)MkSC#ޅ-)XaJ_ OR\b@ (F 2QxBQZv(}C(Pbg+-!XĤV uuZ*QUU1b?ةELr*UR/ XTSE>0)NqpSr//)rK3V|R#)H>DQFF-dQjg MZ?"ЂxXe,Adr XfQ+A_jWds+\% ݤV"f:+&XƊLUboJX\*UB,UB6x{J*A1Q pT7b% E=h)QNqSxšx[,qf4 .Y[Ixŏ ,mqbTXlw,1_ (W"tJ+\1VBf:+%QXU"oPEX*UtLU.B@M*[RǠ^Bm.tP\@4E-ܡnm[R"жů-l!k TdZBӉд0-g ;Ybxۅ,Qdr pXbZ,&n,.`PX RPjEy^+q]vW.¸Džm+ZqZ$VmPVa&.s P\!EX.p [M -mahP[#BkPE-Pj(MZNhH,f2hYtO?Ej,sc XB4E,a`? W?H2xd0 p`IZ/~_ _=Bs[/?Qy\^6^.v \]uPM.|so\\E,.p6~0[bE-l cȹ.[1rT\wfE.oҋ{t[`]9\1 j c>C*.1c SbNu"1 ;a Yd40 "`C3Ph P0? `1/~F_YbؾE$/kzۋӼ^~_z/w ]i״.!u> ]#6Fe3! e#,[hʩFS2L @dC&r.>H29ёw d2 F=1ݡ qcpCXW1(1|XbF$1Qb @Tam0(01) &aC8 0H `@=o3p; ָf4P̼d3A e.qHXFYd2e;(C0FMz2bq dy# jFA2yc#51| a$b=(^4ˡ )$icG2@fF4Cҍ thHAHF31g<ۈΪFt&3!N Pf6vp?hn3:` Ȩf)#0~\21r em*N?P2xQR d$e Ԁ5 !=tiMkH(FH4c 'i-H@PІ&4\hzCf4Ō gƒ=Lv3q g7px~Fj03EЌfB#d*nF5at$kc[ب֦59 _jVxNF5=Ij6QF4䡦 3iKV04q@hCE(4*4h ?LH6Ј6yQlCd".6$܍l c_דp5- nHk_Z4(/FN5udWjCTƤ5 @iNoTd7ha ncs0ܧB7amnqbل6QmCiF 6na ldHج6QFk^g<5qhp1P<7o{xި7!bnvV7? xnAqYޘ6A mkYښv61.lÌ\(ⵇ8Ċ!0p3v X8Ia pQ# X+7D oC|(7Lncwo7FZrb`G'9-!FLrN}G8ڱƗ1qz!TXpZ8qp#)`80a8p"CJLA9ϟ{sC}69"fTsP弇,9ZʋQ1={޹cG=āl {P*ǯ=va[XzChמ>Q}.@>g|>Y~ qG?Iʹ~^(XEG>}_ 9@"אcG$?Qst#$pw@2 6D m ]H$@"܁D 8q @Q d% AĈ,A^b R肁d H#AB~#y!QHCIK44$' TJZ;)@p;  @@d5 4lwf5  p^_2 ہQ@( Ԁp 9Q@) h p;`d΀`x< `X]1pH H|PaP28 Eks:fLT,R +L @Rp:`>NF4T`+   h5@X5@}D"h jd2 HA`pG% (Fj_0p| ~eEAs&@Q@*  Ԁn:@h~X8S+ i~ `e84F&q`> 0 `^@]1P HG%L 9^Z. x]A@ @ԀH%P |x|j@8@4J=+0P)0 xԁypi(6ZK_28&+Q@+ @R{Ax"` ]ӂuET`,$ z`  xʀp=`0 0^ ^2LւH% ,y`ʀj7}K'  @ }@D%\ (xwj70&M(f ?Ls;t,W+ Op( p p;`@L]RK:`0[H0 HA΀ @,W, (Q@(  Ԁp:4KylP^1x ~π @@,W2J&#Q@)8  Ԁp9Hтb`0 |A  hX.Y@2H&#@Q@) ' Ԁp:@ 'i ]/ 0`൐ P`+ ݀E%P (xg`5  FAQZ- 19 `NX}K& F.}A0"h t@d5  vpE@W-P T p[K2`g@d@<L@,W.l `Nh( qd(4,P Ut@ @M@+ I%P   }D8$0$ ^Z/ Z hA `L܀}B% @}D%hP Jx@}A" QZ@. |`P& (ZЀd>`@ , |A`$ P DxyA"` WrAEW.` E<s ,d2  `pB%L `tp=H H~X8Wh-P @8p8@ d:% `@$W2H`d5l K2< up=+لÀh`5B }pH85"ÁAHJ$ClWs`4\v-0K2  `x&ˀ@K( 8 p;0lR+ ǪcA\]3 BX`](\pQ#d~E D$aAP`_NA8jew)(d70VH뀺XH>~#]M}8f Zzb<&NG2 `@[q>"`D `^Pb0D^)sCp)!{9PYsx-R<nxQZ|iKdbe<C 0y հڰN/0CY 0F${>!Uk|)CM&QWz_0Y!LM(ROf:\l&R FhTq(ɀ71 L !.(J1̄JsbHy"yK1S1{GƣY2>iEDVA7՗D+*fHR}JRܒumF̔"oCVM=(Krd#۵4d;Q-<|Ld=TI%;u,M-!IPq6)@g؈ r_pC^/D`=$&fݡrL=6} ŎiόE ռ3"0:9/A? Wo#)w2-TMcAhT?8OsRK0a2% `^@W+``@l2/xqq7A5rxV *Lw9X/{)KǠnOH]k5ZzJ֥ r?k&. Lo\m$DZ|أw f\_.,ǟsyh I$'* - 2H֡p 𖛴0)N&P[)ovPβ)nF˹[15a2.sKn(A0,h<+iP0--b4rrnN(e(4P@+"x#x T@rp[*e6tZ vA2޹~|C_4s5mܔר!H2ˬ-oݨ2I}!gP׺97i=/b8b aDt`Z 9]~;]Cn?7Ѱ>eiA(-5'< 1 V r+Jk&Bᨬzp¯,ܸ"dK2@ ` &!sZC1m}Pq˞x:Z)&C*7RGJǕ@Arhq8+h75ںT Z xGva;(8r)AKw۽ATe^;3 ۹ .~~WZ;'[@V6u*11%1Gz$!1Ca1l" Q4"lj 3DgTtPf.KS<b э9F@m^{ ^5Φ]Y0Sw8I 8ސRd(/@mgA'gTR<ēW2dH@k3\".W cI}BNVѠG\t8]n ȁS;W w|wFuDo;oG=cQq&Xj@"Q̂gstu*  x6[j DX[v!`=j$-Uk<^>Hǽ ncP4 Z@&դki0<[7tV oRHڠS-V&i(Q:hqB %ђY8]PɐM֦0jb1*εf̸O>'he~Tc)=jZpZߞo##A]2*%~y!AL#sK̡#^I4@r-H)`@Od%1םFj*2FK*;+xM&dCĪՇ$X䊙ٵƌacA<]Ums4MHa?䙡V DbթHk-{ IjbyPdV "爅ؘB/$ 8 <(CO`zC,f4ɝ e+%-pVbe()VwObD%1/QLfV!*A8D'0, v exéP<1h'\"a$RcF2ѓ hCcpH EREi3I"otUF".qe W(F~7<)h:j NZP L{$Rs'1J _C;(fIbྦ|8qکiL%hk^r4`L A"&x~0Ql&U fyAM:ֆ&8 0ܚR% 8! jCQ 0Q8D_෦J7PXЩhl%h3J&y4pm`H,a px@&AXl#!)HZT('Fhⴶ4ď3l]e^!4+1B 7 4|- ͠wt5j~Zx'A4 \nr0KOH$H҈tƆ; I-͌M\s+!AA+`1JH Pb!7 $e i" ZpHBH2uZFC6/CF0h@ f'!y,!\Ď(j̎nΡe$ VdWc"ƒ`jh``@ 0_%p ȂD,!pĘ))n̶pC!(JWYׁT# V L B\%!k D,qd)tLr#u-EYc@AHA XF 60.`Z%m Ѓ-!܆*{,tt%3|[;wC" PaT P @1@T/ `]!= jc"7Ja3_hNRf;!Їj0W /@0`|MB4_#mQ YiI҅Uy80ML "<`D 6L@#<$ G-0 pUN !2 Q8D'JJYpơ7-~T)餚,܇A<PʌěBWF}6n~!!Ks4L 8=^& PH2L! h%d v=&aE[St֧;qPbt; JUdYMvܠ XGQ=m(F<5dd`%A<|X&9O$d3TV7ůNts(yFNG}itM@(@ `}x:^舸Rφ;(Yn`/n>j Q,'aI Y\6.{l!?J*fT+!u kHJ{ E1MtР Ih"E}-~ pg^\' Pe8Sֵ 9R) MH"a[ hl&N HacFDrFX%n-Lk7DAR'+ g a-p%u O(ϖ5PuÐQ PQˆ:( SK8^ \K=RލdCjX dž}6NyC H\3=af뙆&x @@@B"|1(Ć'QT|cRx EBM|.aLl(BE:D-9ha/ܧ*LQ)OW6`5 )af gztGcD/ Np(~G"SrMEqLd#AgR @Y  Nº҇0> hX-,pL@e1ssȟ HL:{`80x _0HB9*qm 4hc{^GY. lc &ap@pl;0$, Xt!% THφ:ħ'YL\C ֧ ^#A(?~"a% UBX?#hL-Q n#Ip0! 5a X¤ $aGBF<, oC 1 HOt6xDW$6 :V2՗DbqCX0,!T XQ,+g d\8FBZ0 t $M"hBxC φ80!v pZB0 PpL 4{"#ɜQh@pb@͆P2 fCH h>bp)`cGA)< *`\9A nvP@yB*)]a2z?pj"dBЈDX#%OE1sC IkL"Vh$&TL"pb,u4f#W@ '= O`EP+i a#(P,<h%;+qY Uб.1| 2 gN݇:8##IvNxkC[ن7ǎw#bEr?09ώvCA\D&I= D=l|#0B"&); *T l%!HF;@'qDtVB0m#!,JapE)XJrT&9 Q$e-EڣU`bR_L;7bDZQFIY/t䟔Zջo/rAHM"TZQGJ)y6' AfcjMU=CrCȝ#SToB[Sw/È$UNۃlpY<c"Fi.'U VjbLݮW\`,2:_NAT)2כrΑ< d0*`Oq^ڛ|tIع#%La̩5&ٛs@]أz_L7WB_*R.^NEUID)tc3f؜zGu@*EXDnu忨Ud4JL!V,2_LA5&$ҊuSk uVۜP.= TʩS*e`e YLA]v*z"I6EPK L, ctB2)ec{=LE_.4[ӫ{F"Ǚ^# }!=%(cea=i&(%|#{P>M*UaB\}r(HYvHb x0p0Y :'el#|CMqe/Tk~/I9-Z !(#p¸a aA@,@diNaC);eYgsO:Cx( d AB aX1%H茓a Y?*%bXCnΥ(ЗdP!`C(x˜` GJe1h^ gt$Z"q) H p: A> ;N1$(szCN*h0fٜ̀P7JY9$,81&+`!īLs'J Y.6(ћ˭|P^&Iw<@0  )'EDpsm GIKuZxFY74Xx%$E`D,BhaDIA?*պ ttrx-ge$} `@ÈoBQ Q7'#Υ2a졠];{oBdϝtF֚`lq\ӴyϦs#|3v|ZcSkP$"86 p9B/"䮘#Rt'GQ5%2B_ E46\{xOqY}2hNH\&ۜ}JF-0]vڜvAX?paEOv 0"!' #(Љ"^ Y6'jCK)Cj"b5c{ #JvКKPzÙXoΙ?E#ĤUm&UmлD#b9U1 4֨i( |nndWQ=0*3,j aQrPKǩTzJ(ʖmL` e&rWvp_o^s}$F)#v#4_E6pQ)rU5*ekpq@s1:k.{)Bdq刑dZ8M"[K&S*8e=9_7\1/$lMFF3Alԇl"/3#y$u:`Cd:buR*0";d0 EV;ous=:vOkxX<Ia "IZ [HL<@s0頖Cl25cMںyXN0'%{a?i (}Q2^QmՐU~8Òc EpYnsz]oW٫Zk"6*Ng:#APDT#bR)z傇MX[dWIk6nojB6CaՎ x*}?C&1rk7Ng PK&& bZj0_x0LFSepEN7uzx\.838qdt<Ph~Ua5ȠfR*UҊdff4GZmF+EɊ^u;m)Vܭ(Xt-Xvd ǀ}?A?کJzmKTX0-dWR!#iѫj6|ۼN+;S(oI€JZ /v 9&e 9G'%I.RYaz]0ؙvoIQڡoM8\NiNZf m^0L¦TQbpHGY9 _"u"@#C&Pf{AEfJQ⮵Z .hWڬ=E5Y2m6\M.cWNh7lvDa7;oWyt=QIxHHX-h_&)s'Jk69G:ëنvݢ@0?("?m*6x^"34&/U%nVO)}}kEz-`Ơe-V{ɠv;{.u@>x "CBA`zfE'L }Fߥ&g> fθ_YϖcWѴjOq$9:֗pp<b4Bi᪑ذj># (cQ ȸDri-_dX]eXuL۷o8wu|A@(H\Z7 i$p|BO—X [/2>o6]Ai"nSnds0JYxDO1' k26x?ݡQS1PdZgW܌?G[6h֛mj7\kSc۴n`B #!9BGH c]5rh1SE0pRVkhEkRȒe{S6Y5ڋnK)$[UwL\Ku ABdճ CTq~`m7`[wӕBv^;7 B2 (BH!Xe<"J=H`^C]vRɵu*[Y-#+Ɇd3,cO* )RUf l?]X!,90!͕)gkZzm`i_p+\b?ӧ52]w6Q/CGݵAU!9Ht2O.N(6}p%O_Z.WS Wd3̍ Sٮ:Im7ZkuZ;]q|r_ka[Pt*1 "A!?p_3C E#RXar?XR*/u*нm⹒^)<2"mMg̴wmbƒipp6b@ aBbw6QCGݵ~^%Gd}G$gɂyC&SȪKpڻ^0VlR<{'ŘΔhg4u˛np8ܩrGLxmlPSY~rhFXe6}by˴mC8w&Ogs2 y F$ zqT*ui@ڣ{S<*X ՏJҙn^Mʘ4Bv4#~͓g4-fjI2ޓpF8\dA;׀{w9R/DwK~jeAwo=AW$F0 K&MS)zP:W ,8skdq|Na1c,d;m0Kj5[4F~Dr$.7e2yJOwv]!Xw?"GJEs"єPl?7ǤB PE#= QTm%L{*&RTAW, ")~b,!l?E+k";CD"=!8$K\&y.b)_,]90 ¦baBu< )d.b#.2I %US t*HTث ezܙsb;_Z0Jq^A*)M!' %.BI\[/AK~mcN1ߘr:B2BGerқ |RvA0tS*ByJ%Z9lʸ]|X ,,)IWP' yN̪~W,|ƋC!$jj6NgcXB"Qo甋zQ.LN([ZC[Y-l u:TM*Rt@aS1qyv<!&+*!Y@HRɞtZ~aD¥"SUJyoF+ oҼ1aZ{-X ѕJj6FɎ9H3XAQ` (qzD='LЧj&T:W,Vp)iе[`.R e Y$g4ͻgӖALz> _h\Dr-tI%. †HTܫUkixܽF_B0KN=+aTj6ӎ1>|~@[ èt\9m">KKV }M|]UV"'S.fN3PM^v (u5;wόG  @EE(@:eM8P(j]jazf3: Ch$rz:=G31-xCrQDP-fN(J<3Vޭ5Ǜ*M&v< *E7=\I32)O\(QTydF:4ގpb8Ԝgch}?ꠀPhF,,I:%;t{BS(2Ufg@dy=W(tB"F #0_I4ObԀ*fEFfx=(Ϭ4 LD\O,iM]Dj!šMQܫI>?vOz HD#IDRI-M'f*"#ڛ9RB@ Њ`=%BG$OkIVg 72P1( rJ 3C"VQ`ttA$̔dK2&RIrF8VSlHL,iTPHstXe6{O(iwJ]@dFuQ dS-M<*MUO#iD*S,L&ӮU J^&T*@5zY/{M6jڐ}LT)g"e}5|O '04e(QVC&3 $2w>QVTj}%M2ZVNM' ҏK)TpyY?6Q)(TkBW4HW|,0(j(M4T*ՕRqHLiSY,ꬥbZE^YD* /*a-]sX,` Ål:Y``Y-$V*Ձbe7ZѭÖ1K,թmz#Z˔Etֻ]/82RB \y(E;}.BRʩ_-6%Ĩ>\; 1@)e_"*`Pi;&(ENt`-WɔCR(sfVʋ;s"/&%\יM!*Ch"[ߡt2Nsd3qPCvy5b2iP̊^-d­S8iM8J) ´vвcCh{"@LҊ)IKD+<Šغ.sf 2!7MGP?xF>4a 6jP|R/B(g-f!$D؉A;)E$TFCb\ɍ3D|su<{,6 :AL e!:̈j&E"WO>Jna^+-ŹJlPMS9ga8~#݌}J@*u!1D-!3De"Ĥ]n@љA!&h"b7̻63xXy*?53℻ CH "$VmQ2FhRT$OfJcr.<ܚFcoMa0; 9^PDZ BV(\ !EȉK,qE}(j$qQ1 #?n`LY6GDm3z}ϒ@$.P#D(=$g,IJ:G z!IT-Jtȩ,e@p=oپ<JD Ў3CHo"XR5Gh#ԂАՒ)F+D֓(b\XKax44F$TPzH@j*i!q9*]0 *ETztCQRAH~#$5pt$H!7hM1ċ:t'qtJ;RC"_ᑷ:G!$ITt3ULI?e(&6C1&;еg= l6a&<8jtN#]EQ#^s6BH$$ԓSJ)WH,g1p7>G(XSShraN?Hšcd; Sj/F?#Ԇ`EK&I)=(4$蕤ZY{KYIp8E<89.;*̺Sx/OylAiVE"]X쌌Q;Hi#uLRRSJ ],sԷT_L-2>O( ."% !TktM:ATAQ`"xt]VQ?H)%$pҰbXK?n.4za2cLɘ 3w4DHajCn8uO~jeBXH}Uӣ!rR%I,IB)%ij_0LIu1&Qt̪Di}MOIU6&tڨST NXb1raxq2?d@ ˄m$*JI)N+% cjLI4@SemMϩ98' DSjs%N ׼ aB1t7hǬ"4bH;12MEJb)_=-%tB.7hMT 6ߢ9rNmq:f'U SwOI5=fxaP8b^Ldi4&ܸs%Nխ;4'tD/yON='F"B}FO ?pl0 9褡%F%<Γ}ZsNIYꅡPw?S*waN9ಛSmlMq)e4ȦϊS/eL1NՔ{V W*ԵXUU!YS*oLl(T~ʍQw*'CaTIO)e=^TSJtNpE8ʛSkjlMvAX &_o'M11 f(W'R۪Z+6d%,;VّZ?epUۊVEWPriUUS*nLrU)ҤTjQ*-Dʨ ,T*P)? TZSJxiN):G*DX/_vZtVV Y.dDZ[UЊV*W2QU ʡSuM.`T R*9Fv2.T:*%P A:JS!*A Ugly4uZp *aZFig̵ !4ĴX`K-_+yҺ4WjZNgURZVj W~5[08aʫU_JT*Q=DTتaRXJR0U#z Tw*Q*,%Dਈ[:\!j7u*]Z}XKb&,+e7W\ޫeo\ZVj̵Y(eaU{2UVfWTŕTU/ETL*Oz7T*QR*QI!lmvjPBCBej0뎻AJ*\J|n-ε"Y«0qe,uZ•X& _SExή::W j݅[=+YiU}V8IX.^2o*KUUڪUNzU!yT#*}O6nT+-{\'E;:$\&kyMmҭ%(ЇYk3Ee%䱛(bçXLk_5{`*bW8jE[qlbUVjjaX+a>U{U*)V*ͅXʪ[«UTJT誗ER".UBQu0W9:1[ ;If fF z^ov .̸:ݩ[gckeKed7xbeYw(dd,u5+BX"_wyԮؒ&W*[+nlFZVkYa̬~ʯPUeVZ$UaBUkUKFsbVKC"Hcp U0V j$j/^av.'W|[ kl]-kVυY1ey5hV,mXh`,~*钼FWlQ\+%pd VJZ7EeʬUjDVW*]īUp UJV/`s_ؑvNd6RV0Z1=WflA1f3|v `~`ꆼxu]똍q.彤+ԒؓZ֫S]iгg:ˍYE #cҬgŊ X!+u_EzƯ"WD \P+nvtfk I VqOYSS#c `uf? _/ ykUʺ;\Tko$-ĕVRZkGh"쥚VbɓY c ,Pu V4A&!B`1K]}밼WE]esܮ[Uǂ{@|u/^9ͻwhB6Gq%a fX,ZcMh٭a3D]e$sck\( &k_+|/fȼ]y][kt78 t.rYV>ݮqek[崑0ٸC1e,s4Ōؓ;aike ^w=8N+t.o18FxgEkb4Y̳4>e }2:ZOثsal,Y0Mvؿ^396;W\(\ӑqM6ЮGSim٠3vmmhC(d̀ )fOo ` ]/3stch=ȋ iulOn4񶒊9fl2kElDzXkrbC0&z:ctBde8'.>jwlwMtMu6RgK;^fЌJr\[&bՌM&ec]Cr " d7f&^k-JA~4yc4Ce ݲ>F@\/S b]n 9 @Syn_ uIfh,u3fdRP&KdT|1NVۏq/7;vVٿ[ [KjA}c:Y4Tf MI2OvCljӖq!ۄ?bk\Ͷ:flɕ<2&NZkܲTq O; ]kMm648*[8]f,avW+C ouζ|׼Z+Ti-)d4'&|Ύk2sV n8PkpMmX@[ZfHdhE3g7HuۈqpJqQ7ْ k\2jKI"lq!BVLk[bg:lDuAVAj;CIrn9F,P盒;m m6fCgHr.9F/6[3mm͐u5F<ԗr+qN"u7vt[il- |Dqcpl-e7@ތڣ[/akck99,/k~wo/u6͌؜ZcZrm8-g8n$D"q*N<'o-IŒ8 R"|p,@:_;+-|nWJ;_z_'|Guᄑ׹z^kzXꂽ:WO^ky1垼ג ^6+ŽxR +_zk}澆k_"k|1f׸^MzݯV5*2W#^z%y/^W^2+=x W _v+e}{{̺?_=|}׷^ǫםzǯT'Wz^vͥy,W^-ĵxu W_p}jJsW˪_k| {ν׵i^kzR钽סz^ry}*¼׎^)5xeWa_+}RW_)k|M5ֽ׹^ҫzYꂽ=W:O^ky1嶼W ^6+ŽxRׂ:;_+}ȯW*_.}|c.׻:^+٥{\UڽHW{^υyʯ4uדj^;Exᖼ"W:%^km~6UzWj_K|֯U)Wj^{viU쒽|W*M^ҝz-@炼י:^Q-xKWZ^ xWJ_g+}D`WJ_+{w2W^+zM5Wj^jky\&VwWY^!-xDW_m]}_ kWʪ_5{yW I^֕zOfWj^nk̝yr)䖼W{^'+xZ5b*_sk }u5vxZ5_|}UWj^+EzR龽$W^sMy,U׏J^++]xj 5ࢼ~+_x}ξͺ__!+|,RW^kzׯUu/W*^yky.UFאJ^0x{ U W^%5}u;_ך1^`˕y\)בZ ^=+xͯⲼNW^xI5ࢼ Wz]+wˮ»_eU}>U]W_ ]{߯vu׳J^ezTB=WJ^k=z@5¼Wi^hmy},^W A^Cx"[׊z-^!íxe ׂ:%^wz5]zZUש ^zHFU芽Wj^sy3"W*^N]y Ҽq׍:^++xu᪼-Ws^ +x ߲W}]+-wbW _J+|Я&^k{viUҽW^ezTB=W^kmz@¼Wi^h̝y}-^WjA^Cx"[׊z-^!íxe ׂ:%^뿽wz?]{Ug쾽װ:^=zSu:W^+z?u疼כ]^f+Eyx,UFג5^Bxu YW#^ +Åx_ W]+w5zZ)]k}rW%_9|uWJq^+۵{`j꽒W^֕zTB?צ^mz@¼Ws^h̝y-rWjK^Ekx6^׊-^!íxe ׂ1^뿽w.{ ?]%ws׸^-{/c5y׭^kzN钽'WO^ky:W^^+=yQ(5¼W^:kx¯U↼H׈ ^}x>v W*]kwƮޖWx]۫}ޯUW_5U|~UWq^ܕ{|lBWJ^EzүWꮽJק^kz2C2W ^nkMy0uʼWw^Ikȭyzf׋Y^'+]x{ Uj$׃Q^+=wr| a]뼥w5ݢt_ k|}f׻:;^ۍ{[h쾽W^kezT*=WJ^k=z@5¼WJi^gmy},^W A^Cx"[׊z-^!íxe ׂ:%^wz5]wsubJ_g+}D`WJ_+|\&3Eȱm*\6!{ā)L #ju'0 ajb&OB&0((B tl8XIBVYgYt]!Y" QR<-ڀ@2Sbz@4Zv o?b*Д 3fn BX#Zo@{q 1 bp&U"@~f,waVڡA1K^0@݀;(AVN@/ \!8& C3@-p`H|cmr@0n>UXL$[vn wZx ZWU((EYQ@06< `ݐ+|> Kpk #R`xG#ZNȺ.g ֢[`p<"ᜈoe@۱eS]p `??I1t xl' ;-@ ;"c~j&! C~Z0#@BS#0㼈o;aȈRzgm!2=d_\s]/0 0 p$6 ~{.0`a q?#(p2~ "Ђ$3Ma}o!\P{bNc`i;X ȷ6], t؂r X"](#q G.yXFq#~4 cƕ4$jl:a? p جAVN!)@8n #} @* Gi g9DSLOSU\^W<6ԣ+)x#{FY&pSG]#: w- *B C-p:c`<dAT JY@7ti&D#b09 (npl5ÆցZpc> < *W8*i t<Ԡ[T _Z@ ^h,h@EUt@1H f>hJD%D\X$-@{8mX}،ݚ1/``N4J`3 @H#Ed1k}PI` Ko;f4o #EhWY$$&Ch#^T8 ) gXnF$Vڡ-zBڤޗQ0*` )|2\`q`S{EEL7  3`#0da]@qBYT&"PAnz'$Юl%>W^eoar6 w(!<w_h,6I K@< K0=$i8π]W@') p0Dx%W OS/ۈd-Pڢ? kAp8~x 5O`R +t:zV;d1p K}I j*6@ ;8(4I;Dyu6nW<cP'1[YJhXejH`P1xR p7@xXx+?L i:瀕!j2؀8 p}#hD&pH&zq?FXڱWCfTFad&OԐG#hcx X;!80>$ |J@x(J#`;F h^.u{ lI9-~#tF}vUq! DKD6 lB]!!U1@i%i>R?. } [$e Zzv3 ,C /UkXćAFJ\J1 dC[{%LAzF iQ@}0"? \$сO-`30M v&YnXЀS$JDdeC%D1,@luCp<>Cl=pMCTpit<<]Cj'tq̕iǕ02gp.8<s|F>`R|Cz;Sè]p: csSC"9fOUIC9%FCÑLP09?Ï&<8E6+Ì% uʠ0*GW #B܄p6/= `ʄ0U HC82 *p˃3 1C8P3 CD0 49 0cCO 0L\55m WCVp 5k lxC[.3E nIii%Rh28B/e_Cכ\L!BMP%! B~p') =¹\, aY/ H\0j %=C Pë,1k L1 pW;023 OC&G0=t2 ,˃- XQ_e. yBQ. B1\/7 Cup0L C30 CG)P|1Z_ ^C铊KCW10򟫸M2Z>m/hű;@P<E{. !9pP0 % zB~p) ^VB|0nj*m PBh+k jB, @B3е- xB᰹5<.x; /O {Bо+/7 p\0)B.,pD$/ )XRЖ\&#U p't' ‰簣<)R iŸ0 4* PB#p+? L, 4B-дt-aG d۶pD.; 簺<.o Hpi:Jϥ#V辕ܓ5p3L+hPcD-3<pyl;9B ;p,T!)1dpmD$ Y )XQOp4& m4'i B( QvB1d*)M }«*,+/ ¹갯Y, JBʐ^L, B{p(31 ѿ& {/G@1[kd@-w @A4:\PRz!s`9%d+0kӄA͝0uD;0~Ud #B!!IB+9jD#peBAnj$# D+BU\% BgM& Z+h0"Kt-lC}OJ啣:d~hؽeYER۹{r?HpUl@V;T2$ ŞApE"J)BTJ4x0amlA0ujLN}MB `T!U~B%RpD# g; P <$S & N\0%sa lB_>/t ?eoG|P@^|Q{@F2| AA:DT=S q_ItM0j HAPstQt{.C/p RBL"Ys0 Il#Q6CP $# @-mKÕ 軴Y-~:mn*[Y=s{͌F};/.26M]+ňCq6 p 4)4 3;7;2Kt1SpW\A>pc e#0lÅutAP|-B,! X"B0"]B/p#qAуP8`CoT됶?+o?o3vr0sV_p,A<@0,4 <N@bph ȀʱK?ogc JiѿK\,5) s7@09!P6W.Cx>gDnHR0w˳RM<;o T2@=PC=}ƒNzZSY"?)/x~@Oī@H0M @U0S-.D]۹c 3N-'rKT'|u6z%m{y-8|NG#<:gβDUj;pS"/ϖAc |>gﺙk6ܿHd ?oc32lX=j#5 Jg{bF7<-~~9b˞; %};# F<Hxk&=y$>bhϡsl6>OQ?c$`4SJz5 -xm.E 7Lv#u9y3q:;2MobF?==oNs@w6!};lUOJ>,[\kN *զ͛ nݗ7NC}|x!sA:z{BCOH{@=/vD8>Q>/KoϽjcH^D{"h17y9m3sc9 `BShR:.Hݻ!kr-hO5бbje(FzB}fS2.63gq2$I%L9mNuȣ]:$׃tݛ/X1f=4Z>P+/jc7(6>OkQv{b6Rs ׳s/4tR J]9(t{q RncD7;>f Im)gsgH#;x8yNJ!{H'!*UqF;@)ֶ1ɫ:2]<J{IBԽ/sbhA[s3 :NKӗ>A$۪߮˦4.ܶl率 cȜUE[D]E pݽ37΄ +Wo?/:`BSfaDΩ㮔쨪;gRG! >7 l:W ƈcL<3ȏ=Lnmd;8Dn;C=9hNU7:[zG1z?M;{Y.#8Y*]F\y.s++"DGγW+}X;w;@ <>mm|ZXd,[ a's; Ӱ ;nKnC~7ԯ+NQ9"ΓIèr :îZ#_aKM#撸nA;=9[NvT벫>vmXÃj8CcGm : μòH[8#(G9#nUN&I: .kS:ǨL.6ji9tSX:}n m|ec 9 W S: .zCn:n ønIS/x9"[QǺ. #;K"xNMfj}.ų{P9ki B#:D!ˌ? 8Y.\qs:nC9=n],X{:in4t9yBkuK0Hq_{gNW`dDBO湆fV3`Zk%&%F0]&f!&af浉ɒ։)F^%f5%f2&ZFf漧'ǥQ),IHYiɃI !*I 彆0fH'Ii&F'.B'ZȇRۈC{Hȹ Yi]e* AceX{fX&FCFqg` hbhH踨)eɄ) iJ 5ʆNeSfF_g ffeƤF g穧皧i ȢHeH)G))i &eeffJ E'o['!GCUG~蚈(i .bi śvEffD-e>'mbG@3bGǞw(hM(Ih'*iiCv)FOg[&2JftiK^GSGGGg (Yk{h)?(Щ钩Ffݦ6G%&fF&X?]GGh9(Rh+2HȾI\)절dFI9d62tFdƹfg^' FG!YtGggǡD(I .1i )@~DեyFgV&eܦӇƇf'+8kǠGH G7 :)I iFd^ HNE'{]fF֦g ;Gʇ( G"(ȹH+AF'DHDDŽ%1fkFg&FƲF&GJ%BgyGG' (Ha4HGȳh ԫPVeÆ2'JpfƯf ƩFgDžGvg%((BCIH_(pH+L6%EڅhEfƑ&k&g&2gX/zgH g$HnhZH) wm lYE%'r$2f&'g'CZ'gG(po(H j9fRƩfwWFƂƯFFgg4ǒzgHH (okȃHHi" ʺL^ ; ]hb%Ƥ&3E&NFfF|ffg/Y'(glHϨȮH)I: ,,u[ f.?Df'&F~Fg|ǩш '2Jy )Q` *:ˍ,挊!ƗFF(FyfƜf f'@.'4'dG]'G(8ehh 6o @grf*ofHf;J'1f&F&ކ'<n_mM-Mmmm M-N3.@?n2*N0CV]nUC,nn-- &nDaxNΒNΜNίλJjl{9?._bjnm--m.1./N.NN#Oέ.N.Vn̎NyI=eΡHJo-N6qNێn.nNo<߮Nt-M,, T PNNcN*EONΠ..^Pnt+M Y2kN.ξNŽinN M,l,,mx--8.M׎-.¯ONni{.΢. KNwN.-.yn .U ୸ % z ͍ H i8n΀.7Lͺ~.N&mN A<1 N>.-um?mkMν.nB.Ȏ-mnz-nΚm# -ݍ :M;΋/AHG5N-N4mxn/nHM=N.&p ҮA.Dm mx./NO(n ȭ-- Wxg;NE.9Un.n U, N®.jNc mMD)ͅM0NuNM k [ nn͑YMomzCMX JM_M˭rA 3 m> 5m\-!.Kek.nk1Vt=Z Z)Rȓ@ E"U{8 %z>b,I41e.l7GL5$; E,Axb2q@AFF% "`nG&R@? q|: ap:"!N)Wtd,ѰR$pX, l:a@LF24;D `* A L( !|B# u(e+CH ax8P* N_'IdA "0d(X,  X0BW\A^'0x0 @( Ba`l< nz>:$1L P P0  aPl `0`@( a`l"z<:D1@  P@  \4FL>$ؼF `AP.bQ WP#D5PX4  aL%j4qY%@L @@0( Bpt@# Kb)±\  @@0( a`l X^0L( !t>  PZ/:Dk-HAP4P X0! P* CAx>! qHX.FcA  `4BA0T, a|@"DHX. FcAnFLXh*¡`$p8  x@$ Bpd4CH%Bh`2FÁ A@`4a@X. ax@!Ģa8T,!n8K$ f+Q`: P( `h8a@X. a|@!Ăa8T+ j74 h4BA@X. ax> bA(P) f4 #~@c+(cJ`h, Bx8H& h6F$PZ.FC1p9 . A D B! P* Ap:"!L' EB`^0ƃQr;V%Ї3XA 0p4 PL& a@X. ax> DbA(N(bp`1 ƣat;G!p4 `T* h6F$HV- h6#~@!Ȅb9%TGXDł8F!áh2 pd4CB"a8T+ Ed3 Fv=$"H$Rr5 ł@J#l6 Ap:"!J&"XZ.C1l7GC~@ ȄR9 K& U&8 %R,CQZ+A@Ct:"!H% PX-#!j6#1W)I$Rt8hR' b!@F$HV,]h#h1%By,D cd0 B8J$B!H%HT+ EZv8\N%Ȅt7"hT) a0L&PV,NF瓙c/Ji(D!ǣh2XT)E"PV, E=>&1X*IIJAACd1 hZ-E{IzU(S)$Zy92lT(d1?#f3 F#`0֪q*D cb/RCp7XTI9>&I_.JB8J$2~>GbN#ye1 `U**"y0G"Ps7 &c!^.%BKȔ*z;Qd1KZ@K$Ȥ2z;&af2"}4I#Hdx:aҳViY$E!Gu9MuPO&j) @OglV)IU"E!Ј#Zjeu2Hh2\*:JR!\,ʕ*:K$(3xY+U*U u4JLVlVT$q4d륲ZS(-6k!]-JZd0˥`h5pk4Y#zjY3ig7mv@ @   @ A @ H  @@H$ @  C@$ @  !2 C!$ @!@21 F#B@ B D"`$ D F`0D"B!J, D !`1"B!DH$@!D  "@!D@ DA H, B@"@!"@ DA H%@" B@ B!" J%DQ(H$!D0@HB "J$ DA J$ D,BH"B! DA(H%DQB!D \( J% DA(J%B E@T"@ P A(H% DA@!D"@ E!@P @J% A H%@!D@! @T*!P0%DA(@ B @ D"H *CШT  D@!"@!D@ D"H R A T( B B @ "B)A(B!@ @!"@!DB  A (B!DP!DB!D"@ @ E@ @D0!D@!D"@!D@ D"@$@""@!D"@!"B!"@$B!@@GB!"@!"@!DB!D"@(D"@ D"B!D"@  @@D00D"@!D"B!DB B!B!H @!DB!B!B!R @@ #@!@ D"B!B!B!@ H!D"B!D"B B!"@ @ E H $b1 DB!D"B D"B!D"B!D"@!D@ "H!@!DB!"@ D"@ @   H0 "`0D@ B!@ D"@ D"@ "$D@ "B!@ D"@ "@   @ @b!"B!@!DB!B!@C#@ D"B D"B!B "B    !D0 E@ D"B!@ D"@!@ @ A H$D" E"B D"B!"@ D@ @ A @ A H$ @P( D BHP!D@ D"B!"@ H$ A@ E@ DB "BA H  @@@ @"@ P(@!DBH$@ 4 D"B!D  "B!@ H$ @B!D"@ @$R)"@!AH@P 0@ !D" H@!DH$ @ (@ B!AB AH @h4@B!D @ P( A@ @h@B!D @$( @@(@@ B!D@ @$ A @BD @ $ A @(@BD @ H$ P  @ "!@ A  @D"H P  `0 P  @d@@("H P@ D" @ B! @  @0@@ @ @P    @P    P  @ $P X P H,P @@(A    A  A`4 7777BUFRUb 4~hd!]}L!bV@.dEF FUL (!I@@@ @ @@ @@`a!!AAaa@!@!@A@A@a@a@@@@@@!!AAaa!!AAaa!!aaāĂBBbb""BBbb""BBbb@B@B@b@b@@@@@@@@AAA"A"ABABAbAbAAAACCDDD"D"DBDBDbDbDDDDbb€€""BBbbbbƒƒ""BBbbbb""BBbb""BBbb‚‚¢¢""BBbbÂÂââ""BBbbĂĂĢģcc##CCcc##CCcc##CCcc##CCcc@c@c@@@@@@@@AAA#A#ACACAcAcAAAAAAAABBB#B#BCBCBcBcBBBBBBBBCCC#C#CCCCCcCcCCCCCCCCDDD#D#DCDCDcDcDDDDDDÀÀÀ##CCccÁÁ##CCccÂÂ##CCccÃÃ##CCccÄ##CCcc##CCccƒƒ££##CCccÃÃãã##CCccăăģģ$$DDdd$$DDdd$$DDdd$$DDdd@d@d@@@@@@@@AAA$A$ADADAdAdAAAAAAAABBB$B$BDBDBdBdBBBBBBBBCCC$C$CDCDCdCdCCCCCCCCDDD$D$DDDDDdDdDDDDDDDDddĀĀ$$DDddāā$$DDddĂĂ$$DDddăă$$DDddĄĄdd$$DDdd$$DDdd„„¤¤$$DDddÄÄää$$DDddĄĄĤĤEEee%%EEee%%EEee%%EEee%%EEee%%@E@E@e@e@@@@@@@@AAA%A%AEAEAeAeAAAAAAAABBB%B%BEBEBeBeBBBBBBBBCCC%C%CECECeCeCCCCCCCCDDD%D%DEDEDeDeDDDDDDDDEEE%E%eEeEeeŀŀ%%EEeeŁŁ%%EEeełł%%EEeeŃŃ%%EEeeńń%%EEUUEEUU%%EEee%%EEee……¥¥%%EEeeÅÅåå%%EEeeąąĥĥ%%EEUUeeeeuv&&FFff&&FFff&&FFff&&FFff&&FFffvv%v%v%%@@@@AAA&A&AFAFAfAfAAAAAAAABBB&B&BFBFBfBfBBBBBBBBCCC&C&CFCFCfCfCCCCCCCCDDD&D&DFDFDfDfDDDDDDDDEEE&E&EFEFEfEfEEEEeeeeƀƀ&&FFffƁƁ&&FFffƂƂ&&FFffƃƃ&&FFffƄƄ&&FFffƥ&&FFff&&FFff††¦¦&&FFffÆÆææ&&FFffĆĆĦĦ&&FFffņņŦŦ''GGgg''GGgg''GGgg''GGgg''GGgg&&A'A'AGAGAgAgAAAAAAAABBB'B'BGBGBgBgBBBBBBBBCCC'C'CGCGCgCgCCCCCCCCDDD'D'DGDGDgDgDDDDDDDDEEE'E'EGEGEgEgEEEEEEEEFFeWeWegeg''GGggǁǁ''GGggǂǂ''GGggǃǃ''GGggDŽDŽׄׄ''77GGWWggwwDžDž''77ǤǤפפ''77GGWWggwwץץ''77GGGGgg''GGgg‡‡§§''GGggÇÇçç''GGWWggwwććėėħħķķ''77GGWWggwwŇŇŗŗŧŧŷŷ''77GGWW''77GGWWggww''77GGWWggww''77GGWWghhh((HHhh((HHhh((88HHXXhhxx((88HHXXhhxx((88HHXXhhxx########$$$$$($($8$8$H$H$X$X$h$h$x$x$$$$$$$$$$$$$$$$%%%%%(%(%8%8%H%H%X%X%h%h%x%x%%%%%%%%%%%%%%%%&&&&&(&(&8&8&H&H&X&X&h&h&x&x&&&&AhAhAAAAAAAABBB(B(BHBHBhBhBBBBBBBBCCC(C(CHCHChChCCCCCCCCCCCCCCDDDDD(D(D8D8DHDHDXDXDhDhDxDxDDDDDDDDDDDDDDDDEEEEE(E(E8E8EHEHEXEXEhEhExExEEEEEEEEEEEEEEEEFFFFF(F(F8F8FHFHFXFXFhFhFxFxFFFFFFccccccccccccccddddd(d(d8d8dHdHdXdXdhdhdxdxddddddddddddddddeeeee(e(e8e8eHeHeXeXehehexexeeeeeeeeeeeeeeeefffff(f(f8f8fHfHfXfXfhfhfxfxffffffffȁȁ((HHhhȂȂ((HHhhxxȃȃ؃؃((88HHXXhhxxȄȄ؄؄((88HHXXhhxxȅȅ؅؅((88HHXXhhxxȆȆ؆أhhxxȣȣأأ((88HHXXhhxxȤȤؤؤ((88HHXXhhxxȥȥإإ((88HHXXhhxxȦȦئئ((HHhhˆˆ¨¨((HHXXhhxxÈÈØØèèøø((88HHXXhhxxĈĈĘĘĨĨĸĸ((88HHXXhhxxňňŘŘŨŨŸŸ((88HHXXhhxxƈƈƘƘƨƨƸƸ88HHXXhhxx((88HHXXhhxx((88HHXXhhxx((88HHXXhhxx  ))IIii  ))99IIYYiiyy  ))99IIYYiiyy  ))99IIYYiiyy  ))99IIYYiiyy  ))""""""# # ###)#)#9#9#I#I#Y#Y#i#i#y#y################$ $ $$$)$)$9$9$I$I$Y$Y$i$i$y$y$$$$$$$$$$$$$$$$% % %%%)%)%9%9%I%I%Y%Y%i%i%y%y%%%%%%%%%%%%%%%%& & &&&)&)&9&9&I&I&Y&Y&i&i&y&y&&&&&&&&&&&&&&&&' ' ''')')'9'9'I'IAAAAB B B)B)BIBIBiBiBBBBBBBBBBBBBBC C CCC)C)C9C9CICICYCYCiCiCyCyCCCCCCCCCCCCCCCCD D DDD)D)D9D9DIDIDYDYDiDiDyDyDDDDDDDDDDDDDDDDE E EEE)E)E9E9EIEIEYEYEiEiEyEyEEEEEEEEEEEEEEEEF F FFF)F)bbbbbbbbbbbbbbc c ccc)c)c9c9cIcIcYcYcicicycyccccccccccccccccd d ddd)d)d9d9dIdIdYdYdididydydddddddddddddddde e eee)e)e9e9eIeIeYeYeieieyeyeeeeeeɁɁ ))IIiiyyɂɂقق ))99IIYYiiyyɃɃكك ))99IIYYiiyyɄɄلل ))99IIYYiiiiyyɢɢ٢٢ ))99IIYYiiyyɣɣ٣٣ ))99IIYYiiyyɤɤ٤٤ ))99 ))IIiiyy‰‰™™©©¹¹ ))99IIYYiiyyÉÉÙÙééùù ))99IIYYiiyyĉĉęęĩĩĹĹ iiyy ))99IIYYiiyy ))99IIYYiiyy JJZZjjzz  **::JJZZjjzz  **::JJZZjjzz"j"j"z"z""""""""""""""""# # ###*#*#:#:#J#J#Z#Z#j#j#z#z################$ $ $$$*$*$:$:$J$J$Z$Z$j$j$z$z$$$$$$$$$$$$BBBBBBBBBBBBBBBBC C CCC*C*C:C:CJCJCZCZCjCjCzCzCCCCCCCCCCCCCCCCD D DDD*D*D:D:DJDJDZDZDjDjDzDzDDDDDDDDDDDDbbbbbbbbbbbbc c ccc*c*c:c:cJcJcZcZcjcjczczccccccccccccccccd d ddd*d*d:d:dJdJdZdZdjdjdzdzddddddddddʂʂʂڂڂ **::JJZZjjzzʃʃڃڃ **::JJZZjjzz **::JJZZjjzzʣʣڣڣ **::JJZZjjzz **::JJZZjjzzÊÊÚÚêêúú **::JJZZjjzzĊĊĚĚĪĪĺĺ**::JJZZjjzz **::JJZZjjzz++;;KK[[kk{{  ++;;KK[[kk{{#;#;#K#K#[#[#k#k#{#{################$ $ $$$+$+$;$;$K$K$[$[$k$k${${$$$$$$C[C[CkCkC{C{CCCCCCCCCCCCCCCCD D DDD+D+D;D;DKDKD[D[DkDkD{D{DDDDDDckckc{c{ccccccccccccccccd d ddd+d+d;d;dKdKd[d[dkdkd{d{dddddd{{˃˃ۃۃ ++;;KK[[kk{{ˣˣۣۣ ++;;KK[[kk{{ÛÛëëûû ++;;KK[[kk{{ċċěěīī ++;;KK[[kk{{  ,,<<LL\\ll||########$ $ $$$,$,$<$<$L$L$\$\$l$l$|$|$$$$$$CCCCCCD D DDD,D,DmAjPЊB _A&7'`70|R"!/ ~xR TZA](A  'XV °)AN *aPw0E (X*D)R!e -`7CD =npപP4u ! hO,yf `.c> qj_CR?_hD2!9} oSDc>"A<%J PYNt1%Ie K(Y"k3^ tOxĐ$@"n{v%,d ϸN}xTt,Tqb\ zś,32i#g=pਇD9׎TwӰGz@2?!&"1 ;Y;XPAN v0'tV 0\{ Ɖ|5E(wüȈRB/~ &_q2tP#Cn-'ai; p]DFG:31ag@k:tG:oxG Aq" GU`)zK `p]D%8 ")c _6 ՜6[zC l`D6<Ж'0990Qɂ@J-1od_S#3a4! l@:;aLzfc'q9MhBD ,ff g,,$`G?dlX\*q= c Cbupӫ X8R݊P~,`!=a E߂J PHE,(@ m$Si"oxeE*.!vQ  `"p~{5wTn"yT^9Vq "QG"WD(ֱFUÞŽV/|Sb/XҾ6`Qp%cQׇn=(AB} D)GV>\倲 3jTpt[t {NBtRS`p. \iMPs;AxD%" $= ưN73A9hqCNJp#G=8Q 6a.)t NJp)H 0_كkd[ Dƌ4`fH9/~x#A9H @U *1T! ,p.ÁvT>ڢ؋HDZB$$ PLj~aUE2+GZ=> YUpn v2pgW܎t8ưu Óǽ?֐S/! n(#) D(AG _ug?D $x(ak X_$B"{pۆʬ6Uanz= >{C:Q'8 -xQk¦k3XdE"-nO H^CqF[4uDl#4;VڵB@z\ B7x)6I/<Q,0a e84GJ:Pp3Ml*kan tbiNIM7!8}}!t8EG8DĂ'Q>aSBh@E.t͌`ΰu5\n`GN<$'u{'Bbs h @l( =(E ݀^c$ CBbp+,(a`q ^.hE66!̥e(y -lwC8ǐ0&Q7(dQC"X*-m^`,Yd4za l\}?1;BQ=,ycTQl `B bkl` /ix @P7!z yp%5) W+]-1h@HBBp#ݴ&Ra [a=4t! pvhDLd>A*Wu&0~ OB/xdx,f ]cǢF=3a R(jCo~H:aQ x'caA* VLd;M!i MdCDR"iH5 5؀P/7y lDb!7qJJRVE(}+zk Xe^* O.H9rA5kX!!iFR`x(aD LTb (/RzAb+TZў6᰷ ocGe<}(k #YH?"BB܇V3!D w@ hS@]p-h =I Ph9@Apll#ca6A TMBjŰ-<0\ o`{`;p>stmSl$J"U ~Kz tE++ QXb/HYzB7F 1ұgS,`ۚ8įtXG?bE<),u!ctDx"#˜\@z" "i' T-(5A ][ CB (d^K X^O~D&5fS0yVʴ @mCn6h@c&!6A Pp{-al[ ^xcwH}S4Nu k{劇,T;ب2Ld", 0Hi^@Y)G1J9UtS`E0 qSncv6@mFj6ͱm q#p|h=~ ~ UDbB#N܊vL"dHR5:t ӠiL!- n OՂDZ (<:L$aA WA ~K2] prB??BаBj%( Md11IEBJ+!^ ]`Ze(\2a3 hnc^iL9IMuWGJ@Bqă$7!F 0Me )f#6-Π+#Y {PcڃU F01;pۅ:)ai Na7 ܆7Xպ|8 A DAp8'~;KR\bo?E.?q_$ LFjb5 mfC?1YGB;ȱE^z#^BR숏d_" SHښG:b95 heC(  O?.`ԙ;A@$@ V·h0AE2.ᬝ dqC"(PB?"0gX$'P M5$@?\+]]VZF2kѓ^(h9C\F90Ʌu$I Ȕ?!k Y6!9QFR6B=y~^P-*$ =*V epc+,)`?EB.%,54)hA B`-<7)C{n .DD"@E(Dr'e;/DR'BGb8.&q0T_bg4QJok~iLH8IaKHrE#SZ;d!py ս?Q ; a O&D-"!iA ]"G<cX3hCTؓĞ6! oX@ 1:aQ vn H8=a l};cnq@H A* VLd;f!1>HEk+_D$q#!H2F0L `g99 AZ NT;;^0\$? XV³RP@d2! XhqCPz?}"xd$q&{ME< +!\ OZ} &/~F(aD>I3(G h#Mlˆ\6`ncpK"^9!aux؇=OQz |Qk\@cP&!4 hXD ߑ=HD\"H"Gb<+$|$|(D[,Rb^"oc~+_30Y_Eϲ/Sz 4`YG8v;2ѕgFz3Ƨ6᰷ m|{}G9xltcp P<۱ݏm8{iEx@@rFh3D- i 6HYC6 ĉ$U"!ƮG8uXČ)$aI&{3p,{Ϭ}`v*AT Ob|EuB+ d.)wf 5`ṵe6ҳ{iN MjDR&p3 tNT)бNUPjŋV-2Qi\pȾ&0a Zb#$3!ec(3q2i#Z4Ѡώ7U PpZg8r;:AպTwh3@ǧ>Q𺏼 }A} |32!1HD',pi#MHNBwJPD%+v[%&)7K"m|k(qD+WDR"d =a+_8/HYzBe(;/G!z9 `={89219ߴfE,Ʀ&6ѰFm{E(9hGt#~<`iP{J?8XG@0B3 WCőxT"QHĺF7 dH$ZA&= 1JhrSCh@JUj@R @go x`4jVvOL a" 8O^z7.op sjYRE,}0;@M*h#GbKNZp+ı^&b3ΌNtb6 `)1N!U4,Eb-$Ai" ~[hE20 Vb#93a3a# /di{#Y:٭h7Dq#hp;Cm{h99:=lvcn+pG>=8} MhAo |028!D&kċ^$h#EHH?RAF 1$+8 YK2]9mCn)H8m4$?!F8J1^x '< atE0s! Rb"?_3 +|i[X@nt74ѹpssG7:ۄv#č$h{=P}qHBA[B l1%!)}pDIJ&%gx;g#HJjT,+a^@ZPֈ/q|k`QFB2_HgCIN`Ʈ6L` LnJchX91yu G=Azy{`e(@rrR肗D"M!i _CRґ,gX"!G@:<9{${'=I9JUPᘗ &04 Np$5!\ r/Cze(Ib]蠚'< lPb@*V WpbH .?qŬ^-b$F"1d4ОF5 v|kkX8`Nrp](;q&y7CMh1Ɏ?%- N jqH5BB:$C"aWȢE/@zc#!IZ"Jl%/I}LÒfg?'<=I H_*Ð^)M `r@D%8p!) ac_ _p̣4; pg8~r@;O} NQrIBOȋ[Z#f!I Uƨ'q8sO"^O2x*lSg$V" PH-qn[(]IB IF J1+\ dC-n(v~4!i XjcchP71i1qÙN;MAj xL]>q$ 0'NB4٤<`!HJE{+r# "HG'od%Lq-InBLNbbsJHRD%');tIۢOb}:)a\ `Zg>pL0a g?΀t7Oa>uOCz-h>! DJ! \dD"3`CD$'P pK"i{K؝Ä(HBD HlRCb=Y+]8 Ybd ż/x8_c ~[>12hEϰf}Af 0ӾF5F m@w> 9'A:t㭚lGvZnq@H A)B J*!P!IPJ-`k(M nPV¶VF|.Ⴠ dD"&@:5} qD0!X<:7y2 ^B%(ed$1 6I[8 D'_:8Pq‘@E<*1UW.7E.Ap x]š1| |d24a mko#hFݕ8>qEr/C h7(E'/A ml@h@˂\ `x#A+ \NEr,gp;P(*AaR [B`#'d1;!: i3CI"<7"ivõ` > P !|Ek8Č%;A) L(nvD(D \R⤐$dx+_΋4\YڪP H/XQzŒ ``cljFD!!iEFb70IĊN%+)] Lml(!DQ XpRÂ}=a+_E 0Ye(/G!z9 `@{1JV +i_|l>&@:WT"#h @L2Ba⠟.)wY YBb?001 whg 96A&tl䇴=AXt&Cp T4B䰯%-uy? a @@P42 wox](:A%X||ψ4ABԘO#XqÉ H`"Pb}D&5@OJ2PE'*qPU27$0u g38D' %a<6 Re“.o|,,aq `_BWi3H! =mMCjj)`K?9݊PzդP2@YȈ.At" GuIHxD&@12XN2E)LT?AZGg#=' <M8i$@">)]8 ZB240 h@ܐp7kȽEu<;؇H>"As !q D4[ؐox$'rKj*QPH(PQBIRO;)D%8@%: ׈R!\|,kp _BB@dT3"v 3mCh;,9a^zo~r@b-h?"wGPBG?]&+1_ N}XEc #a&V 2Lgn:)a\ `Z[Blg0! g>F`[7?ǑU:!!!1 Exb9$ čn%A* L5bnwIDBJ"(E'MM jhV ֈ.A5 d%!,p`5k XqÌf<`<$t@6 [tBۢ$p#tDc#A 3IZ&0Drd'!E /TBw<-{ HbJTk^x4 o}09gP;:59}2 ">(AB芙DT# dH"R`D`0)!Y YB*G`;\0:8 yg2946 )tp=![ ؈HDbB0(| $q%cKAD &pT¤^ -azk XaUʯ4XS om{hP劇a; 0H|ڃ e9xA0NDRr#iM HbQpRB`+,s `*Vm3m FpmCl:ԇD:'ޝz@BSmCh"qGJ PHT@¢@2-xy? a @@P42 woxpۇ](:A%X|`3AzЉO#S G@z  6a}s=T3e (|!d9 !պxCƚ PJ?hsׇ@="PA<4x(`<@wÿ|>c@/W <-h UP" P @)H%(-7`X U(@F:4@Bo}d$5$  _"rxCp -l?/um h'ˁ>\ ЀNjVqÈ%, ?0IF6Pb `\{ۤ P,e X/_*bS tS`nZr@`4 ׀б3 Pf2(D@1| 9a̓81Ox/=y ]h Ub"Aw0C F0x >Šp8~ @ovTۈ@6RA skSҾ4&0 g 0 N+́^d ʀVT@J>0)UAJ ,Qe &с6 LwỲ> |iH<ʁT`xgP:Vt6| 88rÐ ou(@$ 63 ek-P ` 3 fq)`K>p1] 0PaP P.Aw2 \@_,uAc @WT"KXDIB"J4,!a XB6n@r @B08=AX z̨e@ ;A݊@v*TB=9Sʘ-qm @6t lZg84 Pgƒ4ʼU2Ap%Wa( GIMtL`c$T `G=  Ii#M_E,@BH"@}pChBB $!$a7A)B J$Ё& >ׁ|ڸ=u Ox@:8' 6s MBh%,&a.& q0KX04$% .HBG |#F5<<X"@Dh#D3! hS@B] o!: ?Ƞ~EF@Vy W*T PTB 8T)¡L! aR((AD 0Px0'; NXBrsԸ&2 xL2Ba蠗E%A* P`IOhUt$c GB<p#g#=EO*~1C"!qV [L-rai MYV] ,xaa @W¿poh+{AY 0U\N4*qQ Sւ`,)dI HQIN (N@ Ow{',!7 HM8i(E0& .& q0JWDŽ$! Ub7nu0! `PC𿿅/A{ ܸ^bzׅֈ.As p\tзօ-!k \Zg>w,c_ XNBrs+Z 8V"d$P*aQ XS𦿅,)dI HQH0A (N@7 hCD"'z43ѡ f50q[2- hdC&rȯ=1Av kb60a -0`dP҅/!} X^.ݐ.u% (\0 y-al dhZԜ,Ac Xml௯t+[+ XV;moh73A 0mCk@6Hᰅ (kC]"`[5^ HiσN|TҥX4j{ g?n|3sᙫ Xe/nYȆN2v c8`ƻ-@1jC JaC:Qˆ 0W X_B `#/G!w ] 8a oACz ݕ7&t m`k866! } krC[0ц 59 =iqK:ІP42n pgXC:͝d3% xe2C)+^BX2 uc7!$1 !U 2z|C$<& x~\;ݼvat; ֛u6C.PrD:$aHz@sU &90>qsË< 8< oy{7< mrk8`63Y zkLZf՟d5#!% 9(i?I0х4 PjYR`?4~X>E(|`>$q {\=8yPCʂ4񦇆,<1a߰woxgP;:u<?Ht:Cs8`(9@A>qsË:ԇ86 @om{h76a hmlkdK\8@0M*h!8 ňGBA8|| =āIbHzx$<͡G"8xĦ`#yH;Avô> Xx:tl99TjsT_:*WTR"zԈD$>!DE*"v|CBh@7!a `Cbyȅ4)!/Q zDB&b|Pk^ ݈'A?"@( T F4 l@KbX?Wĸ}xI`Kʈ>TA|{CB@JH=ZAF(yP򇇐T<|@H?\z#qG@8\m#R1̈́Fl"1`"&,Eb*VHHR@"tQ Db#ЈyC"yCBnp5&! \[B؂HD%!<4A hDJ ,@b(x ?X~nw@*1Pį%eq++SJS7c$Q'8IǂLe`Ė$$ `HE/xĈ$)!NlHb?ȏ\z#qLG:b8*Pl#GA:,FYb0]"Ee)kKXN"VAE *(PQB Phb Р'?$ Oz Mh'u;\NspX'8 MleՀ&4 L"e(v&0 ~xK]4%-, c KW&6&5 VNzpo[|*V UV'I8O.*cST"ȨbEC)O y@S6`)qLÊ_R#)).1IqDR'B_zs(F(QFBpC (PQB DPb"O xE'?LOb}'>'= OXyP:D'~;8NvF0YD'HA:B lN[bqKX&!7 M"npLDb)H =QŽLr`a(ȡFE .@QrV@s(ADj 0Q7ȡ (SqBP*P(@ P" PD'> |O{߀'q=#O,xEJ,Rb Xv@2E+_ W"ȮuD+\lVb Xh+-!Yi V(B}\D*ǑV<@UZ'I8O.*]QRꊐLTbjP0EA)qO{S(I:H)N. mSlBnE3r)L__R襀,)PJQR".pE$)H B,Rbx{.(F3QŒ&a0E(aEK &Q6F EJ(tAC TPҢ-AE (BAB V>" `^*qWU͢l`7EY*1UlUcb:QЪPR*T< \T⦝4uEK*MRmTB2Dp*P TŸ|ৠ=)ۡN sS0E5)AM d(S!B[إޅ.)lKeWRB"()7IK(RYBHN"t)!H9 >DQ"܅(Å-l `["RŪ-=Ai ItZKдEz,f֋0YʿUKŒ^,ycϋXM"hEz,`tW󢾣v{+1^WBjPEu+\ Wnn+ea[+ pVhg+-!Yi (VAB `^*qWU͢sk*YV*U؊@UZOx=EQ*AT Tb91ȩOJ~*IRH TuD>*qPT8>0)qOv S7)M iy+^ DWj"HjsP+!\ W"EmB+_!Z ԨVBbgf+.YuVG[عyE.PAr T\tEŊ.Ap p\#Tž-Aob w([BŸ-mj[UBH:-l \Zwe,-IjN OZ}$-h A@Z o{xŝN,f֋30Y` …,!e! % Y)NBpᅏ ,kc_ tX.)p,;qaۋ XXZ@Wł, q`KX" E|+ڑ^ԊWC N0p `CD" F0a _B/} _F˅\/[qzۋ8^_/x ^ X.wW]h0خ.au ]XOAE .at+ \5hyE.PAr \[ظ.!p D\" HP-qo#u[܅(H-mn g[HJ@ ,}c_IHE/!~ (_B)Hޅ/}L_=B H/y!{ ^"HEB/G!z9 ^oE/qx`^ w{,.qv󋳴]>aEֺ.1u1]6EйEϲ.qs\\h+@!.8qƋ\X :.!p |[.pf0-qn[p["@T-ld[&bkXůF-oAkz XZłՖ-Ej-AjM\Zj8ǢF=1ұo4cyoxDF2"1y Xbǃ`F&j1qAb wHVF01a/a|C LZ`¡01ь#a,A`F0iN `%)(09Qʌ `V(IH0Q:_`/1~_`d/1}$_!"ZнE/i{L @^x/4ay ɔ^L/x.!w ]BpȻG>.qv+t]k|Smh.tv ]u;͌._r \%ǰ.4!q \L"X@.p6~0[Ƃ41 bPc(F.:1f7 V4b(,14ቧ J|bSJ"P1q#>`auÂ0 1|a ]³0 $a'wC^0lg |`%)(F0: `V(B01!_bt/~x_~H@/|_X/`!{ ^rE"//y}^Fb5E/x]uxE2.Ӂv ]B^кֈ.u> ]<7W~Fk3IMfg1 Fa21e,gc8FV2! lec&5wK2Ta d#xF2#@d2 @F@21zcCD:$1ġ% nlcsc֘|311ac CŴ-1` Ulbcz'1.u HbGcZF 0 ;a#!qP0/.av U0a !a}; 0Z! `cFb0%( X`*p/ _B/!~! _u /a| _(@$/l{e ^Ƃl`~Fk3V1lfc4(Ffz3'Q:|f;1`2 e#.)qHd[&2!Y pe+;YʣU2A e(MBhFO 2mak d%F*0H2<猌d`"&0]B2 aK c0Ǖ<1njrTc0F6 1a+ ec.ĠF/1nuXbǃ[(1;! JbWJ"P1 =atiH0n /a|C LZ`§F:0Q$a'wCF0q `c-IF J0Eт.`xF0Ќ,`!cuT4! *iUIOJxF4i1I8hCֆ4)!I h_l{`3΁t HgC;Pζu31 g/8ŨFo3sᛟ f58?i3E( ͼfm2~c3f#/}†^2⑗eC,dW2! eH#)L,FQb2! d#&r3jKT2KX d## ȏD~2 d8> 1Quc|7h11igc>C*PƠ4;i#Ncsfƛ64Q0iK]8ҷƕ4$&i0#HGXƐF4wA hՃF0S4IO\hzC(@И4 h#@S܆~3ꡟU Pgƒ=3'Fy:3 4ga:,`es,3QZߴf7>͗Fl3Z 4f4Ffz3'Q:f80( `l2 e΃-n(9Y2 lc{؍l6 l ~Lkc^5a 5x pk[ܐֲ5A ek-Xư5w ZjVT5K^ Oj~S^ԥ,5  D|j#PHF4򱧕9icMl;4A .8iqJUh҅Ɣ.4a "iG= 4d$ hD&45 hO#AP:F4 gC>x{3HgC;qۈ{F6qq xHG8ڱՎ3qcMbh8akuOjx.GYr:unV(T^:uC;AGOJ:oa{t,FJ6:E1)twpD:ht#C ?x9aϓyxsÝ@FG:29(nswcטG49Qʎcs7G/B;8lvc,`m^;^qըvC GhJ;5ٮvX1`c;vv}8]:!1huCh Xx:ߎu^aSfGS2:1ԁuc=齇M:f0`t(Ht"A|.G=|{Uv=rL{c Ӈ=Oh${A#Z1=]zGB=XŏS zcx@>Q}eG>AT}Z?Qd >}}=p>g8|cD* +\>?|m#H>||hL`?4߬~cUL?b~C Gj?;~^sq?`~Hަ>AR8}_j2Gْ@P䀇$ H@)H<$d !3@b$#G?`iH?ATRG?f@dt {@06ȁD 5 i! @-hkD VH*@$ DS BHz@pD$ /S @INmh AH42A cp Á/Ar Z `+AM2 iQX qH&"A' =G096 !!Bh@ d%m!+i 5IBE-ud#.!qDB'D !! @NA{dL as;Arr(D1! IHbJC `HD/!| ]B(Ld-$!i! )YNBod*!W) TlBܐą$(t!CCzC"I|d;f!1unC,1d9!qpC~"ݔ6! HlrCXІ4^!D|#xF"5 HDWR"DI""ID1! PB "QVȂD oS"ȥE()EGp;Q_",`E( >؉O"x9Dݲ&5xL"fAH2E/ly d]"iL`Eɢ.Mpt[n"qHEB-:g8YH"A&H2E,PBi#L:FB4Bg?#9ͶFb3e2#) F@b2HDtU#LG4"9LRdr#fH2G8u o#~HނH@@#HG?D$|#G=h$VY"HD$'d$E" PHzCՒĕ$%II*JIJJTO$$$%JP $'I>IOhyLd%^i*VnJU]D!% - mHK_RZ pK&3LɚfJ wp;L&6m6L&yzi^p/8, Y,j5x \~P?(! `SA)Nx(f K_`Z/  'ȐdH2n {T`Pm8H$|>q?0W+K%| >R``^p/8 K%`jP5( `@Ap"D Z-`Nx(f K_`Z.J A|Pa(1 ^Kڀm@8l6+@q 9$K%  ``Zp-8  `i 4 w`A"F Z-`Nx(D G]X-| _s_h18 cR@nh:(̀f@8xtI$ vP@ X0,D `j`50L&w`A"F}ZA- pM(d2 f3R0V+ Ovpa82\. lA s ce`5< @Ep" vrGX,d 2`j5xL&w`A"l6 Z-@M' 3I@pT+ of`5L &0 FcÀa2X<ހ~`E`"]GV+H N`j5x fA"F S)K'< F`U- M&>`l8H$D́Pc1L`,p8 D+@o9?K%| >xe^p/8, W`u@:/E$  ki4P*\. rϐg6B whl6@C &0 cX2X Ȁ~`Ep"]GX0, h4h( +~0? ~M9AJ@'  Z-ρg`a03d Smp:-`0`P (L`&0 ce`8xVI$ vWX0, ;j5xL&w@@@ !0j^/@Q,@  A gx5L `u7c@1 #L`'  xe`8x`K% ZX,p K%`j5x4 ?"d2 YEW.  `e4J σ`vhLtM& L`&0 ce`4`< @Ep" vgGW+ @Ǡd2X ^h{ =!x JADU- z@aH2< }frTl*6iM& l6@&0 cb2X<ǀ~`C@!\]9Q( ybP1(, Wwp; f -}> T+ _kp\0@ EAmD"T!AҡeL7f3`P (*L`,p8 +e`6 <@Ep" v]GX0, R@àd2X ^+@G@&` 3R௰W,x< }ׁk`H28 (oAЉD- {@ 0nxf3` @U@,P T+n`8xtG@# v^X0, K%q08IY,N(f K_`W+ k d5  ݅ԃH8js 4`p t:v; 0u:X,H |q9'K%| >p@`0x Z`B$ tt:`0Q*\. Y[`PY/  ,,@jx=`R )HX,qM& l6̀~?@! ca3  |q?0~a0.`Q( 󁠀@r9.,`I&x< 邍FpT*\. }ځm@@^1L \RA {}>q 0te2 l6ހ?(0 J+q3p `ǀ~`E@" vfGY, v~?!0sZ-`Nx( 3RWx-L փ`ƀc@3 fBA"HD0" 2@ @`HM& l6V@* e`8pz+.`L&H| >kp50E# ?\.Nx(f Mg\/ 1&A ix6b n@@x @ i6p nĀf3`\c1X`,p8 VG#\ 'a_0/8 w{= DM&(`pH8% RW-  ȐdH3 Empm0 2 xLi4`p r̀y@< cf8xX,1Rp)8 ip4 ^݀|>p zQA `a' AX _44 W+nA yV+FVV+A@5| ?,`0i8\.f@r; ߂ucj 5C! x-hH$ {}>dD2$Y!ĐL&PȠdP G£0p3h ȃŁE"A@Xo7@ L7N@'  c1@Y3@ K%| >C``^p/8, O'p8h 4AHW$<}c1[|.{=FiPܨh04F #KBH`U.1yQm,u*G}d@ P|(>Y BxJL%& B! _¯[TD*" or@`0l gPo74` 0n|p8@% 4q?0pW+.`M`&| >Ui`0f! F#Ca0sA քa=Ŏ=šb73?U*\k.|>SxL& ryCxÉaı8c _FAȡy@O~:o7_/Q(SvqS]DA }ƾqlS6NW! U&A@ # 5ad@7aNAlB!u:eײõaآ,Q I$SB)(K%zN ''N.PǦcI]g @ uR_)/Mͦc19!" vVe+-cL˦I$ǃ% W+?\q8q@8iЄh@P(,o7$@|F`*@ ve`8x\R ZiHC!ip8 Y,vj~Uh4J)КMD(]&TL&|Id b[u:НZE=b{tCveɞdDW{Q(g+iδ'|=phc4&J%܌E0P`jرlXLLO&&Fs#8 na0 YI$R%lb1O'ŠbC!P( AM!r !xpx@ J%@L,p8 c[2X<B"eg֋EUr*@ߠݐn>\rYlΆgC.ΥR˷y:ԝjPU*1z>N_'0Y8,Cz!B~͐f/Z] %9Nih:4 ;FCT Cϝ@v>@_ 3 FC#AF"W+L&j_L(Nsp6Pk 5 e2(B Qs_/8U\*rСPx}o7^ w>;f'3o7òY CԣjO7v|An Wp8i/4L f/ތi4D"8enT|Q(2M&Àa5 چu?U?DB! xV+nGI#I2$~aM0XzJI$ &L`&0 f3@L0]DTOܣ6Q#N äA G~QRh4h5bIN T*Rq^7npӈRhq4>^pxL.GA۩mJ#rPpX[+iѴs=OwV-OD &yI7T7*M/;i nj74z9myD=3 BX*v;LZB-"o̷ @g3Cv pr9@R)h,@O'ǜ}>H$bBsvZ)D Ԙ/L 1֘.[OE%"YtKوl¦!S'(bun#FZ%aH$Nf[pͻFݣق~vm5T|*LT&*I$ 8:upm!׻kݻ.˘e Rk@`Mr9co5֚bذvX1rIGTqi8|>x ̉$쒁@Mh"QvƓHt`M& )L`?`" >`v/$ u"m}>t^0R)LXei難ya`74KJNM'&T ~<{lLGr#H]0>h4B[!$ LJ)f\Pr6[Lٷl۸yP_(,_#` UC !t@FM &GbxrP9(].?kG5 ӄq8P΍F_mg_LJs^կjyšbаXh4S'CNa/6|^p77z r9 ҆N'9ʜc} G#ԏO!r.M5vxoy7RݹnC!Zu- Pǹf3` ( au:`Q ASQf4sZ5-PU*M&rYp 5@֠kP0H7TX# LF&4^SPw<2~`J%{Yky[-Sԁ>~ώ')y ֏DT;}|\>.Sw)\-tcM&vrж n'Mgg35A`Tv`30\&փkAd0^ǂfZuY^^RNgl+Cq!39wweDG#ӒIbD"{@ : iAl2mT&]}.ҞOC9ߵoں1`0{dݲn1i(8'A!ci* f\.[m7}qjPܨnOQv=A `/6ԊtйW+pj4's|K:~7a-Ėk7kdf3áWQVk5ɁzC`p4*n\".Hd`oV+i>qp c}GO'FǣcMэhƸ!wQs (>x{J!σgz)gU*WśEʢKq8Üg23* c\.|CoςʇeCaðk2Svzet !ri. ΉgLB,VT*eJyJ H$wXa06\Mn&h4LR&) @.NWy+~Ni1ɘVt+)Ք{뾇C7f].meԵjZzƽuƺOVPls|>C&Cӈdzm{Ap8E(}|>Mv;el2j5Mtr۴Vr9uQ>HMSrsQm6(s6c/|$>%`. e?bxm&6\.#0h[Zy]SΗgKHͤ"J|<^J W+e2 q:NBz=1ǘ㿀ldzMTJVLI/ta0se`0O[MΦ{p={[=G&xqI!7*ѕhVE' K%yFUTSVxDp (űt@6^?V*ɕd/nFW#O޵Z*``90>NPh4sN#C8ݍw9ōV!yb YkZ ?_/8Kٌt5˂U*PڤRN JT*z[d[,rT*EK I %wW1Td2)?A]QY}-X J e{u](w: h)4Cڢ`紳ZSA)'`NS p8H`y4XZ,+Kťe F!5PQZ?O#Y,\A j A r9dL)}: l6ZRnbKG .i(A|'R[i\D*L&NYL&p? ʼn>ԟiipn7\DYYjxQOZyΜ(F7gS:d&K2YLe-E.AmKjZזܶ.i[ T2%(G>R(D'2IJTXē$' 8KXP(ϝp4!6ALd#!F4p&Z[cG<厨uCG*9ώvD &8Ld]iOp #l`ц2 dC&0n5QoC(D'!Qʎ3nk݆wb&IF0֮w PƆ4.v 0YЭl,!a d["FZ6qucJU@,~ g=Ǜ<Pr$q&Y2TRǖ<5Qnt'CRʦUF5<'q;tKRD&4 P2a mþ.G<EU*t eS*܇{;P:e&0 BRu-mmۮpGCvfsG?dS"]DZ ?! HqV.wg#hD'e1)k^c c;\%.J7Q5)MmîrfINDqՆx3 mCjp@:ОD,` @Z.p$?Ў'^`ԇH:AX%.DR"ǖ'Y^ `Z:h&m3jrSV6!HrC޿hCF7"nCrP$0-n If0 PJJP`5ȓ*T 0axGF5ȞE, 8k_&A2:QқܪMRkz[9Ϗ|0*1QEYtX `3P RD@(GB:Ю4D*qS4a}GB:ő,^mckCBB*W]K'>VҺ~Bb,1aH\p(->.X7Ad+! |$%U@(j Pd$g=JГT)AJWrͶmw Ihs LAP`@8@*`/x `` |*P m\"Z iCJ LJbȆD9qːP$JT\B'8ODJ%,@RP4y-qkޗh;DF"0 L(F x3@  HF7B-n 4iAMlĮ&0|wC Hv_v esoxWjИMbkC2 (IN l\d @zD `@"Y  @Z0 ^戜DX6}Dr#@YȆ7OZ$9%( Q–p͞mX2 PNbr@*,Abp;( @(@$px@B `0~(! py$!! UG"A" t3)tg=?j.p)M^ZX&9o}5!HDB"a0L`@`h@@@@D &05d C5,f gHMh& 0itw# H"wUbw,erWrF1Nt|FAY @ `[@KX P ` ` Ox U' 8\`Db8E6}v#BJZb88OzDB%]*7QFn9qGG>9wezp ` 0@r.(@?A0H2AP+1Yd HKX$%/Y,tæ 9FmhL%q,dCe,X?iQd߰z GB: g@] n0(@  H@ PhAY܈PBxE5d{$b#= XǘƦ79D4 FzNRrtӨaC TZVž/~yc?YV¸A2yƊ4RHDe:Й|5Ď$v<1A&0-NړT`,g+] df[4n#g@6DRVR@g (!@!,D @r->A81 *V tc6JrSĞ%>)cK]F4Ast?ƌe$&M 5A_+s[83ў{kہ\ 7I}8yV2:Թ99x7w#HzHBB4(YJ[Rژ ]2Mjq>b ^DJ#hKBuӮ$*W4Y^25io|$e/n{t` (QE(1$3yϚ|ԜTB``@*KX 0QpZ݈XB{ث^.qsd]4=tc#*TO2y,qx cC(F7~yΟcC2D&4 Pz|-n+_sXx5ݮp7P;aFߖ'CΞux)L hM$ kKZvS%*W7YWb2Ŗ-5io|%;e/l{d O' %39ϒ|T*`4I H> ]]@" X `x10$)! |!@Jp.we#d F>1񐔄j#Q,dO~,ycHF7YΎzc iCJ4&4P~-n_Vznp+(WPS;FߖgCSʞU}l0ه|; hDz&7TbĆ$4Ѧ$q#AD'P&INWR0 lCbt{;϶i HR]4IJJV2~0 eOxVAv^BQCyc(FLf5&7U*X:жh@p#40 : `X X1 UB4C" pKE/~fr`?ȆDx#/~PT-!} d$|7Φ{# E(uC7d&6j"Q( pk_X6p{8ז p[=xGD')DZ"U,s{& 0R`3{! 0I~d.td#\G2=d$^") HNvܦ,)u bsφ6NZx(LBb,Qb 0iOĭek-9i^CXfc5-mn\w=+_G>2Ц?)R]l3mlފXmhhF `'A:%((aC W"xN6ts8E/D'QM hX<0ldG};}H2[ޝ-IjU̖d0e+IN8vަBo}&0"a(R Wb֮<58p3 e(@@`3v0!- hTPp3(|! ,Ib{ث\.!q dc#RG<(DK"Y#MRjth+AmKj`S`5auӮH@p%))MTBH`|[69ɞMwkD]}T nE/Re6ٶvҦadk#\˾^%-hGض! 7o xK\|4X$$D {@:`&0 X4XB4)q f0߇="Ć'!9 TF4QoúGC& B U2|/i|iH@:6i HEIRJ'?jhSBxCb0V)NSr ^ &6 @J8X"AFx4v,!a* PST2 yC!GkX8,g `c::}d*!QKX؞>)b]r똜y3ɳMrS>vO$ IiKJuS5!VbЌDbp01M hFB204 0a *AR PR` +\ _\CЖ8)p eC*݇DawC4A H\<)A MBj$! HBB@%8 RB. l`>Ȑ&4TR2A \jh?dy#.IrO~E,It! >oCz@<1| [BP*L `QŽ@(L `U0. XiH|;F$$N"Ŏ/QzdXGpl3a h`p.l `[. dC"0ц7A| F"U*AR {>!ȁ!1 D:Д&7 4Qp/| d#KX:h{D qȆG=ܖ'Ħ$1!F*PDB!lCbF"GG8'8 (QB0E.qs0a38qÎv10D&%>I&7 L"W$&4IPĮ&0 M~$*U Xb21 ip4Q}H AR $?!HGr;XĹ%7PbĦ%^* HB")!I dC,d]"HHE&8PbU*`KF4j#QG<Ĕ$)NK^X'D S"X}+iLJW%.tLBbD$'= P@*XJX".y>IO}(1DJ"QE*)VʶWbLe-uˮ_G*9S URp+`KYr˖.QzK`Ŕ,f 0YΖ-p]bf0)LBd E/Q|K_$&0LZdC"t˦z3Ѧ4k^G29f0F4)%(#AІ,!EZ0ц,[wn%ĸZ_k홳2^͙=gvØsby-5V\ sWʦTʱV+5fXK-eESʀP CԺSSbY3&%DcLi5DP=G25vKɅp)E(rR JIII)%mM<(U TzTڛ_kׂa,$2Aa "BR1F($$bXK 3'tTJS*CXk#]NF(HڻWwn;~O@H-$$"DHtBZKI7(ETW Yk/eػ:gMy8W ]kzBY"ĆZN9E(E޻ګTl7w<3~@h=8b?GU*ڢBT aE;+emI7yBXK2XNDD޻d3clp98Cw<P&DtRJYUWcxBAt~ҾWDD] 0&@8i15؛cloM8TS~OA$DPJR,sb6xA;"d}WN DGV g,ܻCc a1Hs.h 5fݜӚx?hY #4fҚuNeL|W+a6FwϱA9bl~XN9E#ZOIO)刱\ q.%̹`2&snsy=("TJ:jMJ=Gun a,њ6{w;|2ZNG(y@"DtSqT*DU ]+cLi4ޝ~OCH$ĢTJV+mmf ٛ`l=ǻ||!1N)i .+Eh_q4Fxb G9'%Xbf 7 њ]K%Del#b7V;{P:D(㼭>gPPc ax/кc a5` D\KB+Eh8g1>"ș"] 2ޞSʃPj7F1 ]uvoA5|]i)<Øap. XG"p`S at.p~,E4+`#dlAD0&DRf8'!D(kMjeL(e i8~%$.eˉq4FPj 1 pnGH)X{b8G 1F.x LIT*会@rA>(q$TQj0F͙hmmͼ $917iM0f ܛW̹ ) @"@\ Ca@(xO r5FdPrnVف0&ԝ#O(E U*E՚\lQ4f[ˬuh-"dR\I0ߛ DH  $@@ZI !l-!8^ >#RcLi6|ѺeLMI5&da8~F%ΙӦt͙=gT @H$pn@\ 9)8rR J AM)̹6q!=m ӺTYk0vYlm=G`L?:[9'%xP@  ( $@*@ |XK !d4F$LpWJ0֝s.Eq.(]lq4vϭA⼍bOF(%8pP `0fB(Z A8)E(cq2I1J)E0pN =h] DT+Eh&fli<917iM7mW@ 00P*A0!``xO r5Ƹ`vX 0/Eq.(U]L 16Mi8Ӵv9>E8"T3vnϙEh156u.zXyo/u{(e qӼ~BBW4a2bLK9g3|t:GO\T P, $@¸m A8-#wLIT*伙>rA>Hq#$dRJ_K;("WQj.#cٛ4Cp.mͻWj{}0A=aT7ȹ)4˜SvnϙEh2U:VfLɭ5SzQ?Q "TVJɱ6(5ڲVK)e.%ľbLFZvnA:T{A(% alH1&d̵FhB(E$Uʹ^k P  $VAK ax/0"DH\ !;Gi !d˜[y0&PsNn9Pj"DH$䜙"{OjII*RV [ x0Y :gMI6\+s='dІøwq26FʉQ1&$ߛ}z/LOXim)l- h6$|@m b:]K9U W =guοb4؛{onQ:\;|/X+ !Kq3H'$丗NiB]'_+嘳P|0F P5 !PCHTJV0xJIIH)d^ٍ1|ϛ#dqN);'+Ei %dbPJaL+`:[{0ə3?g]St޽g PuBHJY1'ƨ!d,^My<TjXnۋqu.2@\ A7 w$Ą0B(GH&$vQ1S*e_ 1fl͚Vo 9#"B~ҚhM >%$BWUjcLə4[ zoNI:V#{A! TDz/G$"dNI?tޛ:[uV;wg \kW P*@Ba !7%D8gA9"dd@I9/%䜔"SJiY+%"` 1tΚZoM9ǀ"Bȩ#zReL=("WKAh.uοb46snn1:GH]zϽd̃sBY#D|2FI9,%̙tPK)eTZYx/™#$hM 6ߝxO >H!d,QJgY6\+s<D0n Hitg@9-њ3vЊH?njd*^{πea5Cht@(,e;`HPL4&؛tOE(0BWh-̹bY3қChp:G3P*BxzbLI;(V%d arZhm90y/ -#Ԕ[14F#x2E)%$읔"Q !H)0V _+圳^ن0Dș0Rh !"Q 1F) !%4S*wN)E):UXel0Xel10Hi 8GCzCq#drnQ1T*^ф00C(g 4f՛ChoM8@øy9>h +Eh$kMi>U Vc`l Ѻ9 zBHI"^O J)E`r] 0,řC(gL4֛Sjo8@y!>P#Dhz_K7TTwq?!H8JII5&Wk-eS dL3fҚ\m7 vjY6 vQJ4FҺdL9(rS优Ccy3fӛ3fo9gXzY> Cȥ4fII)U*SjzOJ!D)U*cq3&`n8LzQ>Cȡ#$dQHI M)会RvN A 4frq!:ps|?!!4&P(E#x:RJIu.НP 1F)298g8yO)> tC$dGHE(ĸ:rNI?(TjXx= T Ch#bGHE(ĸ:qN)>T V+t{q?( &D#v2QJ)q.&d̜S|OC5&UJ[5"Ai "TJ;Gi#%fL8:RJyO*RUX+9g!4Q*0Fd[Ki3'PII):UJV`,Ř1F(dR\K3PII):UJV_ֺ\i#$RgL9'BRjyO*RW-ee]y/&ҜӚ~O%DE(UJZZZˉq.ھWuN@(TT UelV Y]k%Ļr_,ԺTJV+unYal.5ƻz_L""U\Ez[+r`I1::Z+qn.uμ_!16&*eL՚WJ^X d 3vnК`L50.e圴F&dŘvњ3RjMu~ZKUj}ݛclm7#}o!9'$圳t.ݐ>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>%>>>>>>>>>>>>>>>>>>>6Mŀ>>>>>>>>>>>>>>>>>>>f >>>>>>>>>>>>>>>>>>>=xF@S>{>>>>>>>>>>>>>>>>>{*ʀ>>>>>>>>>>>>>>>>>>>)JP$I>>>>>>>>>>>>>>>>>>>>sn{><>>>>>>>>>>>>>>>>>>f#>>>>>>>>>>>>>>>>>>>>.>>>>>>>>>>>>>><3LӬ(*D%ahZd<>=ORXDzip& BBc@P#){H%a< b[>>>>>>>>>>>>c !dA<hP (ZA< (  BX!(In1TU Ba@@P>>>>>>>>>>4'7 ( x@P(F@PP(2 (/hn@P (( @P  2 (>>>>>>>>>+hZ((@P@("2 x@Pda(02 @ xx @P >>>>>>>+*2 x@@<<xx( ("H@P8 (dx >>>>>>3&x  a( 2 @'iنax ( x@P>>>>>>0xA ( (@P @P(RԨ*N/( P(>>>3 #H<xF(P( 0"@Pİ7( >>>\% D @PZ((   @P(*@P @S>>N A@P( -I|_ bX   (((/c (( ( @P((   @PF( Zh@P ( #Hx(( F)Fx@P(  (( x2 <(((.ȯWիuдZ{-|־kr -aXl e\}Y>VUe2L^.S*ʟOگ)WV(.T*% EBR!)j@zaTMzryM<P (CF#MةTӿXPJX,KvIZo7̞9O19ʚiM4T=Bޡo~'dq~ dH $I%d }i4BN(T :ZaN0UU~xsa0 zk9EJ"t$h;@H$zikĵgy3DN(6 I$PԬVV+,V {za0q9YܬEïwI$hi5Z孭yռpYΌ7F0w#jA!}PdG0rG$tzԽjoa7P ԎFZXڭ‹z }a~1ʘLfZ3=3imKyͼq9^NF`dwԟjK%5o7xF#6I$^?4Ty*f d-rZKv%=Jo2ud 2YgQJ?!{$dK%֙1LjS%)UXD|V+cz b2u:s{KAk6g Ì].siNea&P(C!Co6s ÜzB,!H$bqSx[-󘿌_ƣQŀxq<8e245I&}>e2LLU,a0o%xnI`XŬW=i6 6~r 9\.[KR$u:}b 04v5 u:{ؼ8^0L Q(h}4E!"I9œlL&b1R F@߄oRu):M(Q(t:rZ@Y\. W+֥S ^c3Y- TGlX[.g VtnU7cpv}8C$HDY(sDya\/L=&"Tafy+ЕT^J%|sLa0Ұ]X.X-̥Scr2Yv-&SlM[W e2tj;].Wq].wl[-Q(`00;A!P(a0 Yi4,@x E'" …n ~g3G<CwIOnu7:P* U*pQgU\WӬ;V"F_fz=Me6jY,ްoX8Ugw3TvNs[L#U*R tC+ǍF!DP(rlK6&$qLr9gTH#D&I$HOܒIIHjU5,K^Bmv`Jģ&Sjdyi]_/L&{ 0~?@!oȔJr3=7Hrnd2h4Z`N("J9⚡MPT+'{dZ-˨bz@_0I Lv&;\ɐd2Y}qAҧiS6ModHAmSC~0q9Go7QѨuQݍnqcx<^j/OwE"/|p8 $ڇC"Qn(tw:I$JΦ7S|>xUQ*\T*CJVήMW&G#>e2ڡmP[\W+%e2Ýaα VC+Y,f3ޙMO=j m6qYXo,8 $W+^sM Qv Czq^u׺}Ak5fhTS*(@mE6ϑgǤc8 e228U*gV3+@ t:S IB:A!HQ6HHҋ{k5ȞO(K%Cu!Nd*U+Dբjre~Z. WU֪z_OO'iɺd3Yfs3B\iq5BZ-s&e5Tm7W۫Y¬Dq9Jܥnpg83uVNq{xV+-ȴdZ2b k>}>h5 caaom7[&󓁽^q/9c'1 tsji4xh_/g3U*~ F#-!xa0cQ`!u@ HHroTتl\.L̀[ql6r9d͇ix<@&H@J-GiEiWb1JM'S*bIާSYem2`c P (;REBxѼi$Q(̗K̦WitAƣQ)ڊMY@pW,FKNRio4B]!/4K]Jb%1֌H-ͭfֳ"V+QUuk:#[ rNޢoQ8&N(sLs&: P ׄ~v;]/KQ(Mz&y޼w5}LڗKGLXs8CI$`H}Cw!<dRxV@\s.9݌no#7 n7cN1QCL&|> h/dE D٣=ўT*M&KUt: D"?ʢQq)wʌ%FaW0lW6,MV&?UڑmHB\.{k}UaqX@)f`mF#MŦՖj5Z-o n }>zr9\ꮒI[v]]ίG#9zy=L^/l7U|۟m'X`,%02XRYe,,K&qf3sMIܓnIht:SOƂEA"gPGj0pmGUi\Ԯjc1ЛM E"IS*UEl" `Y~l 5šus^&f3Q5cc1̽^8F^h/4ZQK[Elq]{pnXJ%[ Zt-:]E._s^gwC!+Ǖѥ {=^GɣvA~ p߸o'q%`r$R)I @ U d Gr#I~$rt9D`T*r9tX`a00ufp8XԬm6՜B!gFC!n{=d ! GD#quG#ޕ J S)يxR+*G$R) Fd2].zM='\ӮjQF0S*A ^ʺa]0LX,iŴrf]/ k q8Ńb Qc1ΫgU9Z0֘kTZN*'bbH1 X,H`$0u :W1N 'I% B!Q(V+[R)e{,a0ڙ,̖f|#>Xլm6HN$'DC!z=SxϼhJcCQڔmJB!tI饵ډs-9OۨSW+ڡPUuUZfpZ K׊z޿_0JlK%-[dth 7؛O''#‘PHV"+ u_/VX,Akc5gc1w;ƞVg܃BȄB!Qj51MH&keTeM2:N'ɓ e2MIS*UM*Ui*E_yYh4PO('A B!M T*ZEri\m.6 af3?RM)&#`q85Κ}SH}> 8H? D@HK%J6it:zq=8uPp8zSV?Eѕhʶ[{.`0 |S)וPqXX,Wh+2Eq^J/% -#^g3Gcgdr9DUΪ烃J~? A ? ԉD#4њh~bJ%dJѦSD"xVIF%YlA6 N'6e2uL:TdnjpalDY-qָqk[]-˂eвYxD_j/7'\ȮfE3" FTadٲo-7k5BáaXyo< ^oB7hP3G$RP)TT*`Y0,MigӳҌ5FRު JXb[P8X,VY%B|ܾn`0;!ɍpƸd2pL&AɨZl16G #˔uE:I?PF(M&A LE&#C~JI%$EJ u:&P(ZJW+MP&UYS~adc 1 zM&gB3-MCR\.m6g!S~v;@4GQrG@#ZHV+Z 1EV#W$J%t_Jiue:P(TJTU**iOjU5+=՞|l6h4No7ƱsXn)7M'sΘLu:G|~?^ I$9u :DN"fmAe IzdIBk55^N(J(rJDS*Fjuepظm63ts9НWuc0{l=fGm6BB!J/EFG#$3R5$W+dL2&^i$5@QKDJyB(JF?Qܩ4O)'@K &/Sيv;l.P(UJR%)eO2UGNӧi|w;N'gF~q?8 &R CO"3Q$X5AG$~?iEZ-j(L&q⁕@ʢQm)OTz=b%TxG$+T*XK&iS4ya:QB"DH "vdSЊzbR,JE@]벛Sp I\-`h a3fzi2m&M8`< v$A(`vn6PbL@ d!N)ЦZChb4Ɣ?Ƈ#D蘝"$WsR.jFHÉ#3j_ma-'DXipm/E(.7fF6&ӄrN;sTPs@{(!?EGt tPW A(? .E1.%zDhע:MډQO)E΢dVѤ4F۽vh i5)e ,XZ 9{1pF.-Pdj 8GhëuB<\#}y@|H ОC aM!ɤA<'"z$ODx"\ц0FrA#aq !Q2:G(#V~J>*JEIHRj]LBшZ3 a\ӨujDmH 95xOW>cglW`P>A6 10&Dht"NQ#_+Eh##jBѽ7GGI4}RJ@H] bhMJL I+x<s a ̰4<ڟSpN{al;XkPsܺ{Oa?\` 8Bh^mRTBzOQ,%E/H"a"3FHx4>HiM!04CR7FI)#d}sr"ZDKI?''YֺZL2fLҐRjmW :.Eأz9?d' 1$ЂB\BTCc1&7ENH/cѪ5XGH#|(b@Ha) %"$TґRJI]Ii-1&$:YH#-de*%B|Rϋcr/NeGh⍫yo8=sx4a=>Gh$|"BhY-)4Cڈ{Q7&Ep٢4g6ѹ71GNh#ؤBHi#$|ēR_KI7'ۤtҔZRZS cUv(B y#O'\V\^!44憜ڷVFqεֲGi <ǐCthn8'QH)pb. Fb]ww8j?MHUi "$\$:'RYK;I6'ҟSJWU+%y$PJZKp.ĥؑ!J$)E/mw >i'R_L⡜T6宜Ӽwp?~Gߠ{$PעDWȊ"_5G@(#zR.EI" $A%v҉:Q'JoM*ƥXԯ*YeKbl]._%rRr_L i 1#$tęxVN_@ 7HQ%Hd ƕ,]9g6Fը#xϣyo@a 1 4Q*#E[Hi#$o2QZՂ:H"$Xd’xRnMJ)E9*=G~jYKp/4X zaLsim2P>ZgM!=5&$֞blRMQ6ttpN$Ę<.%格m#i "ULTي6&ӄfl|T0B(YEĢ8\7 GӨu"{OtwNJbLY+MisRj\K|0&+JeiL 4P8[k`M6ۦtݨ~oN98ԧ SsPNvi:@옝v !dFv.q'`٣hmNq?G) k-tGQ1Gn!=RBPJU,%ĺVJB`HLe 2&XtMriMmi6⛜S~oN9 t(sNq:'[$Sw8Nߵ,ŐrBY\U &B.e6jT\v!r$.Fԝ פĒؒRKicm.T~+eM )e54چPy:o'NI9B(T羜ӨuNi;'ttZy O;q=F'vR{O>Дa~O)%?~hPA8TP{pC7fqܔ?O a@3u? JiPGܨh}rQ>G(E%⤼TڒۣTj{8$#`tnM1{ظdJ̉W[4f`U`b USbH"UJIT O)<ΧzoS*reM᩼56HS7S {O\UGR*T[ aQ=*'DD2T3*ePB*EUTqP-@R TjSj~O)E>T !SJ{]+qخ;VjmZX5i"$UVU ʡX+a֬:Ձ*%UjWRU\ jڭ[UV uVUW"UL T誝RSUFҨU jmS*~O8 T*eS*cKs+pTjMR]KH! !T{JiQ*9FJ TXJ QB(UDU T: AP*EBWe zB$D{^Pqn9ͯWuΡCaf,,}ïu)2&[tk+euP*^XKb,@EW*E^uyD(^WEj\5prVJIZ+RhcVSuXb.EՃjmUW\諝nzUJVXX:U\ʫUd UKuTFMZU&JTdP?rTjmS*sM4ʦT*R*_EKDh*ҥZTjMRf*LI""BHTj-Q*p3LtɎsaC(y/^גB]̫uf$\95oL):[`=j^ʴVZ@gO,=[|Y@(dN,Ŏ/X=a,:ń |X _+|zWj]뫽uvUں;W.\`+p UPVjڭ[`jWV*ХY+9EfՔ:VA9X+b:GU"VjW*^xUҊQW[vlUVi*& 7_tK{/xEX]ktӮuX \*Qon-Ź,%[[ aYk+eu֕ҼZ!KD)gгzc~Ya,9d߬97X+ebuNX99`q,%ʯU"W^y#U W^]#+es aƲW*[+x%mĭrNVֽZ+Si8'#VtΕY+3%efՒ@V:JIX+Eb@UrV W*^xj Uj/EH~Р\~97=X]mm I5i&sY0:d*̅Yd1L2XO `\0 IJ^xƯܴZY]K)r.]ƚW[znEȵ춝ؘZ֫Zj~Oզ֍ѵZ@g_ʳYV`*YY+1d,9vîO; `,ŀVr^ؽyk/-eߠf]D˨s.y%LI\QoB-E !V:ڧ[Kcik-q]ZLIhu-q;Y+3e٬5K:gY!csh)j-Xa,5ܰ X_~V_W ^zUxtܪW\](szoUJ)W}\ +eo$䕸rVҪU[ aukHiZ+VZ%+Dgά՛ʳyVbYY\+aI)3#uվz=Kqo-7#vٮ\jCHqq4&;8gf$ę6Gc a13&ÛO; `0z^ܱz@2FWrU]tˮt.E͎$\Lˉp+uҷV܀[Xkk l-eԵږZkQMiw-.`ց:Yk;fެՙ#Z[YI)9dUP 97XbU5ڱVZËXWk `2FVSWjM_o+`+nnѴI6יڧTi0=vxk4eLi2&DȂ;b]Z1+F8ÇOk `0"&ڲV_kMzR䄼|]˴YuO.м0\5q. EL BH[kr-mo-zOV׷Z+XjhMδ֐Z+Ceg,䳜j"DY0e,E.H6b5.}ϟ{qȻc~o"[Krn]ŕ8(tmGMN ֮wNh apY1es a2^@{obZY!xOk `E(p*_!K){a:]Kiu.ӆp<\ !q.:blV[KxnK-e趽ˢtZ_kDhtNZTJh-zSum--屜Xpnͺڷ[V׬cbk8g5|okBgsLiTV^fK)}d 4ƗXb5F0Fy2]`] j-WR^k-z3Fu|{}] !uu=7\Uq.3%`[+xnMɵ *٥nV*C(_q !zrNY:뀽nڝv߃[j~OѦ64ƊT>g ]@3(Y4Y=S'dSLi2]cLb'Dư~4`k q0WR^߱zU5ׄ] v .ԌBjM\KrVJʸW C{q]ټ7uw97+\kXӋp&ջ7z@ۨ0[f kwycVӹ@cH h0k:gYK0 e] 2^BUSc b\1K!=k aa@l( ZkVRX_+}/d,5^}Ϲx/krn]m뭽t.r_~ Y#DwbSJxڧTTLꉜclrT}ō8Fh+xmQ6z&X«XUj$DDR Y>"g ᵙ1V[{I+)%d 7[ bLPZ VPP `,Ł0?fXKr_||u ^S}x=/~k zoR䯼|L[ku.K9g)x/YS*pB.E7f;Hil/-5ZfLhՍb VvX5f9 2fNߙ#9cL|)1&.Xb A0ƒ9C(`90E࿼"_B+E{}`x/cFhSGhPM vqF(١;4'e,ŕƲO#d,Cv2HIbZKUB]Qu!+:Eg7ܛsqj-Y˸9vۦstm-ųr6ՋZjSYvZ%kDg ᝋ3fl͟s2.e2LɑY"c |14&KbQ J!U;JX*]tsglɞ93#oݷ[ۺCh}lIM)$5Ć0զ{Qiy/5flփw=QgL)MI`d{,e !2SCrnX[kcfll͌wU]sY.k%}9/\"cLo @7hM3ils͎yݖh Rim6X4[?kgb Aele̳Yw.efՓ2yHV  u<4Kqq.5J8IFt[[w+nqb6FՇډSQ*i1|#?$gd,셛qf}[/eX: 3,eqw.™S6߳[xnS-el6͆Έakr5zԱq#N$i3&uTvFHS= g&LɚWvcuc,s'd5&,[o-7mmm q1VׂȃYjQ]V֚8Gh^ ٟ|3vX7{s~ap9$`pݿ7y+tEm-Ŵ6FpK_IkP j5]TZgLi-#E\4K5{=s.|eO9$`p.%ޞ[{tmM66X[`kzoQsvԷtcNiM):gVУ\s a9"X 2\p7jm[p~mk a76fjmZC]kbq D:Z_Ki ]'906ܑs.qn1MQj7B[{ṋٕؔ;g{lZmM&5ƳzڭUj/E4ا;}+rCHa78  roM𩻇p܁im0mm ?ޚS[*j \y5/';crBnHMGX{oѻ7zܳpcn m-vN$$\ckaΦ9/.ܐq2ï8udk}-o -e79۸S jalհڶVc+r]9 3=+pSN i7;[StJm 6&ټbsr˧t$gp|8/Rߊ[y|nӑ960?Kgr\ !7BAK)pj UKvނ۬{un%Ĺw6є2rCHyƗoYG7f݌ۏKqm9!VD눝pķF۶kvnTʑƻ8g7#bo7l㍜q"$UZKW|o-Į ?Coy8G V 0q8'иZ |uBhWj_.|BhWJI_95|RJW_5k|U?W*_E|믝u_J)_km|A5_5k|UAz_ ]}Cu^κ_1k-|56_k{}μׁ*%_L}nm__ |WƾZ ^k{u=W^x&;_>ךQ^b+Eya,5W5^>kxدƼX׉ !^kíxT μ _U+}(~W _)+%|x2&W A^+ߥ{ίyWj^ҫU{#du|W^]zOb,W*e^]ykxүUƼX׉ !^}xN ඼`M_~zo_b]}_ξz_6|Az_ ]{N׺zO^k {Uj즽װ:^ֽzVBW^ѽzB5疼כ}^gkyw.Fד a^BkMx aWC^ +x_ WP~*U_hk }u&;_μׂ*E^ +ExW_t}꾽W_H|*eW?_|Gn ׾^{r½WJ^kz^UcW A^+ӥzMI螽נ ^vy6u6זz^P+y#|׍^-Žxᾼ7ׅJ^ x"UJ WQ^~UWy_d+}d⾜W_8+|&D_ k|f׻*e^5{ZkU캽W^+zV*EW:^kzB5窼WJ^gky}/ZW a^C}xc׊jM^!k-xd !ׂ*E^x2W a_+%}دW _U+}(~W̪_'|su# A^+ߥ{ȯyrW ^+%{cʽyW^+zyO5N)עzO^+yگ;Uμט^Y=yF(~׏^6+xZKWZ^+¥x3uJ W^+ewj W_p}W}_D|毜]W_|6*W^ݕ{p킽WJi^+Ez\Uڽ[W^z=GZ W ^rkMy4uוj^Mkɭy!㺼wWz^)5xᒼ2W:^x޻]k~ 5vc_`+}TW_4+|kxblWz^-ŽxZKWZ ^kíxd R*W:g^xU]5w֮ߚ_skm}5־:_E|믝u_J)_+E|kxJiW*^,kōx5BHW ^}x_ :'W}^ kmxJ W]kw5BW|Z]kwfװ^+z\UFh׫w^k-zR?צ^+zBHU貽W9^5y>~ל^nky5JW*^[+ey\+.דZk^Ik-y"W^7kxͯ^WY^&xu=W^kxI 5WQ^xW~]wˮuWz_]wWy_d+}d⾜W_8+|&D_ k|f׺[^k {ZkUW]^k{`uʽyW^5zVꂽPW^+ӥzcLu6&ףzo^=zB5W^u+Υyį8μי;^ckmyw.垼וj^P+y0&~א^>kxblWz^,kōx5BHW ^}x_ :'W}^ x"UJ W]kw5ZW|Z]kwUfx]߫{el>׳s^{bu~׮z^k֍zWuꮽUשZ+^znMb,W*^ѕzC.ןZ^yk-yϯ9WQ^f+y0UʼW^R]y;'u䪼WJ)^A+%xu㎼q׌^/+xnM׈^xj Uf,ׄ^+x(v׀]+wn} ]+wޒWyJ)]wxJWjM_^}N􊾑Wj_2U|ξ9z_{}"W9^5{pnjWJ^˫u{c"W*^+z̯Yڽ[W A^UzyO5鎽1פ^kz'DZ W ^|+υyگ;UW:g^hy1זz^UʵyF(¼W?^C}x㢼tW^1=x⚼SWj-^!k-xuᒼ2WJ^x3uW*%^=wߚW}j]k-wUުWy?]㫼uw~λ׻{^={r5׶Z^}{?g즽ױ:'^kz\^k׬^]zRBW^+zHIʽWC^]y?u疼םJ^oy5bW^\˕ya,5Fדw^JUy#U&׏:^8xүU aWc^(k x@W^xN W[^kx W]k wЮW{Zk]+%w5&w]kmw]Wy_d+}d⾜W_8+|&D*_ | UzW^ݕ{sW ^+{JiUҽW=^+Ez^U늽qW^յzT>Gק^kMzSJuעY^еz@¼W^rkMy75掼י*%^_kyl-rWj^Mkɭy%$>׏^;uxݯ6f׋y^)5xBׇZ^+%xT !׃:g^ +Exz]5wۮu.{u]}w:Wx:]뻝wh^z_k |+uV ׿^kM{ӯzu׺zO^k܍{p5׵Z^{)e5Nװ:^kmzׯZ`׫ a^zPҽ:W^+Ez7F膽ס#^~y=URWJ^kuy3"Wz^Y=yQ*5W a^Fy !5|׎^4ƕx¯UƼX׊C^$+ąxᾼ7ׅ^ux>඼ׁ;^x5ƻ~]wŮֻzU]kw5Wv]ثwRU݊_k+e}RWҊQ_?+|ЯRW_+|bU2&Wjm_+| UҽW^+E{wW^ݫ۵{`l*WJi^{a|W*^k֍zWuꖽRר^ziM5N)פ*^+ezCWZ^wyϯ9ךQ^d̕y0U岼W^R]y5&䖼בJ)^A+%xuvn׌^/+xnM׈^xj Uf,Z_+}|5A_|A5Z+^{}5f׼:^km{r׶^kڭ{Dh캽W3^z]rnWj^+ՅzT*EWJ^zMI޽עzO^kЍz@窼W^q+%y6uvט^]˽yg,ZW ^L+Ʌy#>׏^:+E} Wj-_ZU}>JW_95|Я]WJI_#u|x1W_+|&BW^+%{ίyW*E^+e{vn햽״^k{#du:W^=zѯZ5]תW^kԭzO麽7W^z2FUrW^}kϭy<>כ}^j+Ey3 W*^Xk yK)u;_A=|5nm̪_-+|&D:_k|Gƾ7_k-{}f׼:^km{rW ^֫{Dh캽W=^+Ez^UrnWj^յzT*EW^zSJuעzO^еz@¼W^rkMy6u掼ט^_k}ݯƾZ _Vk}-^_:]|կ_ʪU_%+|~4W*_+|&BW^+%{ίy׺zO^k܍{{ou햽״^k{)e5Nׯ^=zׯZ`תW^zP麽7W^+Ez2FU膽נ^~y~ל_[}}Y5[_E}5uZ_/|URJW_+E|RU ºW_k{尿׽:^k{tF׷^]{UjײS^؝{`u붽v׭z^5zVjMWJ ^+ӥz^K6&ףzo^k zB5_N+}"U2Wj_8+|ʯUҾZWJI_#u|x1W_ | *WJ^{ȯyʽW*E^+e{vn킽W^+٥{#du:W^zѯZ5]תZK^kԭzO馽4ץ^z,ErW[_Gk}ƾxͺ_1k-|fL:_km|W ºW_k{尿׽:^{u^׸^]{UjW]^k{`uʽyW^5zVꂽPW^+ӥzcLu6&ף{^=}U~Wj_4+|URW_+|bU2&Wjm_+| UWJ^u{wW^ݫ۵{el>׳s^{a~׮z^k֍zX5ꮽUשZ+^+%znMb,W_׳^kM{bu~׮^ֽzX5½XW5^+%ztN*eWJi_'|5ξ9_=|15n Z ^}{ޯ{"W*e^+{p½W ^ҫU{4fzW^+z\U2fWw^k-zQ?ʪU_%+|~4W*_+|&V ׿^kM{ӯzu׺zO^k܍{p5׵Z^{)e5fװ:^kmzܯ[cW a^+|&D:_|LھW A_U{~zW^ݕ{tU2Wj^+{JiU潜ײ:G^ëuz_랽s׭^zUufL:_km|W ºW_k{尿׽^{u^׸^]{ZkUW]^k{aʽyW^+ezV׳s^{bu~_ ]|׾^k{ïxu׹z/^5{pnjWJ^+٥{c"Wjm_ 5|WJ^u{w׹#^{elV׳^kM{buƾ7_k-{}f׼^ݕ{sWj^+{JiUҽW:G_ 5|׾^ޝ{w׹z/^k {kmuV״J^˫u| *WJ^{ȯyW*E^+e{vn햽״^k{尿׽^{u^׸j ^+ۅ{ZkUWJi^{|:Wq^={r5׶Z^kڭ{?gf׼^ݕ{sWj^+{JiU׺zO^ܽ{p5׵Z^ݕ{tU2Wj^k-{Oi^׸j ^+ۅ{ZkUF׷^k-{pڽW4, *ُc+ۺPe, 25h- qBxx*""Hg,p8" @)0 DLs縜0 ʐ+O➞' b31CP*J,c ;­pH\(͘3f"mb<$Չ=c* ʟ0T~ް7+o0' Yp<#臀z o &1 m-, 'O WuU`#uE ( 4 ` tp37CGCh1>b Oox+d  p%v ]. n ۡh0y 4 ְi5"an[o[AAxPC=AG]@|c0RTԕnhZ b@tWƀ;CxX83 z^+؂ @*$Չ=bh臚:aqrz2!n`[0녌:GQ&6Ft5 z<;| 6 h`4 #Ġ@-P bp' E8QN@"% lz=x-فv`k(V3_7Mf@T8,<@JLB껐yr:JC^8צN1IeRb־){g ^]@`"t- a}'ihpDڅ](WJ=A&+I;>mj ^5W [v-ݤ 2'0 9kNZ"f<wD2h/bVU[>ZVO8i+`EH|jB+׳@8-c>ϼ/Mh⮔&D"1bX8ʆr}x^ g7Y7`MXQrTa` Ae@6( )&`.kD ps' +xJ !u@d8? w`W,DGKMfa*JAXGŘf 7 $>xyc1"} HB@*% b/ 8Ni"Aٰcl|5dMY$ H8 z@<ސ c0 @+' 3C~0L)cglٿvoT,6N2E3xL@ 8S-?O- f@P $F 5:b+@0L vG iZ4EarX\P`,M)hCl#c|24R ( &b@\4 !HaPD!$ԈI5"Ճu`7p SZր$x+ hp"?I@l((J=V( ͆3aDTPF5 zf@) ~M@ p\nƀPT#?7x*{ 6ͺine"YHZ( 7= 1E(* sG{`5M`Ys\1<`@$jfڙ-̀ s ^.Fc>r0܌櫀9 {&`#6hĦ1&_I9Ď DH~ nFGeę~"H1gD}aGGg'EPDTdDDCQ C{D5s BL(1LB/D*@fAYVfr܅t9{S7 >Ù2=b0r[,QGT| ==OO`h'ˁ]#HqBP5`@ Xs9x^E3 cHX"Uq&dIĆs$G6UDcQyE^w%0IL1lCn4 tB#1$AW2D̑|@_+5D Q9@Nm80?70?.,A>yT(pL==p0A8dSAT<|;9CP=l>[qfp=}qQiZp7t= 7C< UT45 h-Z p03L1 |C6_C:3ΐ*.iZ_Ŗy*l ]9WNbxOS!HR0 ALSv9ݎpIla@db"w('AzЅ,!`-0l$ [ TCBUЙ&c u0(33 (Š,p)1 Z – <)O ¡0 *rC «03+ րB+A ¾yT,0 OS$, 4/ύ x0-!uB#y"},B7K<$/ '3IS% gBYИH&+ h`& :Bv' ‚p$( :Ž<)A c˜𧒌) £b*# ¬pU\+W ZµXT+ B[T, *, .zB˞,i G'Bе/L-K ^װp- t7B з-{ &p.D 浰G.cq(J( JQ0.L ,u8 h.r qCR1Ԍp7Mdzq~}Arh, ;m@zPJ=ZV0_$H0oلa9k0vAP}<CAЂ S|!(06# 9ppl$ [ %BICB% b5XpЄ%! Bf;P*&ʵ Gr/L' ~pD(@ &B( N>“*) tB7q*o FB4* u®pw+ mµ[p6+ R¼T+ \,W5 !BfL,S 7Bд-- s L!0s-\L|0bgoAPjAqdTYIq0w}. }STAЂJ4 MhBZ!$$0W"Ԗ5%;D# BDPd$ KBR됖V% ~_}&_k bkN' ܄w!0' IRp(j .‹)1 RBP); qBi * Bd*z ªp*! hB4+M! (B ,+ ½,݄)wa "ȷWp,6 Nwkpk˹ZȻ+.hOiЌt#upp/T 3<0v3\AW{b؅00X\ 'B Є,!- nB|"7B+}Ќ#09_$ H0 $ S0T0%- e`p&h uBkPN' LvS0') BqСD(@  ‡pD( @LB<)A ^V—ռ)o {NBӐ* BC* «p( (@P5q \ss@\$\A9PJLAOiXdVAorܡ$7 t$<wAy~eK~ BЃ OBP !?B#"ǷB1Ў(#'IB?P`$X% -Khe %C ]BWzЗ6%́ aT&p bk ' Btr@|' B}РT(' a…Xp5D(Q 3lB)1 OQBP)pĭ|=[j/ S%Ӄ]4Mrw-n8$o/6ͱ2NӯC=P&ɬ΃`?4OC8@8 4 ';sNK! C0(< -9p4pL 6荰?*ʹAA@H$#ΟA3QEQoQAT`X:LJq_3AfeE0lKA.q$nCT0vtA%{?cBz aX"@|!$?"ő1dp#}GB=$>e &!BIP<$ OS0#\% vB]|,& fİ& goY$':I ݗwe' ~pt(* †&oKū?jo|Y?P:5erD"l. @%09UlU[p0B-p5A PK_QA=PS<Omh[Z0Z05زAv,`L5;fٰfAl:qAP<90v'1z^A~9/p[\ BBPT!~yeBYPCt"P +pY#G,B7K#; Bd$m /K0 d% UUUUpy4%M yB^t&+ Bf& /n|')X3+?ĬH+, 5r|-uin]WzuDSc#9qq+\J9+NJﰾ/ԁ> Oɍ{c^F?QK?3m?5%m'o00x@^&~i<blLS#j-xP^*"}Ȯ9#uo3:ȇ!<y/oH[/h6=ψq+Jh`Z/C]okihкC.ho*ʼ?tƿ챰H @$7PXk@V\,  @*&' @x/ G08*q3@1JAHZa/pO1e'IUdd`Ab[vݿAyd`=9APpf<0j4ћAfo-uEAőPsgLVթ0w\pz^A~3 <0H R-,% p*! Y5BMP!!v"]*p"=_B4WЎ9#qB=vN#8»ppBļ71/$1 rW0CPMoos~f?Y4(_[A??o @9Ɓ1p֔up\p @%Y V{H0-4 {R԰5Ǥ qhp=E [ACrZAJLtA5Cˈ::Np<5 B2DoL;Ԟ5'u]_06 ar@CÄaM8SNWĤW-9Nѫj[Vx|ӽ%t𐗼$%S0{o^S׻C=z\CޗmRTx"~'>OߓN?_WY?V}3+LI6M댳,#(~?_~'[lEQFHRއw"fm9@NP*ٌ c °2 g4-,kK/m5[k _fܶ-#s-37L9kNZeeUv]-UUn󣡼l:;E|;_휋g"k-kἚo7v`=VHՒ8B=qi*=vO~QؾTv/#^>W[{Ǝ+??4#7vS>]ϡKaR>:6 H ?4Ԍ#.o*K>?ϏNӀV?ՠ=e @k,~ @+O>S]mGՋQba5XMKdReu6]MVvߚ7樎鋆:bn-8ۋNSÔ09o[:N{o4[˸fO;OnF[ѮIRnaKp=yC}PR>>gw[>Vϵ*J޾wv[]?46 ly29?O]lA C/ljɲZln/[8P3 gBY*T Up5M/ fϬ-o3w[G7c{ 8NS &9vN|l@:tPӬh;!܃+ /ScT!nH["Q#ȔHeYo?$6E/Z֭vmcrSܭVq+J%۾IvjTio3e>ϾId^?,sKDQ /[?mD34CumJFӾ__pW6 +hJ0; xƸ;モ8RC溉9NC:iPί볫G.c#þb<2Y[V-֏.K⯾x:jrܦSw5mp@^wH^R֛'5͊8bڞm[r:kn/(8{9LˎS2*YJnjkZ: B(.ѓ4d8nNùhplĻ1/(6h>s%F>Q2Ll45M3s\*6JͰl:879kzjŸn {=^9.RF9w>YVE⻪xNS%N< SZVr<-ȋr"<G8=N$O_h=Ct![VS>Eq|`&G>Ϫ_;@wP-|s_+٧#iXm4?7O {xո%n"ӈG8QRC9?+7:Mxé0n̍#hJvc|Fѯ .h,:$P8hct܌6# جcv+/X7 Ӄ tM8d<%|59MEhÚ:0⦹s|Sn˭rH;%۶}c< PcŔ;o0&`ʶH++ <۽O6^׮or٫ܶj=}c65{Mj!:7ݑwd`lu#Ho8=j&8L9Nhכ5inxˤ25:AεSAe;D־㵯e黙zn{j=<~}&m<{O+c~<ߢC:oYo[8?/m|[_l=9ͯ^ kׂܐ7$2xߪ7ꮎ4<޸7>돿@9P8Nhk1ů9kΏCˣ׺5+ ;;;Ә4;#u<y j|ϯ'?Th;ryܭmf79yL7Å=0!8N@#ÐN9ShC-PFnˣu:N.l;ks:\;N Sqm[f# |żo:Asΐ\P=4OۻJ6ҍӂtޫa7Ni:y#^H,49NUekYZy^n{jڦ 5.Z+.;9Q+TJ;!DekoĴ<|O*ʨnaz9nahՊub`n7˳ (j.C)`=3=/FѬl<=1|4ѕtem[|!!n[7 S8TDL o[i B'9΋#Mpn廪n1nST탈`.sy0;,ȓ$߼0781<|xrmd Lӂ4`8|2ی6Y#9HWcz󞯼谥:,)NUl dn6.:T;U5FQ&yߧ7b{7 4+&manX48柶zsG*vκ3ˮ2e;D˓'I tD/@ޣ޷}0En$>f٪H@ے69f6jQCP稷9-Ί>雛:fΨiFnOۓĒ۱$G޹jsf˽ٲ>7dC~P߮&Kモ8In9j滤y3|LI`Ƅ޸7:+9,GN[n[ۖ+Jzsz'ޮsifnX*~.skm}~M SnȻr.F;ǥ91N\^ӹ{˞2B'c^:γhS =(ߖW ;PqT.-:Nܮ+MѣthulvqnlyF:^QBKP%ZV$k47QKRM|Sn/qcXnO˓9yBmDD綩nls:\N%cnMdIEn!+hJX.A%;IN9Aw_RԀ˹|F:&N ?:f@8,3ی, QCmPJnybĜ9'+q my:[^N#HmV[U&RˉYʸrD/Ka2Lf9َ|'U:'UN㥤87}STan.TRÕ#B9Ўo|9_惢y.G[X{VcWn}m[f8ISTl9[7d% |n}ԛu&䪭9*NX6c Rw9qy@m91NYI RBa|_.M˓xjn[3 $2 ,BF@*PW@H2BPz[PC2C=<> D1+M99sH|J*HR@b8*`-gI]y! HPLQ[*G-2Y]Aʍ`{kFir; H*U"^!m|}9jIF(3CQx\0X@^1zB!RUio_A z`Q RYj}9$ zCVPQܻ97PL_`ci(o2w BZe*qI1kx} zqA2JsBs9"6Dp;1i+%0 I gh[wi^1W"~i$B| li "sPm j9$XHzZ ުQ:8Hc"Y [!s(A=7 A9 S"yCFcbqY bp- EZnx1jrzX iq~xO7 xRx ZR) z٠+x1a8*;' 4IA9JV9Ga i5*j +ZQya.ezt9tbpn 6>*nji"dQ1ki'ҩ b ! "hCcI 35S43: 621C?[yD8x $JOI@: $oh:/"yA7ڬXnQ/ڔHr@군RJ:Xk)KpsS#YA[') 3,#+ 9#QjyL J%6N| RXYBu$`(ʿٺ"ʨA zA BxZ9jX@a kI [ 'Q6qCUF2\'zb y':0j:| QQZ) ʬqrq$i9 *z! [ Q A#*">AiZ/QFy!,JA%bjRJY߱I*piby-Q =ev+aX3 k'w!v&}&|"9bu w #2qڔRY qY"/ұ0'B1)4i)ב7 A"a0'y- 0 0k5c5=D3YKlISm;ZazYa.rqka!u90rx+ri;w962|>nDZ|1?AR *Y))r0BA#z$ƙʑ/Ba>ڰ8r(j-3%q-:>83: i=9 3.19FyM[);dDz)F1WZtqN\ 2C).$B%q!4m2J9,I0RA.ʍ'x#چ-ڑ4Bq-b"r'"01?r1;²*i79>3ґ6Cr!=8I>DC7%{]!!)1;;t!D)@"Fk))"k!l1P +yJb3"`9zZ4b <8D)BBʹ<PyNrDZqDFRIXQS K R^3RAa!^D)]L*_+el yhb9Jl-,Rfa g4~q0/B #R%@r.:u;zQA|.R~!9)F*}KrqG)@HrGA*ƹ=đHʾYC>JD)G)N1NsQO+QV+TqNNKyR2W`A\c kncqbb+uD[I@~*zl?:h7!4Jg"@y1ʣYva2‚77A6ڎQ>ʔy?RiKr :ʔQ?EB NVRQTUȩLqN)X9Z["^4!Y{BS;Wc4[#AX[Xuq`lSAs v]laRqr{Y1ʏ1K{AjkYAV7uaF"s @"k!Y\JjQ>2Y=1;!8"I7q;•iJz5ʤ 2ҧK\UQSsU-1^E^#ly_yIcSIf)ec_[VPKՑN)RqYy_˶ba_k ]SI_ʙikԡn3qIs+YrpӌIWˠQUUk)Xs]aC`KZSQ 9J9HKR$XtQ[9[+XkYUUy] d, j,nq4)q2p9o*}a$)g9 yBJc926҄L y>?B8zZ_!6"paCڊ?)FbA>QAʙ!>RMTQKZAIzܱ^K aY_KPmKKaM+QR#MH\ G4)Kt$IR'1Y(9Z<)V4.1O45qI ; F>H@YM@qR>TaIh4QlYYn]Qo|_in_n1RSQsr1O+YTka]Ә_aUHKYB|ELH,FtA@N=T@U)H$RPOaTLULaR,NYNOMONOQPT,S T$UPW!KVFTBRC4TGlZNddAV4o]yctYgijilT{mltni"zc J !BlCk/Z!:Ҏ9JʝK1SuQl:2=SJqFaJIGyIi=jQY 9QE UM3)H3qHHKQD W V4LB B$Ey>,HA?R@`?$i>j)@dF,[!L$QPKSJaTLVQaX TyZ,U[V[LWXZQT<\N<]H\]AC]yBL`ChGtuyL QܓV[A` dRQ=$WY<<]>^CXJNODIP;Pl7!QT8T@ZtJy_RbXaa[)]l\Wt\QD\KZH ZE^IEdgF,vGIKN\IR4YW$],c̙i9n}qJ@~8b[)*(QDy:l!BCa1P?ORiS^q]ҕ!HZ!NUC1\IG$&QQDBUOQRZQQLc Vb9X[YP4WIDX?YA TBKALBA<@8B7D>lAIt=Rt9!TD4!P.L,M,/RT9aYlEi^Q1_Yq\]V^Pt]AKZHXGdZFbiF$qEdDdCCùE̱I\QOtW,_LfYlopVA]܁S"YC4M@[JVaQU1SL`Vg\aYYtTAK,K=H8,EY:?y=8;464Q235t2)?1aL3T79T8O6Kl3QK\3OL9SBVDMaU V P[YL]QH\G [F[F`yEk D,{A?,=d=T?|ϡD\΁KT Sq\cl)hlYbw.jfq!Bd zsx)Rajb.x6kDI3"YJ5EWHkQ;#BkQM TQL;9P<9OLAALHQIOaFTTEIB1MHSLNXQ]dO`LIQ`dB]?1Vl?QNlBGC$ Y>2C^9C܀1=|@DrDLx?nQ?JG|/J5iDtP;b<`IVXlR]R[Q^TMyfMjVYdtbYXgR4cTl\Z\[dfUsMxIlqMtaWPbFifEbKYYQPLUKULUySlW[^b,i)d\taa|[TO4{YMlsOTkqTn9\k9iY;VA?CABQC,KFYFeEgKbUl[a\lX)_lWbYQe]cgY[TsRtyNuPDnASlSsN}G)DTxGTgOVXM[OZ WU4`QdiRdVd^gedoh{QgLaiY̑R9NNvSiY`Ya[hd\lcmdmikyfaT)\QY9YD[0I3T!U[>aRYQT9R=t:,%$)@,nG\MYTa!Pi NcTU\N`T _l^QZfYUmiSvR })P$})M$vJqAI|qAHuGxH sJhN]RX!TZTbTkIV|pZs9`tfyj,ieܖ^WIPܕMN{yStoqYg`cffimiyfya[̛V StSDbStR^\Y!Z,dU^YL`FYbSQ[dqR$k)OLkTtn Y|uQVtyM tDkCteHf1NKJa&[`iT;TH7aY+_AlUqVKQYt'!RC Q`YI,1YBENn9C<>wG qyF\~GQ9\ y ZM\aBF\hQQ$VX\[hi`hedyikf~ZLaMKsSlZ@79E79D$4G[Yh+р%9[ p7Yetain h3AMzLPf4OiF+0/ (4V._Jl_ZToQRsHlZa>DP5U9];yߩzT]$5W:]|DRdPR 2YpD0Qq|uN!BYo_pCc>_Mh(!;i>1?l4,36ÚA4z{Ti&Y3ᒼT9w}49uLlHr㚱))}D QɎ\ 0Di9{dm·&ԡ| |I+ѠJ9wItu]INÛqQ!|a<%9dP)4y7$yёê6z8s9tq,)Xq}vLh1<8VjDv"6\q{!F^p `uI1y0pBrd~`[1 L|AimA!hT Q頄PAK~JjM nDɰlD9Fy}$RQq>Y49ɕ\E)~<7!@Qh7 @lHj# ?|I"$bO AI$b(?H$]BvI j$^h\DI$ (?I$ < ?Ib# CI#Ԡ@H# ?&H#hR EI##BlI#ӈ @@H$ (Ơ>Ir$NH3 ;H$B( @~H$ AH$Hq?FH:# H#BIR$& =IZ$-<?:H$.?Iz$%(<€H#n=I$ 8>6H#h,@I$H ?XH$H5:ڀH#>I:$( >I $!( ANIJ#H;?2IJ#A2I#H?dH#Y BI$QEDI$ HG =@I#h? H#hʠ@jI$ 1 >΀H"#H ?΀H$HJ 9H$ @"H#a?fI$kH] ZI$ (?LH#HI 7&H$/(vBHI *$%y 6H# CH#h, @I#h`>ހH#h@I#H @tI$ ?nH:#ɨ. MhI)B$" H&H$6 >H2#h >H# ?hI"$)(C ?I"$:@ H$H >Hb$K B I*#I% (< IƀI#R#e BI $M >ڀH$8 ;H*$H FI "$&AԀI#h7 A0H#(bC4I $# <؀H%X CH"$87 AHb$O(>I:$"h' B^Hz# ?I2$Fh"?H$(۠>܀IZ$*>I$%k AHI$ /IQ#h>?*H$( BIr$ ? Hj#ޠ?0H#( ANI$  <~H#H~@I$%$AI#( AIj$H %8HB$Q>dH"$xhA؀H#h FI$ ?H2# BI *$ ?ƀI$"xBxI J$W BڀI J$H BI "$6DHJ$,B CFIJ#ȨȠ>hHr#@ CbI#h ?0I$ĠA I#h_?fI$& DI*$AI $qCfI $Q(kFIR$d7ĀI3$O4I #HTCH#H ?Hj#CHI#=ԀHZ# @fH $ B\I$ A*Ij$"h CրI$J/ DI $$H2 EI&:$[h^ P0I$~>>@Hr#ڨ >I$BH#H ?dH#(?XHj$ BVI b$&A&I2$G AI$?LH#H1:HB#TH# H$\ A$I $v"=ހH#B ?րI #HlB8H2#3CH$H0A(I $E8 EހI$JHT D6I$  CNI#H+=4H$ a9I #(BrI$H D^I$' Hj#HL >H2#H ?I"#ޠ?HZ#Hs=ƀHr# >HZ#>|H#HG=2H#>H#(N HPHr$?:`H$]A\I $B DIZ$  BjH$G;H${ ?I$0h BI$ >؀H#x > H#Ƞ?DH#( >TH#| >&H# >vH#(EtI*$ BRI:$  ?܀H#Ϡ?(H#?H#h >؀H#?"H#(?H*# =H #H4HIb$?(ˠ>8Ir$ahCI$:$EӠB2I*$R @I$h >BHb#h?H# @I$h5@I$/AI$a AIJ#à>ԀHj#hР?~H*$ @rI$@:Ir# ?xH#hmAI#( ?jIR$. @H$h6 @܀IJ$ @Iz$& D@I"$-hG?tHZ#>H$h2AI $ , @I$H^ AI Z$*( BH*#Ө EPI#HALH#q DIr$: @2I *$v AȀI$\ @H#(?IZ$(@I$$ AvI$]BI r$Mo E\I$H>DH# ?I$(G@IR$H@I:$#h̠DI$RhM?€I$'(Z @Ib$ ( H# @FI$?^H#H @IR$/ DI*$\ >\I#?H#(" @I$ >؀I $TDI$ A܀H#?rH#(<H#( >̀Hb$pBI $VIC~I $W BI z$!b @I$@*H#h >HR#HR =H# A~I *$7 DI$E HI$5AI2# @HJ#h ;tH# ?H:#h @΀I $OH DI#HU =H#! AI$hq AlI# (H$(JBI $2>*HZ#g;0H"$h A\H2$( FI!$GI$%5AƀIZ# @ȀI#(f =`H:$(UAЀI2$G@I$! DI# :܀HJ# ?TI$hv B>IJ$>8Hz# 9؀H#(;:HZ#?H$ hj9pI$Y G΀I$DAI$,e @pIz$+ AI$i AbI$*HCI $ @I$ =H*# =|H#hݠ@xIR$' BDI#xH"#? H#?FI:$ CI$-c?H# :̀H#o:H"#H =8HR#(% @LH*#h DI2$Hڠ9H$_hJI$"ؠ?Ir$3H AI $=^G$I$e(AI2$B2I$ H ?I*# ?I$Z@ЀH#h @܀I z$<) DI$/h@2H"#( ;bHj#:DH"#H =lH#FrH#A IIJ#M =I$(?I$\DPI#(* >Ir$ H.I*$4 AI$ AfI R$U FI"$!(3@ҀI$(k AIR$_@H# AI $(hh@H$(` CI"$OH97ހH ##?NI#H BI $>()?Hr#( EI $ E\I:$%`@H$QBĀI$IAI$(_B^I B$1ܠCI $")?@H:#GBDI b$T D؀Ir$L JI#h ;.HB#H?2H#H AʀIb$E$ =̀H$"gB,I*$HCI $  ?I*$ B I J$B@I$-H DI$>HΠB^I#H OrI7$ AHb#( ҀH*#H"A8I:$&q @H#AAI"$nBI 2$#CtI$K BRI$Qנ?IZ$hFI(I$e AI $ Ir$ FI%R$i( 8HJ#H6?ʀI:$3 CZI#Ƞ?HZ$7GFI$DN ?H*#H EI $ KXI*B$]h B6Ir#(S;HB#H ?I $rDHR#h B^I$DbIZ$BHN >H#BI2$rCRI$]>ހI"$ EI $7H AH#n?H$֠D I $)(s @\I$.hBI *$G(WD Ib# ?ZI$*H ?>H # JhI9$H D Iz$NH?8H2#" @(I$>I$ @RI $;( CI$Qh@ހH#API#C ?I "$( E4I $+(AH#( BbI$ k:ԀH2$ߠ=dH$ CI$D BHr#ߨ@H*$h IրI $Rhw @,IR# <I :$k?@0HJ#ܠ>H:#h A&I $* C܀I :$&H:$d @|I:$,@H# JI4$( EIj$ ;I$gBЀH#-@8I$4HyF2I B#H: CI $hwGRI*r$DI$H >I$hݠAH#^DI$H IրI$ E(IB$= HI*$=m~,KDj*h٠?Hb$Hj@0I *$TWDnI$H( IB$h @xIR$ H=CҀI$- D8I6$G܀I $ ?I *$8o @LI <\I$:K IZ$d( <H#h DHB#0 ;I"$(m 8H$aH9IB#y* @ 7i[nȳjh(-s0u)tfz}gBkBD]z},':jF*:[!eS2s^aE RH@.gs(ƚZ`Q$E <™.*"4,/GҼrV_rOC9CYd2Q%i"'b*)⪸r(FP_"%iNDqAc|6c)T.b8)i b ! "1$bQ&q"(+G"h$UDF9#IZ/b6(Y#aAAa b "1$ZrzJ^!QLQ>cj2<)Y"AAAaA ! #+R~hTD~7#H,"q#pA` A` ah pAa" "(J_!eIK9\E#l4.'BȤ ఠ ج   HDڜn%fSy=IH-i  !0 @Ц @  H b0h:s&J&WD?CaJ-a A  `hX``x`  A(Aha8)$犂iN)8B,'X P @~@~Hh  A0ah :JSB.n&Ri;#6(aP @Ф `p 8~(x(x0|Hp ز P!I<+"( Hఌ@v pnp t` z@pࠦ ضAX1}'&.^EcV1  @`z@ rnp t` z8h вH!!a)Vh!EMD!r7b%a  0t pr t` z8`  8xAЃG2>d aJ#b5b#A 8v r t`x(`P @x ض AP 3+glo" SDq<#)**b  Pฐ@`|(v` v z0P` @ ش! 0 AP pA д `  @ `aA B)ǪVk!ePDIt:"( H p8z0|8`Hh x ` ض  !(!Hhaa  !( H`PX@p ȴ ` A 0aHh!a " !) #HjvFzZٚCÙD0!A  p@P@P`PX h x ` ะ @и  !0 PpA" !)"#bI,%``h p า @и  ! 0H`!A !!#"A($Y2&)Jƒ':8eʼnMd1j9C "(B !` xx x` @ @ȶ  a A8 PAhAa !!#BA(%a2'x ` ` ؼ A !(@Xapa " !) #BA*%a4'<)0I*''22eM$1j:c&*0  @Т ਞ ࠞ @ ȶ a !(@Xapaa  "9&$Y0&By8(B*a ب `Ȧ @ ` Ȳ `غ A( a8PAh!!!!#"A&%Y0&By:(D*L,/22*eNAj;),+HA XA8A  ج Ь а @ظ ! A8 H`xaA B !) #BA(%a2'b:(D*N,"A ax P A0 @ `  !  ( a8 P!`xaaB !!#"A&$Y.&By8(‘@*bJ,T-\/4*Z*h*`s&YYER4B'b Aa X @ !(    ( A8 H`apA!  ")"#bI*%a2'by:(B*bL,T.^/# X5:."(ba#BaahH 0A  Aa ( !8 H `Ap!a  B !!""9&$Q,%i4'<)D+L,"V.^0Ch1+6i::fOiBl:aN24, 'bY#b A p!X H 88 !@P`ApaA"!"1$#bAP$G$1?#d7cAH12+ 'Y#! !h aXaXX hAxA!b):\u"& b%VMDz<\5#)F04+"'Y$! !aax!xaABo"fcXE1N$E=Cf7IP2>-b.)by &I" aA!ۀ2rZbvg.j!fB^TKqC|<f6AR1B-2)$&I#! !aaaA~gzLq#F e ZeYQ$ɲH$IAcz;f69V1F-6*('"Y$b1" a!!a.irď*hrNJTr*ff[qRٺIaB$=ér8a`3P0@,2)y&&"Q$1""!    ʐ*g^s4h"* ]SDKDE!?~:Cl6CAZ2CL.B>+2(y(&BY"$A#1#)"b*)~{&JRo#*d!eYQQJDqD?ɀ:n6cA^2 P/D,:)b2'q,&a(%bQ$%"J(rv%Hj&R` W1OIDiC?Ʉ:r6Id3V/"L-bB+<)6(B2'G|wgRl#j(b!EY%QQK$F9Acɐ;8#q~6Zx':fnFDfF*&_! XYRMDHaD!@#=;9)u'`m%Be#F&^!X %QRMdHDiEd1A?#ٞ<~s^kBdF&]W%IQMIyFACD@)Ǫv'*lnNg$&:4`"eZ rT!OdLHqE$B})u'jmƚNf$&*4_YrT)PMIdwBxp&\hR@b#E(\!ŚW RSOLh*~Gvg*vo&Zh$J@aE*\"X!b T %)Q-HR+'Ңy)'RqFfj%rLd$&6_"&ZW!%Z-:纞wBp'&fijNd$F:_%,[ebʂ+z)jrrl&fZf&JHb$& :^舭JȀʨy)Rr(tl&`g%fRPc0Hzԃg{gtnfnj&fH.(b҂,Gzzto0RЁ,'ڶz*rtg*p ;BҀGҺz炦u+:Ԁھ{1 H:ڀKhj2;1(3s.) L4 sRL @$@@ @ @@  @@ @@$@ A@ 0@  @@$@@0F0 Cd0 @$@@  Bdp9 `0 @@ @! D C`1 ` @!@!@ DQ X,A BD  G`0@!@!@ D H$ A X,A D BD0 @!D@!D@!H% DA H$ A` @ BD `` @ D@!@! DA H$ Q H$ `4:D0 BD P(@!D@ H$ Q H% DA H% A P)¡@@ B`   @!@  H$ DA H$ Q H$ D@! Ax(@ A@ @!Q H$ DA H$ DQ H%D@!P(B@P  @ D H% DA H$ DQ H%D@!@ @ PT(D@@ H% Q H$ DA H% @ @!@ P(РP(@ @ P) DA H$ DA H$ A@ @!@ @ @  @BP BP8 Q`H$ DQ H% D@!@!@ @ D@!D@P(E@ ( P 8PT H$@ D@!D@ @ @!@ @ A4 @( B@D@ @ @!@ @ @!@  A@G@P D @ @ @!@ @ @ @ @!P)E@  @PD E@ @!@!D"@ D@!@ @!D@ `0BD  D@ @!B!@ @ D@ @!E@  P  C@ D@!@!D@!@ @ @ DP(D@ @ @@ F@!D@!@ @ D@ @ @ D@!D@ @P(D@!D@ @!D@ D@!D@ @@ B`0D@ @!D@!@!D@!D@ @!D@ @!D@!P( A@ @ @!D@ @!@ D@!@ @ @  E@$@@ @ `!D@ D@ @ @ @!@ @ @ D@ @ @ P) E@ @ D@!D@!D@!D@!@ D@!@ @ @ @ @P(`0 p9 @ D@ @!@!@!D@ D@!@!D@!@ @ @ @ E@ D@ D@ @ @!D@ D@ @!D@ @ D@!@ A@ @P@  `0 @ @ @ @!D@ @ @!D@ @!D@ @!D@$ A@!@ @ D@ @ @!@ D@ @ @!@@ @@@`p9@ D@ @ @!@!D@!D@ @!D@ @!@@@@  Cp)D@!D@!D@ D@ @ @!D@ @!D@@  P  B`0E@ @ @ @ @!@ D@ @!@@@ A@$ A@  0 @D B@0E@@ @ @ @ @!@!D@ @@ @  A@$@ @$ A@$0 `0@@ @@$ B@ BD0 E@@!D@!D@ D@ @!@!@@ @ @  @P  D  @@!@!D@!D@ D@! A@$@ P @`0@ @ @@ @ @!@!D@ @$ @ @B@ B@ @@  @ @ @ @$@$@ @@`4 @ D BD@@   D@!D@ @@@@`0 @@ B@ @E@@ D@ @$ A@   P  @ @$  @!E@ A@ A`0@PBD B@ @ P( @ @  @@ @@ D B@ @A@ P(H$ @ @@ @@@B@ @ @@@$A H$ @ @ B@ D@ A  @  @ D D @$ A @@ @@B@ B@ @  @P D B@  A@ @@ @  B@ C  @ @@ @ B@ d P P  @0 A@@ BD  @ @ @@BD  C@ @ @@0 A@$ @@ d @P P  0A8@@ @ 0 8@ @$ @P @ A`x4@@ X$ @P @Px4 @@ @A`4 @ @  @@ @@$ @ A`0A@  `0 `4 7777BUFR4b4~hd!]}L!bV@ .dEF F (!I@ @ @`!Aa@!@A@a@@@!AaAa!aĂBb"Bb"Bb@B@b@@@@AA"ABAbAACDD"DBDbDDb€"Bbƒ"Bbb"Bb"Bb‚¢"BbÂâ"BbĂģc#Cc#Cc#Cc#Cc@@@@AA#ACAcAAAABB#BCBcBBBBCC#CCCcCCCCDD#DCDcDDDÀÀ#CcÁ#CcÂ#CcÃ#Cc#Cc#Ccƒ£#CcÃã#Ccăģ$Dd$Dd$Dd$Dd@@@@AA$ADAdAAAABB$BDBdBBBBCC$CDCdCCCCDD$DDDdDDDDdĀ$Ddā$DdĂ$Ddă$DdĄd$Dd$Dd„¤$DdÄä$DdĄĤEe%Ee%Ee%Ee%Ee%@E@e@@@@AA%AEAeAAAABB%BEBeBBBBCC%CECeCCCCDD%DEDeDDDDEE%EEeEeŀ%EeŁ%Eeł%EeŃ%Eeń%EUEUe%Ee%Ee…¥%EeÅå%Eeąĥ%EUeuev&Ff&Ff&Ff&Ff&Ffv%v%%@@AA&AFAfAAAABB&BFBfBBBBCC&CFCfCCCCDD&DFDfDDDDEE&EFEfEEEeee&FfƁ&FfƂ&Ffƃ&FfƄ&Ff&Ff&Ff†¦&FfÆæ&FfĆĦ&FfņŦ'Gg'Gg'Gg'Gg'Gg&A'AGAgAAAABB'BGBgBBBBCC'CGCgCCCCDD'DGDgDDDDEE'EGEgEEEEFeWegGgǁ'Ggǂ'Ggǃ'GgDŽׄ'7GWgwDž'7GǤפ'7GWgwץ'7GWGg'Gg‡§'GgÇç'GWgwćėħķ'7GWgwŇŗŧŷ'7GWg'7GWgw'7GWgw'7GWgxh(Hh(Hh(8HXhx(8HXhx(8HXhx####$$$($8$H$X$h$x$$$$$$$$%%%(%8%H%X%h%x%%%%%%%%&&&(&8&H&X&h&x&&AAAABB(BHBhBBBBCC(CHChCCCCCCCDDD(D8DHDXDhDxDDDDDDDDEEE(E8EHEXEhExEEEEEEEEFFF(F8FHFXFhFxFFFFcccccccddd(d8dHdXdhdxddddddddeee(e8eHeXehexeeeeeeeefff(f8fHfXfhfxfffffȁȁ(HhȂ(Hhxȃ؃(8HXhxȄ؄(8HXhxȅ؅(8HXhxȆأhxȣأ(8HXhxȤؤ(8HXhxȥإ(8HXhxȦئ(Hhˆ¨(HXhxÈØèø(8HXhxĈĘĨĸ(8HXhxňŘŨŸ(8HXhxƈƘƨƸ8HXhx(8HXhx(8HXhx(8HXhx )Ii )9IYiy )9IYiy )9IYiy )9IYiy )9"""# ##)#9#I#Y#i#y########$ $$)$9$I$Y$i$y$$$$$$$$% %%)%9%I%Y%i%y%%%%%%%%& &&)&9&I&Y&i&y&&&&&&&&' '')'9'IAAB B)BIBiBBBBBBBC CC)C9CICYCiCyCCCCCCCCD DD)D9DIDYDiDyDDDDDDDDE EE)E9EIEYEiEyEEEEEEEEF Fbbbbbbbc cc)c9cIcYcicyccccccccd dd)d9dIdYdidydddddddde ee)e9eIeYeieyeeeɁ )Iiyɂق )9IYiyɃك )9IYiyɄل )9IYiiyɢ٢ )9IYiyɣ٣ )9IYiyɤ٤ )9I )Iiy‰™©¹ )9IYiyÉÙéù )9IYiyĉęĩĹ )iy )9IYiy )9IYiy JZjz *:JZjz *:JZjz"z""""""""# ##*#:#J#Z#j#z########$ $$*$:$J$Z$j$z$$$$$$$BBBBBBBC CC*C:CJCZCjCzCCCCCCCCD DD*D:DJDZDjDzDDDDDDbbbbbc cc*c:cJcZcjczccccccccd dd*d:dJdZdjdzdddddʂڂ *:JZjzʃڃ *:JZjzʢ *:JZjzʣڣ *:JZjz *:JZjzÊÚêú *:JZjzĊĚĪĺ*:JZjz *:JZjz;K[k{ +;K[k{#K#[#k#{########$ $$+$;$K$[$k${$$$C[CkC{CCCCCCCCD DD+D;DKD[DkD{DDDckc{ccccccccd dd+d;dKd[dkd{ddd{˃ۃ +;K[k{ˣۣ +;K[k{ëû +;K[k{ċěī +;K[k{ ,<L\l|###$ $$,$<$L$\$l$|$$$CCD DD,D!Y1 )0j)c- fXi iD ;E*a z_:#XҾ 8(xzPx D)}pK@GtRS`..4}%"$8OB&P0l ks4rS=GVІ@n(Jp)f zC%AGX `_\10;e@ وDd(ŁS [" ˶6RAm\ṽъeEO 0%ϠfH9 Q-A ,p.÷mQIcTUEh,uW d#<܎:ɏz5! tאDO`DVpeవD2Ძnz=! UN^3XdEU-!1>H|FE8ܑd`AZ \ L # O³RP 48!Hd$q4 |Sh`&/х ^dc2fƶ\68r%asǎ=OQDl A ơ HhXD'sR$xV$/I$ B'D[,T±ȳ3.}z 4b[h9ƌ5= oc@Fe\<ݏm8}t 9f>ZB͒6 ĊcW#QRISc}`P(1>A$)wf g2o~+0ӈ"lDq>H„:(!qG t,UP-XE .}Ɍ$b#$3ƙB5AhpZg@qC3" b9`i#!;%JRV IK"m|װ+*6AX{YzBe60p fE#F@7ĊZ8t#80G>~q 43!^1bȎ/ Vdg#00Ft6kA#hqSNJ=0=8t$ 4 #dpC&kčvI$$b >6J]9 Cn)Аģ%6A PPEz,l5^o t(R3o1C Hdkcm& +(:2qPyxZ6@̇ E"RݺGȒE_&32(FpjDE ^@@3^z o! )E<Qؕʐ'-@ 6Si" Ţb-Av D`Cz4 nisGS;nFG{#qH+hB6dY#!'(?"f!J"cT8E)Sg$X5B$帺0RQ\ e;?FՌZ7i1s<sǟ=A„! HxDR+^䀊$ry&SKrbsJH'ܱBJBVυ0a 8k:t}ꞇ@>!Y(.L2#: :K"i{p"$6)QV{ YbdE0| ,f}Afw# 7y}It㭚h)Ǭt>9d!; =rE.,*$y(_Kעeb*( CD`LE͔ M xGY,pS,Io b0h\pٔ9Á}G}!oG:bGf8ߨ'QEWaTOz.q{L Hb48e΄ƏL5RYoSŠ鋇g<gq}>n (BB/T0k)A[+ D`]0:70!xun̡^D2i&:8R!i_]-Ep x_# k`We4 mmCvT8^>Dp7( :2`x%pa9 3U¤P#'d2A poÚD0 @aBkq$e1) M"|"LI +#_΋4\[UB$(!$1QόhcT  42~%"!$I OBԔ0P jp\ =āIHJ#a1KAgn)aUtX>0\0f8?Y8-^ f2gC:P*[`q n^Cbpl7Hy sHEFb7)8h&Q="RÂ}-Qs| `@1J|vL! @L2B|TЬ.! whg St jH;!*X*"1!9IlX2؜l(N!Vı >/Q¤@k90ZpT%56 $Tỳ02 o``ͨ@bE0NDm4$B( | MBz+j acC%p9|D a FbAzx,T'zABITA;H [ BW0,kp b2*g`9a^M} f!"GPBGp'ܱEc $3 S¸лg28A# en"O )? ؉gf^$ &nMv_GXPR,CAo b/TU9D}}!1 G$Fj'ЙE)Z B]hj ``7a30xI[tD1 1DL&{L*q` rx^CTk7sΠyC "ttEL7*Ȕf,tv c9Ƈ:Mj~D![”GKgQ3^ 0_ l{hP;$>mBrRr#i!F8)^XV -\VͶ86!éwCyQLD6/G@BP2! wontU0? Љjx$,,ǰ/題 xiCi$t=T3,D@"ޑ.!B h [.,W͝d2 d%D07!$0ʡB@yɈ0 ~\;vC6MrD9T\raC\7A nyCo~\`5!3 YjFCNJOх3A K @9>E|v0@1=*xCo;:t:/9@A.xpCj876a m.`TBqb Q @6؀&C>8||0@$= m"8x h|;AtRp1h9o!ب8R"zDR PZ7!a YB^  @Bh؀&C^\>ׁ|B@iX(%'C2IWH9\.$Q ",IZ"n(E)|)2Hۊ@QZmE|(iQB P3B @!'A;\N}r,h&4 Lc; p焻%-, c Jl]ЫӅ[|*U[T9%HEC)O( sSf()).1H =|Q Q (PQA

'=b (O"wp'HA9m,N Bovb&H =QɂdP(Dj 0P")ET('@ PB~a''=#Ow5(Kn,$`dW"Jo6+`Z1 HV(BqEU*qSˊTTbj=)QN qpSlBȦ//)oKT$R".h[!(GK7*qPT S7@07)M'-YWH E ,jQb XB:^+_w WKEsP+[ VǢ*HEd+XV ,UギTl`7EW*U6 U;EK/&Qx@]BZy.or`\L"зr-1laHZ"L-7iT GZ#7z(,f ,(YKbzD|,]blXgBr %l+^WQHjr$+[ ڄVB*e+#X\"Ŋ.Ap P[bP@-mU g$[ ū,-IiKZBBJŝN,ff 0YaBBJ@ ,kbX÷, q`W"Я 0q ` /С} _./'x ^ wux .qu ]?BEZ.^Qr \kbz 4E-o#u[ۤx-kYZԜ$-1hA ?Ybul?l,Qe_'Y)Ȁ>/~ (_"zȾz/{ ^B!`/ x`]o8;E|.u1]B8.HqqƋ\<\-فn[p[hڪHLŰ-z1kz XZԵťP1qDtcyoȈ,x1JG\b j00B &a,; 0RQ" T`V EZ/ _Ր/{`^pх/x^ ]Bhֺ.t ]u60.Jq \L"E-ao h,c+(F,1Xъ{PXbiJ 0[8 a _]³@0;`)(F\0+A,`b{t/q} 8_-X/Pz9 D^_b35E.qwWd]^к.afc3= F^F2Q3$e2C(5wJ2G猌dG!@F>1!tHcC֘|21!ٌ[HbC[%1# AaCqP0[ (a$E; D0O1 `JVp`/~ (_B /| H^B4`5k3V1Mfg2uE`2xec,`U2A~ 4d&/QH2<񑃌d. 8<1Joc`V˨%/1n UlbC|"P1ᇿ:xa _:0Q!`#4n J0Eс `M#XF4!V% 1Q qcvϐƟ416liL=]8ҷƔ4u hF0S>4=q h9C@܆}T3Atgw; `eqj3~ћ tf4Fd3are.tn(9X86Jql_a5 k(kFYlư5k UHjSԥ5D?icNcl;H4V(\i&H=4XhhkCByP:Ft3!Hg#FEr8gLpP8% oc~ GB:~sރ^@FG99͒is<.z9gQT\rC#9a@>q#AmX88k=czfG=L19}`ժ>}9>TAW|m# W?~cU0G?NA~^s K0>}_fI@Hr@; @<$d q?.i`N?0dt wHd@RC0$kD Pf@2ސ8$ *a'H5A c Y+AX" iQXq s"lAr$Z!YˈDBx ;ArEĔ$0!| \B2+{!W) SrB:H|d;f!չsC<߈6! biCE#x$F$"+>D H l_@^ 0( @H% Ea`Z.L IaQAP K`(  p;XtzY6@@S*H A@ h6aɀi`8ppF`Y@/ T`H%h AR@@[P0d N烚A % &}E# 4dxj8:O?) `NP'p\ ph:b#/X@,0 *@Q+D FU|AP!n h?` [h1(  `P^ad7`y[FX/, @D# *A @jp6,_4 xaɀq@:`pH 2V^ 1( TpF%j @mx8XXb2 xvɀq@=0pH lX ^ 20 Tހ@J( ~l8xqL2  xaɀh8pdFW/ eƀJ((l ?ۂAȠg6Lv 3% ^6a`2`P(/)-Q+$  # w Ap$ uցk@ `X2 heAڰ8d~D% L&}?" li@a20LqjE W,d Y"A`ۀ(3&@0 {R@Z2`p(j)Q+ FUÀK'0\ {^`a2\ Ov08d̀t?@$p jq>!H lo@@j8p sE Uh+X` +A@0hưRD7@&@` X /`2 Q+  A@L'8b Ilbp2lpP D~O20dSK`&8h ,?t܀`pG% ]@]@0x  p+L2` 6ɀq ;!0 @s> <Z9@T, %@pVg4&@ X)/X@2`hH_h7H,h$' @`Nx*< mΠj6d " ph~K, X(S`H^ 2H TAH0 ^A m F( phY3XO^ 20( Tr\h~ 1܋i8Q*PiyB0$7? UpO'czFt]-%X O$ Б54W"m =#xpM.Z m"*12,L7~zWT0-C BY!F0jg3&@p d/`E %D 4~SHsA#W aǐT3:VG䪺#g ۊJYZm(]EAހ(h 3~jsK<$c1;ڱHІ82 {jE(XypL4'1sTm9LYD  @O( w`8 h@A+@0  KpA >81 ,1q*䩮K7aMDeHtP 5x/=MНEH<_!lm.EY!/TP 0 uÐAZu?~)(F3i69ɘRQS0T'[%i!̜^̥˜I}e|8B`P4Z》S, 8 KB0vhtp-2,"oXG :[F%hRG >+Zi<%2Gf3̆"G+PU; /a btρ7C0̓HA+8 I1c!N@e3 hq,A:<3PhV,%)g @`p@L,  &oB$2,@2ER7b[ҲTv]iU; ilо&ǏZj엍:1ZJ0cy;N eZHJ3_C~2t4@3 4rx җ6rЍAH$yuK͆8xX\/xw*;9y)'Ѵ׃s~8G Gdiu~&uEEfIʆtª,ۑ6ܭI"G.) 0 !~D % ` '2FbpYBߨQT j'ɊnEIVpH6Զ86y'[}QQ'):_Ve^noQtz\go'hPioy7g@ZF!6O8!=N&E= ʘ*gU!uI|tAԅIě\e$Kz=PHGoiջh nxo77F\~ 3uѻhm{ge2 Hہ4AwlDubγrv5@E X$/Apg;h z ԓk_Nv +,QW L+jAE`,חfͶBBv@er@y\Km0;e-;[HO?\\~ Hu|4”@4 #˲o5{*tk5Y VJK^iƉ$0GTNcwBṳyt7x4 VvDθ+ w] ![H$&>`sm"{at-"JKHyL-P&5H7>E7e/3Ih=fo7^N<ߋuW0Dgcɮ w6Y³bbڂ4"cs0G[i5e*3╈og$Kt6@Nc3TغN[o6R=w5Bٺl0@`51v1"s|XXAXgRPhL" / o*oGd}.\.7HSb[p ]H[DyR7,#K^w+x%1Q]1g&\ߞ :{=P^O˺fw.B 57-;Zl::0ߧcMlz ݝfHnǃQ|n%vs<dnUK:ՎAgΉij>Ux&ε^/>ffme?K'_}Ytc5O\O+ ߰t_&M2_ؠ,KUqV=B=N0BG۩8x8ڂ Ký"v8ږ Í+Vlw76\")M4|ki #}ZPl/gvl)ٽ(tgT HlOeMʙg6\97Øѧx̳p+ *=Jޔ7#Ffs[8 L>$7w7ylrty6dm :ƕuC^"D0<-OB`OͰ٥n;\MZU"V58ިHT|f"(LBq,f4x MOtP>J LKpȥ7Ad`ħt4ayE&s8= F1BeW!SjKʪp+8G; ׋;eVJLRMVLNgє,[h--к&dzdM2() 9agARd]6 /D;[Ud4cfbrROn=ɋS$A]DvK$`.h< H[.*Igbs4Yڦ =QujqYE.ፍHrc!# ULۇo@R,ġqՒm|sݡ IiB0ĺ$' xO"F:Ad%2-QMrt"a)j Vg8= &a<>`%K`d#si 4PĘ<6>!Rxf. xs5|PFa`kC8Շ$>b' E4Ӑ4d+~ rS&VR7C>V1ޘȂ`T)AQ Pd Zc*PM¯>"jhɓ͖t! 94l G  ,aD+q {R&iTg&ģe0Mx b#@%&@5$@T.!4]YYɖUDE_ԮOJĆp@X/`R 0^]ɮVFi-u͏@!TpAR0Np؇vA?S2U~Aa&(1h' @O&`<<9?:F;79琼+{Lo`K F- U Cl(d7h `e$aU Xqd $HKxf#1 aQX>=Ea\p@Jp tL'?BXˇLBrYƏ:@-stK렍" :L" p`; (mD&l0yM&] ,l3 !iVZ|tRMT9 TxPlU,A༇0EGrɦ= R50Yn0z A خP@   2!8@94YѪo vl_D;at2`T @NP6p k&l 0yDQ' _ Bm!9#W*/R(T ;$ @L`L1 \` # B\4phKҗF2@q @A `e2 K|Lҳ(q> 2$'uIYn˖!|S)F7Ϗ:1)&PE1͞}pK_|FfG眵jX > {h:X戬R" L2٭v K:uŚ.̰ioǷ1B,s>N cY4ESħ.Mnl`F:LS<@n cc<GR|d$i-k3$0 @ @J4!U ()Ae gz~HRfdŶ0Ijq5GZ]u,ekLvgl=Px%39RVK21t{ #)+ T2ȗƦ7)Џ@2)&P% xp@x(C12yOՅ! ; l ;"E:-1 pc Mb31N0wS iKzw.͆,io@=fi>:JBv rC"A+ WxڇP>*ig dKg@RѪ(eQk[ϖ%&})˂q?ks0|" @P)𡅤20'aU c#S"ERM|.YLlBEBD-+5ha/Z܇*;2* c˼cBt?Qt$ƧMZ֨i̮|<oXvi]XCn;(@9B p2`D'VcCTEKxs.kÊ/D>Ĝ*d`&4݃D#_%Y~ζenrfE J L ] b ! ZC-ר%LJXNVt_5 h#L o#? bCbLb@l6Dm%>JXF9ԄW$U/R²ր2ŦrHGD9rmhRڒ0tQq`0@86Al H{@ROPT?,أ|/1s  >NR並3AN(v Jq`.-ha `\@#KFx:!8E+at|jiȖHih/ FohmFzMS>=I WP~@-^ S`h9🅀0~DH%? \Cpᇂ? *gcF'?zbP" 8 c@h7A, XtC"!' Uц;̎d'P]g";& 0j NN`8py #!2 p[DNت0ѡ v#!QIVN31-!R LD` !(CB4l. v"(UBX:$'!JJ[TGth+aL LV`%a5 U`ч < x*n 0gsPb`(DT9 qC?p(WX_)r>p(N!a>| !|FbI E@,1t (5ڣUcԢgN @((+TZQFI]/|  Bu"lBSL A)hQt*ֻ9s=g t(F )tUz]16^S.1ctV6c6&✳v?H-TBYK3Q8J@i,.hI6F|: Gie<)|S2k!>!|TT r0$ƙ(f18lTSZVki2Ʈ݃߁8J)B)#Zqo.%ؿ8l9?q֚e Q;" sf?a&E TJc.fS{Ђ3JS,ɚv/ X*@kA7&d2zRr1fTҒyTk9}2v].?K9,xBbA?q !D8BlR}3fN£Y5lr^ A6%tT q6G#HO s2fLʥZL )Jc1uA4)Řc4mEM-8sҊ}UKav^dOMI`c p.CbtTa7 %%$dvP+JX [{2 y[2$̴x)(I d1!cHvNR Y6y#4Ucm1'℆2lJ6 PvpY D f3GI%,Szy=MJacRs~Ю0yg3 @@$`#4uZTK8g-Ͻ8ӆZ  D > AD @ h4&lZYPsߝ惑JDK20(@%a + 'x nO"GLxy=5០\pd`c$t\TѢ9%hMԽ%c*( ^kx]GJ g1 p^׀a\aB<8`0< AQ K!;ȁ2*҂~Uu{0Nsq=!\EgκH[Qj]-%4i͝9hD*HK0fƦ֐.X @< ^hXWAAK.Xt"TMJL+e- G~FhdĴ:W+4f肐5Ii<(L`yzAucoO-Lukl 0&(-&Ő 2^ Y6'jCK)Cj"b5[{ #FvКKPjzŌs4k=dxBRaD !T*ؐ#tR$E%%ԟX az1\cuIAȁ$t*V n/&Pl;&1/,lt~jcUƼxc dX2NJɉ8' \3FzD#%ܠXKaw0V8SboNE<7`t'1n:S1&Ʀ֋ h-پGdϡChk ?>-(؄hM 9' "Y qz0HϚxr?9#4SbR_UX.hu8H^E8,C}@M D(,bYKiw0&0Sdpy="flP QT+դem 6D!>)ŀHpD 4(e4XAo.C@j: $$GK)@)5N_E3*EC4maA$&Z is/F3:i͙9dd 27I)m6&\釲Q>'ba 8Gh!$\U j.%Se 63{O CH U h.E/(xtRC,'e(b^ 1\s`nN;G $ r>I85,U#e 70$2`Oq\-E"fM 6&îxqAY#DuPaU8PDRM!R,%c"f6FӬxY@h=rhO*)M?!#ҔV )r00˚Vl9XZD $伛SS<"ZN !Q+ŬS$gMI7szAQ"rҪdNj IlPJiY,ȿ0i y88|BHu#DkO*!LU m/FəVm:3ЊDQ倵ccѦ6FCzO@E T`:RK=(,]L2t>G!4Qz:HA.&ꠔ2՛ss>4z9H=-&T䟔Z5Fîxq@= D\NK)8' 9GTrDh$4nOJHVuN>!tDQCIa1U Tв!E!$԰jxPj9M*edZD($mO G)JUD($mO G)HZh!$԰jxPj=LE`:[9,DܞT"SJX+L=(U 2Wi-ҽSR*qS%z]KՀ0(JW%j-ֽ c [, ^ 1Jr\k|F31\}f"C3g%1TZLj쉖ӚehE6vSupǹ<]+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>2%>>>>>>>>>M X>>>>>>>>>>a>>>>>>>>>>8>>>>>>>>> >>>>>>>>>$@>>>>>>>>>>>>>>>>>>#>>>>>>>>>>辏>>>>>><4(XZ<=IazFʢg-B9@P{Hx `X>>>>>>2d@Z hd x +%IsB>>>>>#p2 ( @ P / @ >>>>(P@( @ xP)6b (Px>>>>2@ <(" (8M>>>32(F P8Iن(@x>>>s(P P J @ >3p  #K ( >5 Z9 ( PP>蓃 @P ԗ% (( ( (P( PF hZ@(H( F ((  <( yoYmUګ RV)+d*@*"P|N' zMЩ^L`&pSb)FԦ}?,^QPhgBE+J3OmܕJŊbutaYkv\ދq 9c·zn7˜Wt'hE u)҈N.XQS<vi5ԛcqxwٌFNavr :|˨0?:3#N(OɊ5jsfNafYn֝+^i#qa94ϗ`1L&3oApv{@WwQfitblA Uo+V ˸gͽVÌt)v 3RypɁtGCT*+z_\详g{`A}n.8yuV[R)! erYu jf5\NGb#5dM"ԭꬅԩtPaزmdir59l!v1RJ%w IrxRi{7fOy Ar9(:OhUc:KdpU4 \Λv(tZI$4QTC"x%Bl†g6!bH?8(ؚ|P/)u5홀d3Miz%ĠtIUDX2 HhxR;XHTsrdIܱ_y3F} ]M(?ڪܭx8bYM5+n=:eʸ6^0X଺Q$ܘp<4J@ Α_`4x Hwl:.PTȘDZ:T|+փC nh5d6' ):uV9.C}M$K Y 8NO+ FciH&JG%R!k_12Y1ᖔ#[q\9]G83U0//õa蘃M.|HR|no;_ƄBf@ U,gF }i[.W1t":yyXc)" GѭxbO,82}QJ;~n͉qa&f$~r9UΘZ.Hl$ m@${T1#Z!fD$$ ە_~N]QXqJUo8Ӯ`L" @4NL$9J&`ZXľ1u˼n:hf@*>V,v+& aj! "-0Wwz@ {FtCт2=`4Q# T?<%+XQR*+UkB݇xȐf4lӈuvX%  F|S.+1MGNNL\'juwa1vLfg3<Ӓjܵ<{ǂxt.uoi^/BlHf7%INh4Z jkQpc%ͮP{E@xx|!k섑zV1O(ԩ YB%a[.@s  ы@e-)CUٟm7i[wAJu^;.骉F$. u:vASlseYorZ^{BlH67K$іh4MhjHަpI܋cg@CQ*Rw<-K?׬C.*QdJ)eL^ƭsY5D:*zd8rQBbʶqg\.WBtf]$m4ƫ^aN۩n7\16*Kuv:;W#>4 PtqT7"K&j"ZITzWJ|8bQ1Y,o@Y}kL,VFkm]s:j{ \Vy=b(P&c~n#@*QT,9M3A|x~A0!y*TM-\N(baUH"]xZ-0e } d&򆊣N D׭l޶𛷭 ۍʰ(tx]'%J{,.FCaDd}@/$x I!+M'=N>[YYq +%3-cg|,&J%Zlf6ۚۊLt :xs՗xsz=ȟXb/6Wraq ēP,ᦫs98dyF>c wsTT7"rtKʦ eJSD5K#͍g4zahNp{\yZg;w-85{}%xHX- VaLs;Ot% Hӥ1IGT++R=s*_I0AmL\@)Nh4ڧtʫm58p8\jwCw?^TOCwii}XP"}> n;HW)5ԠzS+ K Sl8':i8dEJri@Ըhy:0 Q{uʶqÑg \ZWLkzɋe3ZM$3Yձڂn?7 "S2pQ*#>σi6tNljQЃClԚY2ƜfO(T~U"q`&Z-L ͥs&ldX٦,3(G>#~㤁Ka+hN(fJ%w`c3l}M3Y@|@ Ψ4e*?%LaS*"jSȬW,|kqeY} agXdbY04O(p sq a2GMNà Ty>kbaI0LJ dŽL7U+&K<Ųiv`0JdRp5N_' 54B[[W.ؘ XfXAHp9;#r ER=)MJj:RQvof8\RIT|*ԋ }<2d3EMM|3Htz#>^^F/EnJBSG}J U[+W (E:T*fEʲeb [Q.L"2+XԪl7{3!xB6wH餋Q=.@NYFNU<YmX- e˙i8\v.Ռ#F/3'LҠk7cZ8A!tEq(\LܧS*$ZSaWz^˕բa|a1?LƌWy@ަq: %GB-zuD"Q։tJ[y3$HOzejmK]a&1L&ZS9ytZl7T1_SӽQ<*@աha;#5Kr&`ӆI M4(UٙtLFKBnm8&ΌiCƉA59(d<#[Kcӆi LШUbS潳jr:zN%҃?BӢR(D|H)F0Lާ bIQn[7ǜ=N'AC)`{>͟?HCD4R#y|H Ҡwq=֡cQ #`4w<ϡG4r D#Qi tbU/JNXT0?e2x/<՞ ҆|DHBN,VMX'BSe {>P (!21vEy#TZ12N'BjF5|APt$H, H$Ivo!;:Q;,T( ] "&Dh"DHQ(6'L&I"J.RB]tS0mH r4l:nBP(Գ {TJ*bG$@RVGb`44OBjA1:>GG*жC vcLi5͈5"UP!N$+teǘcff@i6Z";MH]Z.F =_Z 44T͜Ӕjtz$ChoSw1aFuHr^C;4,՜sua =fǠ$PjBgq"tHʉS-'`"Eh%ͨcTQ524G '%VڗBeM)6tԜuPNЉY',byzOWm>z'`TP. iAW(3%l %P :DLD](TNturN =>'TPU*BO (5%P D,(2\TNQLJ)F(QXv-w%'&MTj"PJmC (vܢYQQmJ/F9(Nan Q9GN(5ޣyhQ>G( jkJ_O5_g@jXšS9JeLt0h2Rj\Km-r:[RȪXK_+pbR VJ)WU*]TB sĂSt əWY+ZHbUTIQ-CSUS{OAU;DZʝSrNG58n߂SvjnaM)6HTšSZJjMS*'DRu ҡMT ʃ=PF@>uS|Os)=ZSw9Nө:֧UT̙UzƴrS[ Hye,D~"Vi WUXɕPJU=S*rMU/ҥTRP*DG T[ʊQ*!CΨl "MT$Pd @zS Y&+UaTUyUjEVd*ÕWJUj/U8*9Ts*P4;*TJuS[5JةI&TQ*;GUZTYQ8*$s#[{uj-28zݓZ+?ecfŅlҺmW'[+djܭ$ VHqXv+_fUsUJV@*%WUUjKU= TPU>TJS]hEL.JT*R|*LƯڽ2WI]+5pdմVYʫ.dkjU꽙Wv[bhRUʯ-U*UZPU/TxPΪ ?T EEC87pi;ًa^*N[ MgmV0ĤXj`a+}aWT\vunBVJY+,dvlVW*\zl6U -V*WWҪUMT*Rҵz[Y 3e,5* X[)`P}PmzWdjI\ëup.նV*өZ(+,>XebD=52 rX JA_+|ݫۘoӭ5fZ{;!f|̵U7˜a; -h*B^ku.eήWW"[xnF- 2qZ Kh- 5,l2̊Ys*d⬏E&3ŷr-7DfӐNEhl536V`^KJbH W_7Kz1/ULX\딅q귙h[? byk-aƴ֔фZ+=,HПN3̾wmw2+klEMeƄ /e,lF,<Ăq f` L/uW$^ vA. 'S\4moMz%)'E_3 n/f)>g y2|vEǔ+b;8W0vVPj_%kڥz1/!z_] rl45X FхѐSO>]ͳt=[8|2^!jˍ<-4vw@q+6dL)1*sr ` a׽^gw5j?O}o}̼8Go6 oq Y, śQi]IS3fg>˞N%3dst1Sv#zÖXY+` 1.]^JkwC^ !(y:w@\d+/oIǑ۶"kGAh, JVY [ c bz&ۘDs`d MH׫S|Sӻghn\˕qi2ּV6i'=fr"~+rdL}10:3،aul#0;VdWܺz9/c)QNܮیp #60׵Ri-E3–nt̮q*dL!N/'Xa{$M0C7441tz-ɸG :۪n(l捂MBf>Z >g=q2vQGY  cV ^!;˹2uNWSvvm덢Ƭ4$ZYKEh!ٛ3:F_ >$Bdv)1uv(gu֎M9V'twsnQܖԤjH3hn+3avd<ˉS&dolh]*7r.6&7vn[#eAkgE96hW*s_Ck |5v^e{`gU:ג ^7xujW_H|˯&^k{viU쒽|W*W^kz2A疼י^UɅy"S׈^kE}e56sW˪_| {W u^ëEzR龽$W*^ukuy,W^-ĵxu U WJQ_?+|56׿^۵{PdiW ^kѕz U]W_ {w2W)^+ zM"W^kEyg(5䂼|׍o^%íxTuWҊ_4+-|~UWE^+څ{)_^UרZ^ey7zWa^BMxȯ2ׄJG_g+}D`WJ_+{wJW)^+ zM"W^kEyg(5䂼|׍o^%íxTuʾWժ]_@|JW^+{Pe5kת ^z <W^L+Ux➼CWZ_~kE}W_%+E|B諾W*^إzX5n:WE^~έy1垼ג ^6+ŽxR_m]}_um _+e|z׵ZS^ֽzPzW^q+yx*5¼W^)5xeFߚ_hk}Iƾbɪ_{xu^׳3^k=zN:W^kmyg(5䂼|׍o^%íxT`WZ_ok}_um _+e|{5׵Z_^zQU钽W^q+yx*¼׎^)5xejW_}u>z_'|Guᄑ׹z^kzXꂽ:WO^ky1垼ג ^6+ŽxRׂ:s^ +µ_Nkm|ᯖ&. ^km{ku쾽׭m^%z=B5箼W:^UɅy5"S׈j^+x#Uຼ _v+e}{̺?_=|~&׷^ǫםz̯T)עz^vͥy-W^-ĵxu μׁ*]^km~ W֪}_D5|^={`f&qWj!^+z>*ח^M}x➼CWZ^x6WJ_k+}ThWZ_k {y5W=^+ezOUNנj^nk̝ym(䖼׍{^%íxTub׀_}+E}Wju_#|7W^ΫuzWB4פ:9^|+Uy0urWZ^3kex &Wz^~ ʾWժ]_@|JW^{PdiW^kѕz<ח*^Ik%xޯr=ׅs^+w5ҾW_Q+|쯘R4W!^+{l꽊W^k}zHCڼך^Xy6VW^kEx ➼KW:o_b}3nWJ_ +{ԯuײ^]zKʽWZ^f+˕yQ'u䮼WJ^8Ɲx¯UrEׇ^xW}]+̝y}-^WjA^Cx"[׊z-^!íxe μׁ*]=w֮uC_]kM}5MW_U{rױJ^kztI芼מZ^ouy1⼱W^Jy 5㎼i׌*e^(kčx~'׃]^kmwU߆| k^MzSHu趽Wj^x+Uy4uNז^Qɵy%"wW^/kexּ2ׅJ^kEx޻}]U}㯶.:_6}|׽[^kڭ{/`uvXש^+zNG芽ן^uk%y3"Wz^PkɅy!Ut׍^, x¼/ׄ}^ xUʻ}]k-zTV?צ^mz@ڼWs^jk̝y.UrWjK^Ek%x6^׊9^"xj U&ׂ1^뿽w.{ ?_t=}{{̺?_=|}׷^kםzݯXڽPW^+uz=DFמ^q+ͥy1⼳וz^L+y 5㦼lWo^(kčx~'׃g^xU߆W|jk]꫼wbtW*^ԭzMf$W*9^+ϵy9י^\yL'u䮼אJ^8ƝxrEׇ^Mx9v]+wޖWxJ_ok}e56pZ_k|{ν׵^+{ ^uvfWjA^zdIסz^|+yį6掼ח^U5y0$>׎^2+Žx:׆Z^+x#U ~z]wU>Wj^M{_붽k׬W^-zoK"W*^~-yկ7溼W^Xʍy;%ujWJ^4x5F=׆^kx.6W~]kwRw_v+e}~W K_km|!~&׸z^+U{4c&|W*^5zOU馽)ף[^5y<*ך:'^_kmy\(ڼא^;xͯ➼KWj^k¥xDu Wz]뾵wˮu»x ]E{u^׶^M{`u붽k׬c^-ztK"W*#^~]yկ8溼W^XʍyA&jW^4x5F@W ^x.6W*]k5wjw]u}¯W*_.}|c.׼q^e{vl*WJ^+zүWꚽHW^kz-CW ^nkMy0u嶼Wm^Ikȭyzf׋Y^'+]x{ Uj$׃Q^+=wr| a]뼥w5ݢt_e|ᄑ׼:[^ {kkuױ^kzǯV5ꂽEWJ^z'B5ם^m+y/垼הa^H+}x5bdWzO^%5xp R"W:G^wZW{ZU]櫼}w~ݎWtz_k+}ThWZ_E|RW^{qW u^+uz]J^W+^}zYHuʽW^yk}y55bח*^S+y+"zW:^0exu5W̺k_'M|s ^{ïuv׷^k}{a5νn׬m^+UzzLNעz/^kυyگ9UҼט^[kʽyF&䂼׏_}+E}W*_5|4W*y_ {|:W%^+5{Pg즽W^ zSu7ץ^z ?u疼כ]^f+Eyx,UF:_= |˯~D:_|uֽ׼g^k={ql*W^+z̯VꚽHW^z-BW ^nky0u嶾W* _Vu}UkW U_%+|h Wj^ޥ{uJ׶^M{`u붽k׬zc^-ztK:W*#^~]yկ8溼WӚG_Ck|ᯚR_m|75.׽^{n킽WJI^ëzZUSWZ^kzCErWj^s}_5ξz_6-|U7WŊ_ +|}Uf׺/^k]{Uh쾽װ:^k=zT*:WJ^k=z@5箾WҊ%_?+5|ЯUGǚ_k|!Wq^e{vlBWJ^EzүX5ꮽJרZ^kEz2C2z_6-|U7WŊ_ k|}f׺/^ۍ{[h쾽W^kezTB=WJ^km|B]Wʪ)_E|RW^{rUW ^kإz]^`ת7^kӥz^IeZ?_"k|]ƾ Z^M{s׶ ^+{_늽fWM^+ziJuZ`Wʪ)_E|R W ^+{rU׵Z^k{]^c׫7^ӥ|&9_ | ~~׻:E^+۵{`iU꽒W^+֕zUuV?ʪ5_!+u|XU W ^k{r׵^{ ^uvc׫jA^}|yu6^%{ίwW^+{)btWz^%|n  ^{ïuv׷^k٥{a5νqWm_kE|RW^{rUWZ^kإ{]^c:_|ֽ׼g^k={vl*WJ^+EzүJW-_+U{ԯw׸^k-{/c5y׭_ | ~~׻E^+۵{fj꽒WJ^+|,׽^kܽ{n5n׳J=^kם|75BWJ^+E{o횽WS^+|1u׽^{n5킽WJ=^ë5|U諾WQ^ {kjױ7_߭{߯y5νW^+څ{:d: W ^+{r׵^| ~WE^{fjZ^k{s׶ ^+%{ԯwW^k{ɯvuWj^+{ïu^׷^k{o횽W^k{muVW 4-M b .C 1W/] XK¶`!'H #N*h5iZ/e^2b9^'S Z9]ó`. Bz& 5$0(n / #BP'+9 ?3bmwȓ"r<* 9s($$3'S"쉘KF)F W4o!__7pbt ;T.Z8'X< kAzfe00)cb#&0BtPdC M-\ 倝% '1Ԡ71fdb\'y'rB%0<daVPd.6M8N,a ">'Ũ2 PzP)l [:8"H0.Egs ͟0e \"x%T-y hzD]d(/62H+ #@j9 <:@û.^d ?#@%ӂ!`ZTB. BdX{#Dxr;!t}W\\gCNC ߏ4 o?I * Z!| ݁n@tpJō,?Y$D-:"pY萸!$WkoXxP!]a=8Z^ʒ/ 5/`h%` EXNԁ`f1809㍉%c&P&+P@N9g=abz̅L :I$o78R @s* Y@!OHك$`oxQ7v XPB<؎'#H8Sx,ưIB\sFahb35S3hG)w\x` 4 Lި@( a21xNA)@fd -SO˸?ߎ@pʹ.tꊜBktȌ: Oa]<?!+(Dnا2, @@%]ayMT[\x  .IFzG#c&5)⿭x4#WQj7* 5vš> H/:`36 M@@%.lvkhyb{XP|B A8^FBiY+UHKp-SDLLWbi!zUG AZ݈9B `%(. 63W|0f(Z0xɂa=A-8qƀ.f U<ě9rAC\*lbJPu2IJI@f@.(0 !9Ke `F4F vO0 ~^ /(~Z(%1oFqÕa\,󉳹u̞=0F=wF`%,W =tf[|Uma*x88 ۜ% $b@Ry&eA^RXDVh6B Ep\_׆L[pK^j| h,R !.| `#Ɓ] N hUtU0X Tx?Ar@ ${&q*JTDpdTS7-Ѥ=p2& }B/tq'A( ;> 1e ~!Sg YsB @3L j]P8 S;8( Iy8DQ!XDHgeDjFlOT@.炄Uڢ2,iYca]Jd`(0,R ]kݨޱd7 g~6x(; hę:1#H3ODoլEa^ S1"C{D6A9IvUc^j<$cAZypD E|r/2> gvz `vI)}q?Fd?o8DR1D)yS4 jBszD1 L@p~9DWv)i Hߡ)"U=Il Zo e#hDL!zG%_DT_=Tp< #Gސ<oü6s,;kà 癛۰qk$@~y@ :F =}!"Ch죬:C,09s/OÒPl86,C8,Cp8{)LC>8"CD6T]Wz<}B(L*C Ϥ.x; |0\0a 9CPƍT1) OC(lp2 7Pλ3 yCCP4 0cNǐ5 M[CTՁ\5q C2Xnd: -@PFT3A硰" -cp ( dBߐ#\+ &Ӫp!.@q & 0M/ pR\0 )C ެ1K Q:/P1 oEC 0$29 C&2 GBsP, IL֝ж-ݻ tB^Pl. +;&“PF'iXKC_V.T󸫜M=fHVRxҏ#CEa@7܌9<0T z}N0D,[S޶GU4aK/clOAorHApy<eۮA Y5B!$"ǷB8$>e %BN吔4%# tsBa𰙎& UBr֐L') B o[Tp4?)^kp "@Ȱ5`A_pHAOW_!l0dby.Ao-nBBAjpxDO˖A604 mEB-"]B1l#ʅ BFِd% UUY&#W Bj' 2[ (,pӖ6"{hFZ:O<>}di_WoKژEWP~+[epsL V{@P5sg F,)^IUCA*|0b+?%zgljA90v;AP}WB sp!%0t# g;Q$O %BNd%j jB^o{L>>}K ?V8X{"?a5@T:0T2e00 KI0B4 D6VpQCx#Aj]$0A*Ph4ҭ@q\QyecHЀЌ =PB$"oB+90D#R[B>$zy .,:k** c+Zyq 9 ζ9S2=,X]{۴쓾5ϛZzC>{*3|\ @, JtPsWP"Ut ۅ P5qsg&A!pEEA?S&dp^$[@AhdҍAqI\ރypD˖A04 Z-8B4|!& "_B8Lk;nŶnȼRB4 /( ~>aO<(mO֛X4-?PF N0H :@P-!4 _?\z@翰=d`QA"8K$sT`X ןb?%uVAk4yǶs$c'tA㛰zڛ }5T!W&e0*rV1̩.sDC2E=$!mF)5zC u; T#ȇh^Z=OX[!u|ϒ*C痞^>҆hq?hvK|.?[<= BP2d '@) #@C:Dt;AxH8T]LAETJ<Aq^|S:TA/'\!9HfCs,r:!%㻚Kc/,Nϖ$z!Om|[ʐQǏ걎S>O$2?H!;_?{AM@zS"@* w/j@i0:yD;AGRdPL`O*f4*-^bM&(h>LC9S.0cgV끟A;瓛,i5\S{Yӽr ߺ[o[D^Oǽ~/h6EWp@C܌'< @P֞_6J"mJw C;TNeJβ;֡ _SOM*KܪS|%S/03?<?!Z^?0\aMP5\,.9(,%^#SPOel2CxKE.C;t?:WXNOSMc>D[ɼdE[$o)oz9t lUov>/p}wl"#/ٔMx/2)uSC3eMCh(Z78}HyΣ:ꤛլ;eì<9ì0/Y[٠*bvO dϾ&ȿ دt8؏(oF J_2 3W4nO@uCY/؆m ˙zzव8C»~ߺ>.C ;nnK/ϕH־ BX?>mcH~43PJC̽5l`w7: #<C9vNu+X9R:H|{ ȋL(lJ'=zkJ\>aV/t#X%@o 8tO2m6f[UZ׋zcX7uz68>.*k9ӦN(tU"/X!<5P+ʹ=oRBׄ y=O~JY>jCX:>FmB"kX61Mvxy_I],(;Oϓ9΍nnS@#5.7 z忌V6H-Ór@I=MXjMrnr]3V0j$k & ;?naO5K#d"l`bK4,h-ƓmL  չn] [hWb.pӔtn߻0w\/$1*ɤ=4RY w6(8nG.C ,9!n]ORH:[玨k(c=/`30ǥ9i.h+m+=.1z,oUni 0NO\s5柶(C9c5ߪjNRCT滛NW`1+7}dP @@ظPp`@أPΠHдXˈРP8 8p0p @ `x X8|`d` 0$$(8XȷЦ@PHtOY0`!x#x&'h&`X(h(8X@@|rgi`{[OrO84*@ABH1@$@%02*("pp &@XHyh0XO@ai gXbypsoO@F?98;4/000*h%x""%'$H8x0DMNXk`N(_x`\X``UHpG?pG;0+p*p--,*),/ ДЮx@pQy|p`\_M`QU U0D NX7/(&0:51.+*.8(>@PPYirW`eUHPNU7@;3`02:E3$!+8<`1 _ P|p0n8cxn8^NxTPG`GxMB7*#(h$/@` "P((@v0X c00q^sm`u`b@[XIXhH8EJB720 8 ;70!'p&8h(f `thphqpq`bN`N`THKHIx21h(.'7Ep*%h(HH%xuPj w(\XXMhS0FO8?=6>.1 +(=1`/.`p.qrp xfzp`СؕtjjxeZZF ?D8>F5 =?<(8@5@,$1%"8#pxF`Q5h ap]v0z0afnRPW^(QLDJIA@89(:6* %`80 .}[jQW}\0elOud`hw8|PnHw@je0p`epWQ(QW_RF;BA@,258+'%0h2 %I|Q5 ؂8xvz}y \lЁxzm8jhhPV(b^VP9:P73GA<;`6p93#(8$2`9=)>Ʉ}tg`z@mh|@THZfnx`c]xK[ bT H`7h:pB=@<(<8&5D`-X`"8, %Cy;Aq'1K]@[bX]jj0t@|xHfu(yHhd\P]dQOJ>PLF@@`?HxCCMF2*/1: gAPǐLxnodmwyxixrvmXfpa8aY`T`UHV(RX@UPE`KMIUZ@S^njH\PQVjh~øg@get8v(p o`jxU\hb aXpbeNNXNPJMxLRt@.)AHC3y X@)a%AU_]VUq]p馡IueZ!KIHKNQT]l}ltUa(yHxPyxf<~k\(f LxFxY8ZXHRu0A ? Zvi ʩy I1ڡY1y!ϙ1i)ɪ1a!qA!3D ISanQyԑy RIq9Qi1q" R y)a(rzqk(vxe8xГXhvpiXNPR``WhIX1Qqɑq۱yb'b/."(%%B! "Jq)"*!j'(2#*   : r s"B!j(j0435rj/*.0--.R)  1! IA!QiQQq z:z 1F2NZdXCb:B4)#R( .z/B+% *RB: Z YAYAQi)aAaQR J*r2 arh@i`ht}rБqJN2s:fJ`"j^ 9J#r*0r%RJ"(,z%Bb),%B"! RRaQQI2z J j8 /hnR:PBM0 )"#z%z& 7*>9b0.381!2: "  9q nGr@fXv8w@`|iRz5J[yBz2aO>:# j+64+J#"$#&z02?2KNJEA ;J/z" "*j y*zb!q bzzY6QDAsἡj;n2sjRduz|haS0 :.*'L:aJ\A/B5EI>15 FZUjR?*&J!R+r/:, %RJJaTiIR!rOZW:@B`_@5R="GJWSWnbfJG:ZG"TWre…oX:PS2QrIE:IPRNB5J127<7b'r r  *:6y|н 1!Yb0HXb*2m`n*Wr."B&#+z57*O"`P<8J_Rm}Z ⃒i"QBI"Qj^cb`jZVP:D6+#Rq%y*< lZjwyzeD8r84r-$ 1Y WjEBZFRdZt‚ oXQr_syodaR_Ur@)>zȀ Ej*"~*Rʎʍjz`^2T="50BjKjT:IjPjSZjB|BxZsJtzna]ji x:zodbBbrZIb=gju…҆{z"Z*Jo*djL03)2 *"`ri*TV\Z[cRwR~rib2imf_Rbbii`2W9B)ZRWd"du{t z҄}:|ګBba_B246":2[RYRU2YY]mڃjx2_rZgph[ TVV:W ^[:i{vxR*yljx2*_r^X2AZ/Z&jJ J?ROPBNH K_w~fRJUrhbskjXL]cz{}Jr*]bY*gڄ{cJ^jQ- * :7GM PDb9LZl~yzc2KbBNebrJjzZy~xkOCFUtxh"^Bj A ;BLjH KLBKljjM<>M`]JeiZS:;"B2TbnxzriY3 BhJ\DR<> PRx"2ZfjQjKQ"ZZ)Fz[2IabBj^j.8mD"*?{j5(R‡>Rk^a8tK!tVjRJVʔIZS9pT'Se"U)Ăp4KeQB"*!e.$0n9=x6eBi=f5C=w6%Q |9XT* BXX- #!pGƳ `G hP&DQ(L&X\0 $`+X(BB!B1J' .;bix4 t8 x>DBA0T,GaL!b(: p`0 ax@" "j~7IJb*!pP& P\0 ÁL()hL p. x<  P\2bQC^CLŒip/AP< h8 `l: E ‰{5 1L P$A `4 x@#[%RUo/Hx> @8 P0xB#C]uv2I> A `(`8$ B`pdR ( `0   \2DQJfӈcD Da8 @P0 !F$č "|5ICѐ>@@@0 A \4bAJZCL8Pp$ @X4  AH%UbUn.#QP2 @@0  !0d6A2H= $N A``@  T.BQ0N['S!T;C`,  `8" CaL()!R"E@T ` @P0 CF&CLUjYo/Hcq`4P `@(! T, D% "` H& !t< a@T, :Hm.CaX2A@p( p@$ AD$ l:DBA(P) x @$ x@" Bph6"! L(pb\dzYpAAH p8 P( H& l:DBA(P)d A H$ `h8Ba@X. B" DPX-#15jFeyn,CP@ `P, h8BA0T, a|@!q@V, !j6 Ap4 h8! L( p:B1 N) Ţxd3 35lFMr/CX* Bx<@" PX. a|@!Ģa@T,j6Ah0 Ba D H$ ``2 B"q@V,j7GC㩠R$PP; Ģ1|: CpP( A L& Bp`2 B"a8T+j6#|- Ģ1|< !X* PX, !p:H% XZ/ FCAp9ǃjTDV$1xT'"t8 Cp\. h6CD# Da8T+ Ef4 z>$"@E QX* !|<Ad2 at:"!H% PX- j6GC~@ dHͰUSiX(!p4ł@J#p8 t<"!H% HV,cg0JdI= ƃxT)D1>|> DB!J&"T(n1DE;TK#h1Eb@J$"B!B1 J&+6D IW)Dbx9 "pT) Q H$ Q(L' fчہX$Jq5 Œ@I"h2 PP'q8N( E-FBdNHf0 2y,D f1 XT* BXqY+咡:EgCq^-DQACd1 hZ-\mfRfO%QCj2KDJ#Cr7c!`0 RK#Ph2er@J#HDt8Qh_J==N1Z+QC r80e"B3qa.J24H"$z<0V"m(COgcd0 %b@K$2W)t)~< 9\,eq0H#HBJ#Pn5edT(ɤQ%fR'Zz:M1]-ʥ"=lT(SiDr!~<I`.bdR $b|<Qa/ +ղPMQ*w9 9_`.Vj*}2Fgq7e`Qb~=#yYTQC ^ u0G"3`:6I#Ht_-*"}4I#h{!Z+ 4i,GL*pV)IY&gXKTQ'M1\ʥ2d׫uTf+\i2جݬd9i3[w @ @@ @  P @ @ @ @ X @@ @ @$ (@ @  A @ @  A H$ @ @ A H$ A @ H$ A H$ A@P @  A H$ @ @@ A H$ A @  @  A H$ @ H$ A H$@ A H$DA @ @ @( ``X$ Q(@ @ @ @ P@`0A @!@ @ P( `0@P @ @ @ @P@ @ @ P(@@ @ @ @ `P @ @ @ @ @ @ @ @ @@ @ @ @ @( @ @ @ @@ @ @ @ @ @ @(@ @ @ @ @  @ @ @ @ @ @ @ @  @ @ @ @ @ @( 0 @ @ @ @ @ @ @  @@ @ @ @ @ @ @@ @ @ @ @ @  @ @ @ @ @  (@ @ @ @ @@ (@ @ @ @ @@P @ @ @ @ @ @ P(@ @ @ @ @ @ @@ @ @  @ @` @ P @ @ @ @   @ @ @  @@ @ @ ` @ @ @ @ @   (@  @ @ 0 @$@ @$@ @$ @ H @  @  @ @ @ @  @@ @   @ P8@ ``0`0``0 7777metkit-1.19.2/tests/regressions/METK-103/METK-103.sh.in0000775000175000017500000000070515232567717022061 0ustar alastairalastair#!/usr/bin/env bash set -eux check="$" srcdir=@CMAKE_CURRENT_SOURCE_DIR@ bindir=@CMAKE_CURRENT_BINARY_DIR@ wdir=$bindir/METK-103 export ECCODES_DEFINITION_PATH="@ECCODES_DEFINITION_PATH@" ### prepare test for f in corrupted ok do cp $srcdir/$f $bindir done rm -f out aux $check ok out diff ok out touch empty $check corrupted out diff empty out cat corrupted ok corrupted > aux $check aux out diff ok out metkit-1.19.2/tests/regressions/METK-103/corrupted0000664000175000017500000010402215232567717022003 0ustar alastairalastairBUFRb!4~hbDif7anA.d jF Fr (!E,p @ ` 0(bP @(`` &(P0@@ (Q0D@ 0`@ 0(`  $J0p `8 pH`:8xp $J8 ` @ H` @@ `  .@`` >@ ` &$H @@ $H(X A $"HH!@B $2Hh!C  $BH"@D (P @P  (P,`@ ($PLAP (4PlA !(DP BP ,X(``  ,X0h` ,&XPa` ,6Xpa ",FX(b` 0 `8   0`8x  0*`X  0:`x  $0J`8@  4 h8A   4h8xB  4*hXиC  4:hxD  $4Jh8 8 p8  8p8x  8*pX  8:px $8Jp8„t)!ѤCHJ":D:u)!դCHZ"zD;v)!٤CHj"D;w)!ݤCHz"D<x)!CH":D"D|T#$GH&}"MD4Ih$$IHL&"MBD5Ij$$IH\&"MD6Il$$IHl&"MD7In$$IH|&"ND8Ip$$IH'"NBD9Ir%ϤKHB."]DDt%ӤKHR."]ZDĉu%פKHb."]DDv%ۤKHr."]Dĉw%ߤKH/ "^DDx%KH/)"^ZDĉ hR&$MHH6"m2Dt jR&$MHX6"mrD lR&$MHh6"mDt nR&$MHx6"mD pR&$MH7"n2Dt rR&$MH>"}D4R'$OHP>"}RDR'$OH`>"}D4R'$OHp>"}DR'$OH?"~D4R'$OH?%"~RDR($QHHF"2Et5 jR($QHXF"rE6 lR($QHhF"Et7 nR($QHxF"E8 pR($QHG"2Et9 rR($QHG5"rE:4tR)$SHPN"RE:uR)$SH`N"E;4vR)$SHpN"E;wR)$SHO"E<4xR)$SHO%"REF|D3gHT#MbFԍ5k4$iHd#MFT6m4$iHt#MFԍ7o4$iHӄ #N"FT85$kH\#]FvI5$kHl#]FwI5$kH|#^FxI5$kH^#mF$im6ڤmHn#mFۤio6ޤmH~#n F$iq7$oHh#}Ft R7$oHx#}F R7$oHh#Gt7 nR8$qHx#G8 pR8$qHl#G;wI9$sH|#G<xIm:$uHt#G[Ԏo:$uH넑#G{i;ޤwH~# G|$7In<$yH|#G8I=${H|#GxIo>ޤ}H~# G$?ߤH $ H8) pAःI -!݇BD[~X%`{(BBD@x-?-}܅:DB"q y )<$W!;# hZ>[vtDO9#?p-PT^ &Z%G ^!!$\{ A HE`g|I! NB~Űbt6!5F8 zve )zmD@ZŃ7Š@KԂxTDL55ը|v"Cm UMBE. ifgK$pP,":"!TS  mP!_#h ސULQX9A5a$10 HRe«#F<0 k #q@NGv=AAq\̤7"@'gk sH"/ AX(~ X;fAa5z%_17GS¸J0fn1 1lc}pǏ> aI$\C*Az py-$| {`30)aR dU2ׇ,A%Qh(zP.Z20hn41 XrCBJA*5T0z:p'UO na=Lt  @BgD'J- X"نPh4 ȄqZãCT@iP .-A{bD&aIe x^C%qx> d0>&D+hW.V873KA oc <? | >`z]p* z28$=b `[ P߇Al=a 8&;>S\Ub(  *2hnSËS!5>z Usn!< ng RYBÞj8{a^@YL#+Q ˰Վ061iCf`HJlDD0`0|3`6"<*!m# -gCm&̈8C1TO):V#[4wHFt5ގ)dtaûW (A"A}@$;1S@N׼ EB_Q25!,ṽ8 Áj\Jbn9Xp-at͌c]3cxB9hס||b I Hf`! x*b yJ+$m; [)  :<،5Ē&a=I LUyBؽg2pލadn(Ǩ(>"ːyDO@ɀBtuL`1h?- /! MxpCj1݈GBUvpE>b+|hA x`7Co71x0ȹlU4D4.0 ``cŠ$;R l$!Va# ڨTضLPΗd9Á`$0TRV"4 'B0R jpKu=;2̃6"|!xF6?@@mP9xk`(+x^ @jL8Q-\Dx"<]ț_ 0)A[@]#Vϐ̩Ɩ6AcVPu#( H"B!n䎐nAX6 T5RxHBvF8е3a5ny!t !LyتE -zk 5d>:2M0}} rF~@^f l$'5j ׀rh"Ё. 90W@g;:>VЈ݄w%IQ6lS|@F1q +lu|{yG4=C:DB"qIRf$%_.V>b v0&ABwLV$>#2"3J o㔳k@Ǿ?}"CTG[ #sdN#9 IRldʩ&8IO64ɤ$*pLFhtu@67(*ٔNNw@EB\m1QvU cJ|lP'`blz,sc1{PعF49D֛H{#n HȃE94d%*,jI| Lk{@E(FEKEF<lSd%[I-InM nx(waFH.٭(BD` 0vA 0s<ʴa 0(H_(HESz,$m Ψa$Փ48|Bz@ 9B8 m)'Tj ڠrh"!. 6WBp(;$OȈDu%96S©E"/1 i8" DnF:35jyo!fe /a IpZD1.TG_B mfC` ZHE߁ Opyôp'T3!Hrpy!pKj>] %94 gU 2}` h&/44 N t @TP2ԁ ڠg9>x $ 9F& @$BQx"E{_ &gA44 Mdmu@(D (QY,\,e: .Y`F-Cj Z@Z: .u> ]pfgZ2 fK3$xn3 gk<υ~4 heC%`X4wA "i ITҾH4 8`iN|)9_bs/D`:9Ϭ~tH:MtTH`T:Ո`uhn`]:v0?8 YX@/Bx 3@)X _p@9 t ~!@ABE_#_ĈF*1Pe#1axF4B6j#SF6`m#sF8\p0q#!XG19Ȅp&>2' LBd(R&Z3 Lf8P&z3 HMh6C@&4&4 M,BiN0~&5 pMK॓-)k!Kr ] Rz`0 )!L! aS062)L eS-™4l)aM4 iSSB`5)M lSfHJ@^,d 'xY;P,!e: )YNV,e ,Yg<[pۅ,e .Ytˠ^􅗤,!f 0Y6/́~l `_6/́~l `_6/́~l `_6/́~l `_6/́~l `_ `l3A HeC/}^2ᗞ eC/ xP˩]L2a: e΃.Brː\ 2 @e-nw[X2 e-m`nj6Ĺ%q.& pKv[ڐ%1-8hKDYؖjD%Q@P O`2'>hO~{sװ'Q TfBਚDp*!Q T= ثE\*WU"n,f .YWBL?E,}cXŒ(ň,9a ,XD h8+_ΊWڢq0vz+1^pXWBݨڅvp+A]]WQ"pҸtJ+\hX5(E,g1c- X.qN.os h\"#,.phP[xź-n)oX[hrE-lB `Z?ص&-\jTZjED-3i KhZX"Ҩx-!!hGZ8ѮxŽ-oUzH["GpE-1n `$ul/Q~__l"N|/a{ی^Ƃ@"/5y}ɔ^@"p(L.w]̂npZ .!v8 ]x"vZ .uc]OJQ.at 4_ By˅*/o{X`^fh6/`!z@cscDF0`1xP W`b#aXĺ$1{ Aa#s`oFN0ʡ .\a`# kP8\F0 `c2 v0Syh`v" k3OfK1^2!Ɍec+;T`qR\2a dc%*0%Hf2>ΌdQC! >@2 |f Osmt4c 'i H<;ў4N hhXA !Fz3< gc<Fu3QmCi ?ٿ<6RA\lIaUd5ޡ qkrCZHF5|1YjU PƦ89Dp cPW77 4ocw|P7Bṳ n:q'6! 4s8㘴@G+(9HɷIr2c|8-LqQcG8mQiu҃cpGT:qOtC'HF:%пT!yunEb-lfLV"Y:G:b9$èn`$di"pHQd@YB"^p  @H=2*p ` @ K2v=p@4& @X.i@>%:H8` @pX,d2 `xq0;`rnEA(0@Xd9@H `xt>! 0Xc \`/ }@` D$ % ` }I% @z@K`*h   h5 2l M(( F[2 h,R`+8 W. Rp9x|#+A% PO( d2@XXWW1pKI>@`2   z>hlO3Qh*0t}@@% xp=!XH 3Ӏk8@@`D0" `EW+X}J`' "!@FP&Pd @Ԁo8@@Fx# IT+~ K@+8  Lp)h X, p8!>+A J% QT`* S+ x|** V,p Dqfq;` H<'M' 0D\W*0K+ &,V-h 6@Q@t;`$`N (DP gFb@@Y`,< d@>$0 8NM@*` @@s=HfpG% (B] PZ-P& T^ XJd8@p `wg7 @A!t$X8T`+ OAwZ- i>   K(x @Q@x>@h]J*A! @I%h 8g@@[@.p ja2H"W0@IXE]2LND#L `n8RH* gw ]-X  $ (x |DP%L `v>ph~W?%`N '\P mRj`x0p8 uc3  KY2p.9]1( h AG$ P@0W-P* `.p H9j@ $@/{`?0"h .x`q> 0~]; Q@) w`2 !we3 H~Pd`8@ 6&,W. H K% (\`0[H. 2@%Ad0t ?nKA j@5 }@"` `o@j@8@~[A8ࢀT`*@ gÂAŐd3X r9c3 փk@|C%&K( @)Q}D$ )Am^00( d<Ԁj@5  <j6 +B;A㋘:{}]@@D" `^_P3H*5+`S+ ."Ұk62 ̓X@d2 kG&F>lcR_=  D"<;%s W,hp <OTÀ }A$n ^`3, *py;` '@g 4* щ0A[ HQ x{j 7 j @;&S*0 TAp;. p5 RF`Z0. A E# ']@]0t  }=<YRa2 9Ƃ  K& |sl``2 EփPx;`@Ev>h 0qE@W-PP  Ԁl7{]}A"` |] ]8. |hMo8@tbX;@R+L pj7XX ;ࢀT`+| (Ґk8<46T`Z. rR0p9L >`@h64z,Aເ`2f H΀v;`j @v;r `w<L% {>@|> Q梼=Ύ*owHFM1{ عe,cY]?y+9M*Ow )4N;\%"E+p%95 ʎvtίHj@Ty C4lThz]HI6Rs=|9a`icIG:q,$&K^*BR6ea-݆n{'82tF .qj8Z7􈤖'iRbaIFM-o.A;X9 T Y4q UHRI<Ƥ9Dp,Yiۊ^E3Y: C:B.@O;X0٫N~K%%A ]凪H RB `U \ k'!s ~)awB@;1NiAJ&`9'øZ[d„aL O @f*JB SBHzKF<RE-y(pǠSA;4AL1A$$!@ I!) @kC$S m"4^s?|xI~UBr(v<k8 @:P @L`$P668a̞~0ΞY%GK _[5wC;} H8AJ0&@X:&N Z#7)0Qՙ'AaC D:2i$O "ޗ5Қ=ְPz5h4 15$eU/9L}$CB*^*>C< Z.\*X{ɶ~-PnhntfM F B < @p38 ^!9Lgc! ( L[S%hEHq((OA\A3Qͳ˯ʼnhs\x|: !S9=pj[k- [BR_` b1 H[Cn q/ d#  X F`&3`w _80!?@6h `? `0!Ah5F7D+A~M s5)J Џ𯄤)A*CH XZ`&`lT᧴`P%P5A c|!!= n%1Tm")/~\s,Q<n"A PFjʇHBcÆ=b"}Ì+X ]( OD`B!xoCfPh?.04!͏D 8!qIbrF6x$)!S ^$DV `F7$'(G 8{"9"R xR#+S̘'I Yp-YvL$eC fj4 pG[7g,0V˜scO?-E\˲JIC*eBX1l.szur E 4(\ i ͼgdy\w?HA$B|O D)l&j!?u&Vc%$Ro3F؄DF#"Z6Tdp Yݐ{QfDKb*@b\Q8p{FqC+e{Њ5KJT-Z|u/^8$ :+ kRvݝ3Հ4I? `!PĸrZy;h Df yƻbtST$(&AP: a|:H/#䭗FrOH)JƚCwtVCh&dU`@ plpx\ 1CqO.&TʃUiF^1JBKiG*}ivrW#k `*!(.!'$rby?h%E<#j =UJa ¤:e˼X 0xPwLQ !50~] Q:_->`JIZv6}%FX;=m2& '"x{RQ!4 !pB[!6' tX:+R+ {;1M;i ۾?dL X)@e A2)czr3KJ U-EڻvOD(J[?nnl1U{cr-8'f<# DtF YA+ƚ{r2K S,{{u/^Xd+0BeBK ;i$ȡB_ɴ:G "4jSYՏ0l a9%PT k/D#~:KS,D@0J X/!D]4"dPh AJ*pTt2MJj/4xs)B(& ƘW4&3~)Hi}>0JV-3vOQB(dJk0Fdc{ E)8hD0UMJMc|P2E,&UAH}$:Vk)ƝbV5r&BZkb`qhU#:cl5ݠ+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> *>>>>>>>>>>>n >>>>>>>>>>>އ X2f@>>>>>>>>>>軲ϭ>>>>>>>>>>>觀 >>>>>>>>>p>>>>>>>f>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> %]jo5u}}>MBHPZeo"+6C7,$ 5"U2g?|G7/̌$,lԄ'#:/PWe{gOT<| 5 # ;F~ o!8OWe{ ,K>h6' &>Vm>s873:{B;Nc^crӣ߃",DȟıլUBEe}5F V-&Kjn>VێG/)B7Zc"JB 1+I;d[{c,3YTE> buUm.N>lv'_-FКһbɲ;"#Ad sC;D%NK^xJ;2S^ ۷DL?dkL,m=d͊me;[Fz>ӆO $>`J>! y.Mq kPïߔ=mȜMHMp]E-~(IifŖ^/G3gM&iukn9|Iޢ %gŐ-$GhF.fV/4Ny]a"x`hY5Aqɯ-lB;![Ô\ 4g\<ǜ=!Lu=v1StnN $?X!PhmMR{4IvٹA<{Z3mݤPW LҲUJ K+#([čllD=n~+Mo&N>NW ;_UOn?zןhGtρ'7_i0XPЦ 1Eل!âAڀb5sn+Fx׽1[}Ŭ-ҵ>_^^W/IUbo{7ߟP\iWu/OǰƯљ`aPq8t*$]4tf<$Jt%~ /Qr>.#'=Wp|Oww̿W^_jwW/צ7ҿݏ1lYF*Y )Ek1iʪ27o#R%W};eE="DNevw2wL_erG~ߕϡ7'7o`myogǜw7ɇ_'ygI`ewɶaJAtZ KD\K{T.X}͕F86Yvy>nn ?(BAɧѱQ J(TZjk;K~ [,|C|rĠʹ$%M=uu=& -No z9Qjb)J:o˜: P 4>m$TDeDel]^ `J:2R1R7DXrۣC/c[ÈCl>Djܖ%m=e=:x2suZ}ZjCl+[4\Al\9`M2ҹ2+:]C#$Hql 4M7]k*+A\{S ,S,y|$==8X k3C=taL,δM;kk1Qrt|L  T40Khܦ\4uE(D$d8Oh<|ܺDm54,q$ܞlє%=BL]7]R35Kde!]5JaxQdx͎}XLz\z f-D+rP]_EȪx\K 1(= Tf:Tef-E.$h H+EbpU ild.F_S?:j9͎&HE,iQQf+кc.e)96D$>3)E(%O#U$oK+ (Ws&dь6M3wa&Y>H )=tЕ"Ck}e"kT2WB3jFƈ#Ԁ\Qkʗ9_-D:jM8={@8H\-#1D#hϪ9+E#v\z2BH$mU6rV0f9ʹSgoNN9Ӷ;'Y#nMj~Bhuv"6ONQ{22FH5G#4")R8IhIs6Ⓖ4[yKA3HLӗuA>8m{|RDs蠁-#h(Qb>H 9#;Te}QJwDDneB`Z6̉3"枤گ#ZvQO>1Ϡ4&&Dz#E#V4r("ZBHI!%ğRV J_ !B"X 12&(Asw-tT ‚P^B=` J&vEP%a|vckP._Ga %&%N4"Zh(Msa8:4릝R{FO?( 5ZPjeC|HNQ PC \RLAמ@@}06R:eSMI 9'SDﺞ"~BO*AoA ;GQ# 'ET%iQ*:G5 " sd4/ȬCڏҔ_M Y:&tN PD.~TeQ*<G)!UTLRWJLI)=(`"'R*VJCg>Ei|}95 TQ٪A!H&&ȥ T^RYAK^re/_ScL)3l*SFJiaM?5T#ShQPv4ַkVj V窲RƪU6RTiR:FUoTE PEBX 6T$ PwEA2TqPKA Uڠ{Tj}Q*0FB' troɶTp5Y7_+sBUVCíW*U[EbrUcJURn5CTYSpuM3j6TJyR*[KZg,2vU T3*PjU@2^9BGWߟFc$ras+vj..4V}UXKa`zzV؉Z+A5fXV*qW\,fiRU*IU*VĪUXҪUZjUHTqUV*eZT@fꬱUR`6C~g2\gl 0/^6:}[aKZ9i:eN6ĹXWK!_+y:U 3WJU[B[j՝VQ Xa2ՁUYWE^uVm ̙Yu+*d⬎V2 yXy΢|RΡ~hai{(.&VZPqh5xArUX `,U~2/Whj\+pLU8V ֥Z+Ri'V|*ΕY+3f UȒW [xntյoVŪ׭Z׫W%jmXbB#M}O8ߨ̚K1dbl[E;,^|x\ o-ƵJVbkY+1eR%{XJ)`}UﺽWq &7ۨPKqϭ4y"˱^a3/5rWZl\wk o~%p֮ӹZ7K?]gȕ%y5(O=-Pdfj+2VDYX )`yK|qBUD"L\H ~ny%IԬ"f(YL:&k!kim4Y6RV2ȋЫa,ဈ/%]*;–&_++w&.țFފ#ZSG;gʱ5<p`s}hmC vΌ86)Z9^fṞ ϥ,7 fkSm\PV{Ϝp }:ZKHh@K5[/js1}8Vݞnek-Z5w7 }gl .K8&^QN upHM]e8/&` 0+$V`6D +$V@}د 1$lP@AE`T_k~u(`6D(l]~;Ujߚ_vmJ !Pz@4Dn ~_w@pxL@ 0"{6@pQ p!n `j'xy H!6~%•02hWH s@ 1rHhD] (!p]_+~ 5*@FFhp `0 @\FX R Zk_k~uW)@;D<0m .@FC@ R @G WU_M} G'@ Xe k*;hD0@&W_u}X L(0@%Anh@ `v,(=G1`.)_+}Z R(00C@k -`T&?@`M@@h7@W ?_}[` W);D<0m .@<B`OBlZK_+%}` h+H\H @v2P~J@X` 4!p]_+~ 5/@xP` (#J9ЀX P4# y -@ ) X-~5ĵRW:@\ @]*@FFhp `0 RDU @G U_M}`2 U dA&(? pBc `)@7X<<0`ֿJ_{}e \. O &#4p8@U@ )"`s `* t#pB~*U&ފȀ`\ W)@AD<0m .@<B`O<lZK_+}40@Q 1$l ;`Z ?% L~p0P.pn@Ujߚ_v+8 ^ e+@2hWH  s@ 1hG@ ,Z @@R@ Zk_k}vU 8<&  =`,`@ XU'5@7  W_z+}ǯ{.M \ "*H6`R !p j)@xy H!6k~%•W_p}]` T_*&LF@r `0 RFX M a_M}bzM_hk}nWذPS 9%p= Lހp"] O' p24 Ƶ WJ_x}¯5ٺ!_b}j_akZ T)0`6D(l .h0@`M@@h7XW ?_}读Wzc_k+ }.ת_o+}nW _`+}o\ (Z)AE`To@ 8/`FC@ RB lK_+%}uJWY_hk}uWZ_Z}j!_b}z.HO (#@9`V .#v ,`&L-~0>W_sk}j_ak}u՚_p} _`+}uUՐQ .$``:`ӀY@ x9$`4~p.P,pb@]~;UjW_{}^ l_*&@8QG`@r 0@\G@Dc P>"@c @`S 9%=`p"^ (U'4@7(R` 3%v@x<`4ۀ] J&x 2 h+H\H @v2P~J@X` 2hQG`s@ 1hGh ,1$` ;`Z D%`= 4ۀp"^ (O' p@9`V .#To@ 8/`R9`V P4= Lހp,`@ XU'pBc `)@`,`@ XU9ЀY@ `0Ȁ ,╋j'*.~ 5(²޸6/gc i1ȨV(ۉ8'$0bq G0&)$Y[*G@ &o (/# d(/z'L"Gr92 & L:#*)H>. B5PJX& 7Eb1$Z*)6 [i- #RDtnBX+K Os縕6#mhPy&˔h0  O(&: IB @!Wz X ,* ~;}\(P#"wyI@paoaIq@M8{kan ?DD3L 1o}H) `S@"wf5ǁ w9h1 )bꨫ (x։bBHȈyb /ahkRqzj;xUq)a DηX6 ,R u{j`x#"~zBMc`FX͆60 ºaX& "Y%X.Z!>Axd@!NΈN΂]u>tX1Q …'m S*4 {ŏkIe O`ȡ/\'l$ @yV cA],ZIa2G[i`Px8 +`,H ^Pp`R"z/W tyy@[@6_ OC Pض*)1"VH <ïi-&!T(O ̈́Uo<\4SU@}@. {$Fx)6(ŀ]WH"H3HJ8ʍ_c#t+2Sb^a` a gzmaKt8L(@9v o!&+ 3fae[!Bh3`l(abb @R>z =&;#v WC?| ,hbg Y!Xf$RPl!@(J p~6LH (L [,jl4`la4/(3@G%HvJ߲@3]In=x|c`iL.B} Bp@@ &{]8d <>a3hF8 ۛ2 A@@$t sU"j Sj-g;$ (L E(F-54.yl[T$&2Jێ@kFr!lBxIWvq`ް2: fHH#.Eߔ@m !OLo23 h 5\/Ii$cAО@b吣kg-&- ,Id`KLoT{CUB1΄Ri@8i &Ű& ~qH8OQ'`L&>z@1 6dנ+JJsCR F{&Uoydް%DԌÝ)8. qQB݆ll-PP#`36 t1$ / d^VƁ j;$ <j!1}MIِ{b*JZ.4EsQM; NIir$>\ X7[DB< }V?2!SX3) C|8`6hxҧT}X ߲@0 5%Qq0̀qB6## @a~Ē!GethpYbv@eq&"BKc>H&"+ϡ7]߄Ix2 _]@PhO`GH0"1"T= F( D#G}De1Qj,DD;Q B`%rD>IkD#RH&ʜAӰVfIl)׬DZb8s;ܿ,dI _DF!G2=zUq0CY'LAT53+pj4?j{Ah8~RP;3'raK@\x,.85< !] hu_ DctF;>Q lB3][| QI\?wPl=ICNp$<÷FVAr^[Hdh;([:aK&–M*)M ¬pw+b ½[T,R !B0$- s NpD- pel."e BP$, ( B0"A< k'X;J p;"Ox6Xƽ5$z ZE[T Pv;B"s",BBJP%Y xeM$'B  С(; C•)Y .Bpc*a BU\+/ "µ䰤k)=E V—PLd)# sžDt2~K{<+/Y[{qOsW@6OAJYsbAo36h W 9#<D*d%3 gB`}& Bs' }Lpt(q …%w h]ap&' g,&i BBq:ʰK. qS M*lǨ>D=P ?e1E@s1T.)vpQ#dɬfSHARu>h0&\ _B 9t",B= $v/ )XMI %. W{BXHЖ% -%R""qB2|#q{?Pd$O BJV*ě*?0LUXF݁9.6S=1sSXx?Oٔ+"cP »8WezA1Q<Ametkit-1.19.2/tests/regressions/METK-103/CMakeLists.txt0000664000175000017500000000027515232567717022616 0ustar alastairalastair if (HAVE_BUFR AND HAVE_BUILD_TOOLS) ecbuild_configure_file( METK-103.sh.in METK-103.sh @ONLY ) ecbuild_add_test( TYPE SCRIPT COMMAND METK-103.sh ) endif() metkit-1.19.2/tests/regressions/CMakeLists.txt0000664000175000017500000000017715232567717021556 0ustar alastairalastairif (HAVE_BUILD_TOOLS) # test scripts use the metkit tools add_subdirectory(METK-89) add_subdirectory(METK-103) endif() metkit-1.19.2/tests/mars2grib/0000775000175000017500000000000015232567717016336 5ustar alastairalastairmetkit-1.19.2/tests/mars2grib/backend/0000775000175000017500000000000015232567717017725 5ustar alastairalastairmetkit-1.19.2/tests/mars2grib/backend/mars2grib-test-search.cc0000664000175000017500000001767315232567717024362 0ustar alastairalastair/* * (C) Copyright 2026- 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. */ #include #include #include #include // dictionary access traits #include "metkit/mars2grib/utils/dictionary_traits/dictaccess_codes_handle.h" #include "metkit/mars2grib/utils/dictionary_traits/dictaccess_eckit_configuration.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "eckit/log/Log.h" #include "eckit/testing/Test.h" #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/sections/resolver/ActiveConceptsData.h" #include "metkit/mars2grib/backend/sections/resolver/ResolvedTemplateData.h" #include "metkit/mars2grib/backend/sections/resolver/SectionLayoutData.h" #include "metkit/mars2grib/backend/sections/resolver/SectionTemplateSelector.h" #include "metkit/mars2grib/frontend/resolution/section-recipes/SectionTemplateSelectors.h" using ActiveConceptsData = metkit::mars2grib::backend::sections::resolver::ActiveConceptsData; using ResolvedTemplateData = metkit::mars2grib::backend::sections::resolver::dsl::ResolvedTemplateData; std::tuple make_ActiveConceptData_from_PayloadEntry(const ResolvedTemplateData& pe) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; ActiveConceptsData res{}; res.count = pe.count; for (std::size_t i = 0; i < res.activeConceptsIndices.size(); ++i) { res.activeConceptsIndices[i] = GeneralRegistry::missing; } for (std::size_t i = 0; i < res.activeVariantIndices.size(); ++i) { res.activeVariantIndices[i] = GeneralRegistry::missing; } for (std::size_t i = 0; i < res.count; ++i) { std::size_t vid = pe.variantIndices[i]; std::size_t cid = GeneralRegistry::conceptIdArr[vid]; res.activeConceptsIndices[i] = cid; res.activeVariantIndices[cid] = vid; } return std::tuple{res, pe.templateNumber}; } CASE("Section 0") { using metkit::mars2grib::backend::sections::resolver::SectionLayoutData; using metkit::mars2grib::backend::sections::resolver::SectionTemplateSelector; using metkit::mars2grib::frontend::resolution::recipes::impl::Section0Recipes; const auto payload = Section0Recipes.getPayload(); const auto selector = SectionTemplateSelector::make(Section0Recipes); for (std::size_t i = 0; i < payload.size(); ++i) { auto [activeConcepts, expectedTemplateNumber] = make_ActiveConceptData_from_PayloadEntry(payload[i]); const auto& sectionLayoutData = selector.select_or_throw(activeConcepts); eckit::Log::error() << "Section 0 :: " << i << " expected template number " << expectedTemplateNumber << " but found " << sectionLayoutData.templateNumber << std::endl; ASSERT(expectedTemplateNumber == sectionLayoutData.templateNumber); } } CASE("Section 1") { using metkit::mars2grib::backend::sections::resolver::SectionLayoutData; using metkit::mars2grib::backend::sections::resolver::SectionTemplateSelector; using metkit::mars2grib::frontend::resolution::recipes::impl::Section1Recipes; const auto payload = Section1Recipes.getPayload(); const auto selector = SectionTemplateSelector::make(Section1Recipes); for (std::size_t i = 0; i < payload.size(); ++i) { auto [activeConcepts, expectedTemplateNumber] = make_ActiveConceptData_from_PayloadEntry(payload[i]); const auto& sectionLayoutData = selector.select_or_throw(activeConcepts); eckit::Log::error() << "Section 1 :: " << i << " expected template number " << expectedTemplateNumber << " but found " << sectionLayoutData.templateNumber << std::endl; ASSERT(expectedTemplateNumber == sectionLayoutData.templateNumber); } } CASE("Section 2") { using metkit::mars2grib::backend::sections::resolver::SectionLayoutData; using metkit::mars2grib::backend::sections::resolver::SectionTemplateSelector; using metkit::mars2grib::frontend::resolution::recipes::impl::Section2Recipes; const auto payload = Section2Recipes.getPayload(); const auto selector = SectionTemplateSelector::make(Section2Recipes); for (std::size_t i = 0; i < payload.size(); ++i) { auto [activeConcepts, expectedTemplateNumber] = make_ActiveConceptData_from_PayloadEntry(payload[i]); const auto& sectionLayoutData = selector.select_or_throw(activeConcepts); eckit::Log::error() << "Section 2 :: " << i << " expected template number " << expectedTemplateNumber << " but found " << sectionLayoutData.templateNumber << std::endl; ASSERT(expectedTemplateNumber == sectionLayoutData.templateNumber); } } CASE("Section 3") { using metkit::mars2grib::backend::sections::resolver::SectionLayoutData; using metkit::mars2grib::backend::sections::resolver::SectionTemplateSelector; using metkit::mars2grib::frontend::resolution::recipes::impl::Section3Recipes; const auto payload = Section3Recipes.getPayload(); const auto selector = SectionTemplateSelector::make(Section3Recipes); for (std::size_t i = 0; i < payload.size(); ++i) { auto [activeConcepts, expectedTemplateNumber] = make_ActiveConceptData_from_PayloadEntry(payload[i]); const auto& sectionLayoutData = selector.select_or_throw(activeConcepts); eckit::Log::error() << "Section 3 :: " << i << " expected template number " << expectedTemplateNumber << " but found " << sectionLayoutData.templateNumber << std::endl; ASSERT(expectedTemplateNumber == sectionLayoutData.templateNumber); } } CASE("Section 4") { using metkit::mars2grib::backend::sections::resolver::SectionLayoutData; using metkit::mars2grib::backend::sections::resolver::SectionTemplateSelector; using metkit::mars2grib::frontend::resolution::recipes::impl::Section4Recipes; const auto payload = Section4Recipes.getPayload(); const auto selector = SectionTemplateSelector::make(Section4Recipes); for (std::size_t i = 0; i < payload.size(); ++i) { auto [activeConcepts, expectedTemplateNumber] = make_ActiveConceptData_from_PayloadEntry(payload[i]); const auto& sectionLayoutData = selector.select_or_throw(activeConcepts); eckit::Log::error() << "Section 4 :: " << i << " expected template number " << expectedTemplateNumber << " but found " << sectionLayoutData.templateNumber << std::endl; ASSERT(expectedTemplateNumber == sectionLayoutData.templateNumber); } } CASE("Section 5") { using metkit::mars2grib::backend::sections::resolver::SectionLayoutData; using metkit::mars2grib::backend::sections::resolver::SectionTemplateSelector; using metkit::mars2grib::frontend::resolution::recipes::impl::Section5Recipes; const auto payload = Section5Recipes.getPayload(); const auto selector = SectionTemplateSelector::make(Section5Recipes); for (std::size_t i = 0; i < payload.size(); ++i) { auto [activeConcepts, expectedTemplateNumber] = make_ActiveConceptData_from_PayloadEntry(payload[i]); const auto& sectionLayoutData = selector.select_or_throw(activeConcepts); eckit::Log::error() << "Section 5 :: " << i << " expected template number " << expectedTemplateNumber << " but found " << sectionLayoutData.templateNumber << std::endl; ASSERT(expectedTemplateNumber == sectionLayoutData.templateNumber); } } int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/mars2grib/backend/CMakeLists.txt0000664000175000017500000000024515232567717022466 0ustar alastairalastair ecbuild_add_test( TARGET mars2grib-test-search SOURCES mars2grib-test-search.cc NO_AS_NEEDED LIBS eckit metkit ) metkit-1.19.2/tests/mars2grib/utils/0000775000175000017500000000000015232567717017476 5ustar alastairalastairmetkit-1.19.2/tests/mars2grib/utils/CMakeLists.txt0000664000175000017500000000004615232567717022236 0ustar alastairalastairadd_subdirectory( dictionary_traits ) metkit-1.19.2/tests/mars2grib/utils/dictionary_traits/0000775000175000017500000000000015232567717023231 5ustar alastairalastairmetkit-1.19.2/tests/mars2grib/utils/dictionary_traits/mars2grib-localConfigurationTraits-tests.cc0000664000175000017500000006005115232567717033601 0ustar alastairalastair#include #include #include #include #include "eckit/config/LocalConfiguration.h" #include "eckit/config/YAMLConfiguration.h" #include "eckit/testing/Test.h" #include "metkit/mars2grib/utils/dictionary_traits/dictaccess_eckit_configuration.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" CASE("Test generic get_or_throw valid key") { #define TEST_GET_OR_THROW_VALID(TYPE, TYPENAME_STRING, KEY, EXPECTED) \ SECTION("get_or_throw<" TYPENAME_STRING "> valid key") { \ using metkit::mars2grib::utils::dict_traits::get_or_throw; \ EXPECT_NO_THROW({ \ TYPE expected_result = (EXPECTED); \ TYPE actual_result = get_or_throw(cfg, (KEY)); \ EXPECT_EQUAL(actual_result, expected_result); \ }); \ } // Used symbols using metkit::mars2grib::utils::dict_traits::get_or_throw; // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_GET_OR_THROW_VALID(bool, "bool", "bool_scalar_var", true); TEST_GET_OR_THROW_VALID(int, "int", "int_scalar_var", 7); TEST_GET_OR_THROW_VALID(long, "long", "long_scalar_var", 12); TEST_GET_OR_THROW_VALID(float, "float", "float_scalar_var", 3.14f); TEST_GET_OR_THROW_VALID(double, "double", "double_scalar_var", 3.14) TEST_GET_OR_THROW_VALID(std::string, "std::string", "string_scalar_var", std::string("abc")) TEST_GET_OR_THROW_VALID(std::vector, "std::vector", "int_vec_var", (std::vector{7, 6})) TEST_GET_OR_THROW_VALID(std::vector, "std::vector", "long_vec_var", (std::vector{12, 13})) TEST_GET_OR_THROW_VALID(std::vector, "std::vector", "float_vec_var", (std::vector{3.14f, 2.71f})) TEST_GET_OR_THROW_VALID(std::vector, "std::vector", "double_vec_var", (std::vector{3.14, 2.71})) TEST_GET_OR_THROW_VALID(std::vector, "std::vector", "string_vec_var", (std::vector{"abc", "def"})) #undef TEST_GET_OR_THROW_VALID } CASE("Test generic get_or_throw missing key") { #define TEST_GET_OR_THROW_MISSING(TYPE, TYPENAME_STRING, KEY) \ SECTION("get_or_throw<" TYPENAME_STRING "> missing key") { \ using metkit::mars2grib::utils::dict_traits::get_or_throw; \ EXPECT_THROWS({ TYPE actual_result = get_or_throw(cfg, (KEY)); }); \ } // Used symbols using metkit::mars2grib::utils::dict_traits::get_or_throw; // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_GET_OR_THROW_MISSING(bool, "bool", "missing_key"); TEST_GET_OR_THROW_MISSING(int, "int", "missing_key"); TEST_GET_OR_THROW_MISSING(long, "long", "missing_key"); TEST_GET_OR_THROW_MISSING(float, "float", "missing_key"); TEST_GET_OR_THROW_MISSING(double, "double", "missing_key") TEST_GET_OR_THROW_MISSING(std::string, "std::string", "missing_key") TEST_GET_OR_THROW_MISSING(std::vector, "std::vector", "missing_key") TEST_GET_OR_THROW_MISSING(std::vector, "std::vector", "missing_key") TEST_GET_OR_THROW_MISSING(std::vector, "std::vector", "missing_key") TEST_GET_OR_THROW_MISSING(std::vector, "std::vector", "missing_key") TEST_GET_OR_THROW_MISSING(std::vector, "std::vector", "missing_key") #undef TEST_GET_OR_THROW_MISSING } CASE("Test generic get_opt valid key") { #define TEST_GET_OPT_VALID(TYPE, TYPENAME_STRING, KEY, EXPECTED) \ SECTION("get_opt<" TYPENAME_STRING "> valid key") { \ using metkit::mars2grib::utils::dict_traits::get_opt; \ EXPECT_NO_THROW({ \ TYPE expected_result = (EXPECTED); \ std::optional actual_result = get_opt(cfg, (KEY)); \ EXPECT(actual_result.has_value()); \ EXPECT_EQUAL(actual_result.value(), expected_result); \ }); \ } // Used symbols using metkit::mars2grib::utils::dict_traits::get_opt; // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); // Try to get the value of "step" using the generic get function // ---- bool ---- TEST_GET_OPT_VALID(bool, "bool", "bool_scalar_var", true) TEST_GET_OPT_VALID(int, "int", "int_scalar_var", 7) TEST_GET_OPT_VALID(long, "long", "long_scalar_var", 12) TEST_GET_OPT_VALID(float, "float", "float_scalar_var", 3.14f) TEST_GET_OPT_VALID(double, "double", "double_scalar_var", 3.14) TEST_GET_OPT_VALID(std::string, "std::string", "string_scalar_var", std::string("abc")) TEST_GET_OPT_VALID(std::vector, "std::vector", "int_vec_var", (std::vector{7, 6})) TEST_GET_OPT_VALID(std::vector, "std::vector", "long_vec_var", (std::vector{12, 13})) TEST_GET_OPT_VALID(std::vector, "std::vector", "float_vec_var", (std::vector{3.14f, 2.71f})) TEST_GET_OPT_VALID(std::vector, "std::vector", "double_vec_var", (std::vector{3.14, 2.71})) TEST_GET_OPT_VALID(std::vector, "std::vector", "string_vec_var", (std::vector{"abc", "def"})) #undef TEST_GET_OPT_VALID } CASE("Test generic get_opt missing key") { #define TEST_GET_OPT_MISSING(TYPE, TYPENAME_STRING, KEY) \ SECTION("get_opt<" TYPENAME_STRING "> missing key") { \ using metkit::mars2grib::utils::dict_traits::get_opt; \ EXPECT_NO_THROW({ \ std::optional actual_result = get_opt(cfg, (KEY)); \ EXPECT(!actual_result.has_value()); \ }); \ } // Used symbols using metkit::mars2grib::utils::dict_traits::get_opt; // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_GET_OPT_MISSING(bool, "bool", "missing_key") TEST_GET_OPT_MISSING(int, "int", "missing_key") TEST_GET_OPT_MISSING(long, "long", "missing_key") TEST_GET_OPT_MISSING(float, "float", "missing_key") TEST_GET_OPT_MISSING(double, "double", "missing_key") TEST_GET_OPT_MISSING(std::string, "std::string", "missing_key") TEST_GET_OPT_MISSING(std::vector, "std::vector", "missing_key") TEST_GET_OPT_MISSING(std::vector, "std::vector", "missing_key") TEST_GET_OPT_MISSING(std::vector, "std::vector", "missing_key") TEST_GET_OPT_MISSING(std::vector, "std::vector", "missing_key") TEST_GET_OPT_MISSING(std::vector, "std::vector", "missing_key") #undef TEST_GET_OPT_MISSING } CASE("Test generic set_or_throw") { #define TEST_SET_OR_THROW(TYPE, TYPENAME_STRING, KEY, EXPECTED) \ SECTION("set_or_throw<" TYPENAME_STRING ">") { \ using metkit::mars2grib::utils::dict_traits::set_or_throw; \ using metkit::mars2grib::utils::dict_traits::get_or_throw; \ TYPE expected_result = (EXPECTED); \ EXPECT_NO_THROW({ set_or_throw(cfg, (KEY), expected_result); }); \ EXPECT_NO_THROW({ \ TYPE actual_result = get_or_throw(cfg, (KEY)); \ EXPECT_EQUAL(actual_result, expected_result); \ }); \ } // Prepare a custom dictionary to test long const std::string yaml(R"json({})json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_SET_OR_THROW(bool, "bool", "bool_scalar_var", true); TEST_SET_OR_THROW(int, "int", "int_scalar_var", 7); TEST_SET_OR_THROW(long, "long", "long_scalar_var", 12); TEST_SET_OR_THROW(float, "float", "float_scalar_var", 3.14f); TEST_SET_OR_THROW(double, "double", "double_scalar_var", 3.14); TEST_SET_OR_THROW(std::string, "std::string", "string_scalar_var", std::string("abc")); TEST_SET_OR_THROW(std::vector, "std::vector", "int_vec_var", (std::vector{7, 6})); TEST_SET_OR_THROW(std::vector, "std::vector", "long_vec_var", (std::vector{12, 13})); TEST_SET_OR_THROW(std::vector, "std::vector", "float_vec_var", (std::vector{3.14f, 2.71f})); TEST_SET_OR_THROW(std::vector, "std::vector", "double_vec_var", (std::vector{3.14, 2.71})); TEST_SET_OR_THROW(std::vector, "std::vector", "string_vec_var", (std::vector{"abc", "def"})); #undef TEST_SET_OR_THROW } CASE("Test generic set_or_ignore") { #define TEST_SET_OR_IGNORE(TYPE, TYPENAME_STRING, KEY, EXPECTED) \ SECTION("set_or_ignore<" TYPENAME_STRING ">") { \ using metkit::mars2grib::utils::dict_traits::set_or_ignore; \ using metkit::mars2grib::utils::dict_traits::get_or_throw; \ TYPE expected_result = (EXPECTED); \ EXPECT_NO_THROW({ set_or_ignore(cfg, (KEY), expected_result); }); \ EXPECT_NO_THROW({ \ TYPE actual_result = get_or_throw(cfg, (KEY)); \ EXPECT_EQUAL(actual_result, expected_result); \ }); \ } // Used symbols // Prepare a custom dictionary to test long const std::string yaml(R"json({})json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_SET_OR_IGNORE(bool, "bool", "bool_scalar_var", true); TEST_SET_OR_IGNORE(int, "int", "int_scalar_var", 7); TEST_SET_OR_IGNORE(long, "long", "long_scalar_var", 12); TEST_SET_OR_IGNORE(float, "float", "float_scalar_var", 3.14f); TEST_SET_OR_IGNORE(double, "double", "double_scalar_var", 3.14); TEST_SET_OR_IGNORE(std::string, "std::string", "string_scalar_var", std::string("abc")); TEST_SET_OR_IGNORE(std::vector, "std::vector", "int_vec_var", (std::vector{7, 6})); TEST_SET_OR_IGNORE(std::vector, "std::vector", "long_vec_var", (std::vector{12, 13})); TEST_SET_OR_IGNORE(std::vector, "std::vector", "float_vec_var", (std::vector{3.14f, 2.71f})); TEST_SET_OR_IGNORE(std::vector, "std::vector", "double_vec_var", (std::vector{3.14, 2.71})); TEST_SET_OR_IGNORE(std::vector, "std::vector", "string_vec_var", (std::vector{"abc", "def"})); #undef TEST_SET_OR_IGNORE } CASE("Test generic has valid") { // Used symbols using metkit::mars2grib::utils::dict_traits::has; // Prepare a custom dictionary to test long const std::string yaml(R"json({step: 12})json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); SECTION("has existing key") { EXPECT_NO_THROW({ bool actual_has = has(cfg, "step"); EXPECT(actual_has); }); } } CASE("Test generic has missing") { // Used symbols using metkit::mars2grib::utils::dict_traits::has; // Prepare a custom dictionary to test long const std::string yaml(R"json({step: 12})json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); SECTION("has missing key") { EXPECT_NO_THROW({ bool actual_has = has(cfg, "missing_key"); EXPECT(!actual_has); }); } } CASE("Test generic typed has valid") { #define TEST_TYPED_HAS_VALID(TYPE, TYPENAME_STRING, KEY) \ {SECTION("has<" TYPENAME_STRING "> valid"){using metkit::mars2grib::utils::dict_traits::has; \ EXPECT_NO_THROW({ \ bool actual_has = has(cfg, (KEY)); \ EXPECT(actual_has); \ }); \ } \ } // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_TYPED_HAS_VALID(bool, "bool", "bool_scalar_var"); TEST_TYPED_HAS_VALID(int, "int", "int_scalar_var"); TEST_TYPED_HAS_VALID(long, "long", "long_scalar_var"); TEST_TYPED_HAS_VALID(float, "float", "float_scalar_var"); TEST_TYPED_HAS_VALID(double, "double", "double_scalar_var"); TEST_TYPED_HAS_VALID(std::string, "std::string", "string_scalar_var"); TEST_TYPED_HAS_VALID(std::vector, "std::vector", "int_vec_var"); TEST_TYPED_HAS_VALID(std::vector, "std::vector", "long_vec_var"); TEST_TYPED_HAS_VALID(std::vector, "std::vector", "float_vec_var"); TEST_TYPED_HAS_VALID(std::vector, "std::vector", "double_vec_var"); TEST_TYPED_HAS_VALID(std::vector, "std::vector", "string_vec_var"); #undef TEST_TYPED_HAS_VALID } CASE("Test generic typed has missing") { #define TEST_TYPED_HAS_MISSING(TYPE, TYPENAME_STRING, KEY) \ { \ using metkit::mars2grib::utils::dict_traits::has; \ SECTION("has<" TYPENAME_STRING ">") { \ bool actual_has; \ EXPECT_NO_THROW({ actual_has = has(cfg, "missing_key"); }); \ EXPECT(!actual_has); \ } \ } // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_TYPED_HAS_MISSING(bool, "bool", "bool_scalar_var"); TEST_TYPED_HAS_MISSING(int, "int", "int_scalar_var"); TEST_TYPED_HAS_MISSING(long, "long", "long_scalar_var"); TEST_TYPED_HAS_MISSING(float, "float", "float_scalar_var"); TEST_TYPED_HAS_MISSING(double, "double", "double_scalar_var"); TEST_TYPED_HAS_MISSING(std::string, "std::string", "string_scalar_var"); TEST_TYPED_HAS_MISSING(std::vector, "std::vector", "int_vec_var"); TEST_TYPED_HAS_MISSING(std::vector, "std::vector", "long_vec_var"); TEST_TYPED_HAS_MISSING(std::vector, "std::vector", "float_vec_var"); TEST_TYPED_HAS_MISSING(std::vector, "std::vector", "double_vec_var"); TEST_TYPED_HAS_MISSING(std::vector, "std::vector", "string_vec_var"); #undef TEST_TYPED_HAS_MISSING } CASE("Test generic check valid") { #define TEST_CHECK_VALID(TYPE, TYPENAME_STRING, KEY) \ SECTION("check<" TYPENAME_STRING "> valid") { \ using metkit::mars2grib::utils::dict_traits::check; \ SECTION("check<" TYPENAME_STRING ">") { \ EXPECT_NO_THROW({ \ bool actual_has = check(cfg, (KEY), [](const auto&) { return true; }); \ EXPECT(actual_has); \ }); \ } \ } // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_CHECK_VALID(bool, "bool", "bool_scalar_var"); TEST_CHECK_VALID(int, "int", "int_scalar_var"); TEST_CHECK_VALID(long, "long", "long_scalar_var"); TEST_CHECK_VALID(float, "float", "float_scalar_var"); TEST_CHECK_VALID(double, "double", "double_scalar_var"); TEST_CHECK_VALID(std::string, "std::string", "string_scalar_var"); TEST_CHECK_VALID(std::vector, "std::vector", "int_vec_var"); TEST_CHECK_VALID(std::vector, "std::vector", "long_vec_var"); TEST_CHECK_VALID(std::vector, "std::vector", "float_vec_var"); TEST_CHECK_VALID(std::vector, "std::vector", "double_vec_var"); TEST_CHECK_VALID(std::vector, "std::vector", "string_vec_var"); #undef TEST_CHECK_VALID } CASE("Test generic check missing") { #define TEST_CHECK_MISSING(TYPE, TYPENAME_STRING, KEY) \ SECTION("check<" TYPENAME_STRING "> missing") { \ using metkit::mars2grib::utils::dict_traits::check; \ SECTION("check<" TYPENAME_STRING ">") { \ EXPECT_NO_THROW({ \ bool actual_has = check(cfg, (KEY), [](const auto&) { return false; }); \ EXPECT(actual_has); \ }); \ } \ } // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); TEST_CHECK_MISSING(bool, "bool", "bool_scalar_var"); TEST_CHECK_MISSING(int, "int", "int_scalar_var"); TEST_CHECK_MISSING(long, "long", "long_scalar_var"); TEST_CHECK_MISSING(float, "float", "float_scalar_var"); TEST_CHECK_MISSING(double, "double", "double_scalar_var"); TEST_CHECK_MISSING(std::string, "std::string", "string_scalar_var"); TEST_CHECK_MISSING(std::vector, "std::vector", "int_vec_var"); TEST_CHECK_MISSING(std::vector, "std::vector", "long_vec_var"); TEST_CHECK_MISSING(std::vector, "std::vector", "float_vec_var"); TEST_CHECK_MISSING(std::vector, "std::vector", "double_vec_var"); TEST_CHECK_MISSING(std::vector, "std::vector", "string_vec_var"); #undef TEST_CHECK_MISSING } CASE("Test sub-configuration") { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::set_or_throw; // Prepare a custom dictionary to test long const std::string yaml(R"json({ bool_scalar_var: true, int_scalar_var: 7, long_scalar_var: 12, float_scalar_var: 3.14, double_scalar_var: 3.14, string_scalar_var: "abc", int_vec_var: [7,6], long_vec_var: [12,13], float_vec_var: [3.14, 2.71], double_vec_var: [3.14, 2.71], string_vec_var: ["abc", "def"] })json"); // Initialize the configuration const eckit::YAMLConfiguration root(yaml); eckit::LocalConfiguration cfg(root); eckit::LocalConfiguration n1(root); eckit::LocalConfiguration n2(root); std::vector n3{n1, n2}; SECTION("set sub-configuration n1") { EXPECT_NO_THROW({ set_or_throw(cfg, "n1", n1); }); EXPECT_NO_THROW({ eckit::LocalConfiguration actual_n1 = get_or_throw(cfg, "n1"); }); } SECTION("set sub-configuration n2") { EXPECT_NO_THROW({ set_or_throw(cfg, "n2", n2); }); EXPECT_NO_THROW({ eckit::LocalConfiguration actual_n2 = get_or_throw(cfg, "n2"); }); } SECTION("set sub-configurations") { EXPECT_NO_THROW({ set_or_throw>(cfg, "n3", n3); }); EXPECT_NO_THROW({ std::vector actual_n3 = get_or_throw>(cfg, "n3"); }); } #undef TEST_CHECK_MISSING } int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/mars2grib/utils/dictionary_traits/mars2grib-codesHandleTraits-tests.cc0000664000175000017500000000564615232567717032201 0ustar alastairalastair#include #include #include #include #include "eckit/testing/Test.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/mars2grib/utils/dictionary_traits/dictaccess_codes_handle.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" CASE("CodesHandle: has vs typed has") { auto h = metkit::codes::codesHandleFromSample("GRIB2"); using metkit::mars2grib::utils::dict_traits::has; // tablesVersionLatest è long scalar EXPECT(has(*h, "tablesVersionLatest")); EXPECT(has(*h, "tablesVersionLatest")); EXPECT(!has(*h, "tablesVersionLatest")); } CASE("CodesHandle: get_or_throw type mismatch throws") { auto h = metkit::codes::codesHandleFromSample("GRIB2"); using metkit::mars2grib::utils::dict_traits::get_or_throw; EXPECT_THROWS(get_or_throw(*h, "tablesVersionLatest")); EXPECT_THROWS(get_or_throw(*h, "tablesVersionLatest")); } CASE("CodesHandle: get_opt type mismatch returns nullopt") { auto h = metkit::codes::codesHandleFromSample("GRIB2"); using metkit::mars2grib::utils::dict_traits::get_opt; auto v = get_opt(*h, "tablesVersionLatest"); EXPECT(!v.has_value()); } CASE("CodesHandle: bool semantics") { auto h = metkit::codes::codesHandleFromSample("GRIB2"); using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::set_or_throw; // set_or_throw(*h, "myBool", 2L); bool xxx = get_or_throw(*h, "localUsePresent"); EXPECT(!xxx); set_or_throw(*h, "subCentre", 1L); // (*h).set( "subCentre", 0L ); std::cout << "After set subCentre" << get_or_throw(*h, "subCentre") << std::endl; // set_or_throw(*h, "setLocalDefinition", true ); // EXPECT(!get_or_throw(*h, "myBool")); } CASE("CodesHandle: scalar vs vector distinction") { auto h = metkit::codes::codesHandleFromSample("GRIB2"); using metkit::mars2grib::utils::dict_traits::has; EXPECT(has(*h, "tablesVersionLatest")); // EXPECT(!has>(*h, "tablesVersionLatest")); } CASE("CodesHandle: missing") { auto h = metkit::codes::codesHandleFromSample("GRIB2"); using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::isMissing; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::dict_traits::setMissing_or_throw; set_or_throw(*h, "productDefinitionTemplateNumber", 0L); setMissing_or_throw(*h, "scaleFactorOfFirstFixedSurface"); EXPECT(isMissing(*h, "scaleFactorOfFirstFixedSurface")); // EXPECT(!get_opt(*h, "myMissing").has_value()); // EXPECT_THROWS(get_or_throw(*h, "myMissing")); } int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/mars2grib/utils/dictionary_traits/CMakeLists.txt0000664000175000017500000000061115232567717025767 0ustar alastairalastair ecbuild_add_test( TARGET mars2grib-localConfigurationTraits-tests SOURCES mars2grib-localConfigurationTraits-tests.cc NO_AS_NEEDED LIBS eckit metkit ) ecbuild_add_test( TARGET mars2grib-codesHandleTraits-tests SOURCES mars2grib-codesHandleTraits-tests.cc NO_AS_NEEDED LIBS eckit metkit )metkit-1.19.2/tests/mars2grib/api/0000775000175000017500000000000015232567717017107 5ustar alastairalastairmetkit-1.19.2/tests/mars2grib/api/test_python_api.py0000664000175000017500000000065415232567717022677 0ustar alastairalastair#!/bin/env python3 import sys print(sys.path) import pymars2grib encoder = pymars2grib.Mars2Grib() mars = { "origin": "ecmf", "class": "od", "stream": "oper", "type": "fc", "expver": "0001", "grid": "N200", "packing": "ccsds", "param": 130, "levtype": "hl", "levelist": 2, "date": 20260205, "time": 000000, "step": 0, } vals = [237.15] * 200 encoder.encode(vals, mars) metkit-1.19.2/tests/mars2grib/api/test_cpp_api.cc0000664000175000017500000000323315232567717022071 0ustar alastairalastair/* * (C) Copyright 2026- 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. */ #include #include "eckit/config/LocalConfiguration.h" #include "eckit/log/CodeLocation.h" #include "eckit/testing/Test.h" #include "metkit/mars2grib/api/Mars2Grib.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" CASE("mars2grib_api") { try { auto encoder = metkit::mars2grib::Mars2Grib(); eckit::LocalConfiguration mars; mars.set("origin", "ecmf"); mars.set("class", "od"); mars.set("stream", "oper"); mars.set("type", "fc"); mars.set("expver", "0001"); mars.set("grid", "N200"); mars.set("packing", "ccsds"); mars.set("param", 130); mars.set("levtype", "hl"); mars.set("levelist", 2); mars.set("date", 2026'02'05); mars.set("time", 00'00'00); mars.set("step", 0); std::vector vals(200, 237.15); encoder.encode(vals, mars); } catch (const std::exception& e) { metkit::mars2grib::utils::exceptions::printExceptionStack(e, eckit::Log::error()); std::throw_with_nested( metkit::mars2grib::utils::exceptions::Mars2GribGenericException("CPP API test failed", Here())); } } int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/mars2grib/api/CMakeLists.txt0000664000175000017500000000110615232567717021645 0ustar alastairalastair ecbuild_add_test( TARGET mars2grib_test_cpp_api CONDITION HAVE_MARS2GRIB INCLUDES "${ECCODES_INCLUDE_DIRS}" SOURCES test_cpp_api.cc LIBS metkit NO_AS_NEEDED ) ecbuild_add_test( TARGET mars2grib_test_python_api CONDITION HAVE_MARS2GRIB_PYTHON COMMAND python3 ARGS ${CMAKE_CURRENT_SOURCE_DIR}/test_python_api.py ENVIRONMENT PYTHONPATH=${CMAKE_BINARY_DIR}/lib ) metkit-1.19.2/tests/mars2grib/CMakeLists.txt0000664000175000017500000000015515232567717021077 0ustar alastairalastairif( HAVE_MARS2GRIB ) add_subdirectory( api ) add_subdirectory( utils ) add_subdirectory( backend ) endif() metkit-1.19.2/tests/test_param_axis.cc0000664000175000017500000006556415232567717020160 0ustar alastairalastair/* * (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. */ /// @file test_param_axis.cc /// @author Baudouin Raoult /// @date Mai 2019 #include #include #include #include #include "eckit/testing/Test.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Param.h" #include "metkit/mars/ParamID.h" #include "metkit/mars/Type.h" namespace metkit::mars::test { //----------------------------------------------------------------------------- static std::ostream& operator<<(std::ostream& out, const std::vector& params) { out << '['; const char* sep = ""; for (auto p : params) { out << sep << p; sep = ", "; } out << ']'; return out; } static void test_param_axis(const std::vector& user, const std::vector& axis, const std::vector& expect, bool expectWind, NormalisationMode mode = NormalisationMode::FullTableDropping) { bool windRequested = false; MarsRequest ignore; std::vector params(user.begin(), user.end()); std::vector expected(expect.begin(), expect.end()); std::vector index(axis.begin(), axis.end()); std::sort(index.begin(), index.end()); std::cout << "Axis:" << index << std::endl; std::cout << "User:" << params << std::endl; std::cout << "NormalisationMode:" << static_cast(mode) << std::endl; ParamID::normalise(ignore, params, index, windRequested, mode); std::cout << "Params expected: " << expected << std::endl; std::cout << " returned: " << params << std::endl; std::cout << "Wind expected: " << expectWind << std::endl; std::cout << " returned: " << windRequested << std::endl; EXPECT_EQUAL(params, expected); EXPECT_EQUAL(expectWind, windRequested); } void assertTypeExpansion(const std::string& name, std::vector values, const std::vector& expected) { static MarsLanguage language("retrieve"); MarsRequest req; req.setValuesTyped(language.type(name), values); req = language.expand(req, false, true); EXPECT_EQUAL(expected, req.values(name)); } CASE("canonical param") { assertTypeExpansion("param", {"129.228"}, {"228129"}); assertTypeExpansion("param", {"228129"}, {"228129"}); EXPECT_THROWS_AS(assertTypeExpansion("param", {".228"}, {""}), eckit::UserError); EXPECT_THROWS_AS(assertTypeExpansion("param", {"9999"}, {""}), eckit::UserError); assertTypeExpansion("param", {"254"}, {"254"}); assertTypeExpansion("param", {"254.128"}, {"254"}); assertTypeExpansion("param", {"254.129"}, {"129254"}); assertTypeExpansion("param", {"3003"}, {"3003"}); assertTypeExpansion("param", {"999.128"}, {"999"}); EXPECT_THROWS_AS(assertTypeExpansion("param", {"999.129"}, {"129999"}), eckit::UserError); EXPECT_THROWS_AS(assertTypeExpansion("param", {"1000.128"}, {""}), eckit::UserError); EXPECT_THROWS_AS(assertTypeExpansion("param", {"228003.228"}, {""}), eckit::UserError); } CASE("trivial") { std::vector user = {"1", "2", "3"}; std::vector axis = {"1", "2", "3"}; std::vector expect = {"1", "2", "3"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("wind1") { std::vector user = {"131", "132"}; std::vector axis = {"138", "155"}; std::vector expect = {"131", "132", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind2") { std::vector user = {"131", "132"}; std::vector axis = {"131", "132", "138", "155"}; std::vector expect = {"131", "132"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("wind3") { std::vector user = {"131", "132", "138", "155"}; std::vector axis = {"138", "155"}; std::vector expect = {"131", "132", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind4") { std::vector user = {"131.128", "132.128", "138.128", "155.128"}; std::vector axis = {"138.128", "155.128"}; std::vector expect = {"131.128", "132.128", "138.128", "155.128"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind5") { std::vector user = { "131.128", "132.128", }; std::vector axis = {"138", "155"}; std::vector expect = {"131", "132", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind6") { std::vector user = {"131.128", "132.128", "138.128", "155.128"}; std::vector axis = {"138", "155"}; std::vector expect = {"131", "132", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind7") { std::vector user = {"131", "132", "138", "155"}; std::vector axis = {"138.128", "155.128"}; std::vector expect = {"131.128", "132.128", "138.128", "155.128"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind8") { std::vector user = { "131", "132", }; std::vector axis = {"138.128", "155.128"}; std::vector expect = {"131.128", "132.128", "138.128", "155.128"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind9") { std::vector user = { "131", "132", }; std::vector axis = {"138", "155", "210131"}; std::vector expect = {"131", "132", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind10") { std::vector user = { "131", "132", }; std::vector axis = {"129138", "129155"}; std::vector expect = {"129131", "129132", "129138", "129155"}; std::vector empty = {}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, empty, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("wind11") { std::vector user = { "131", "132", }; std::vector axis = {"138", "155", "129138", "129155"}; std::vector expect = {"131", "132", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind12") { std::vector user = { "131", "132.129", }; std::vector axis = {"138", "155", "129138", "129155"}; std::vector expect = {"131", "129132", "138", "155", "129138", "129155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind13") { std::vector user = { "131.128", "132.128", }; std::vector axis = {"138", "155", "210131"}; std::vector expect = {"131", "132", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind14") { std::vector user = { "131", }; std::vector axis = {"138", "155", "210131"}; std::vector expect = {"131", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind15") { std::vector user = { "210131", }; std::vector axis = {"138", "155", "210131"}; std::vector expect = {"210131"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("wind16") { std::vector user = { "210131", "131", }; std::vector axis = {"138", "155", "210131"}; std::vector expect = {"210131", "131", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind17") { std::vector user = { "210131", "132", }; std::vector axis = {"138", "155", "210131"}; std::vector expect = {"210131", "132", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind18") { std::vector user = { "210131", "131.128", }; std::vector axis = {"138", "155", "210131"}; std::vector expect = {"210131", "131", "138", "155"}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, expect, /*expectWind*/ true, metkit::NormalisationMode::Strict); } CASE("wind19") { std::vector user = { "132", }; std::vector axis = {"160138", "160155", "160131"}; std::vector expect = {"160132", "160138", "160155"}; std::vector empty = {}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, empty, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("wind20") { std::vector user = { "131", }; std::vector axis = {"160138", "160155", "160131"}; std::vector expect = {"160131"}; std::vector empty = {}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, empty, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("wind21") { std::vector user = { "132", }; std::vector axis = { "120138", "120155", "170138", "170155", }; std::vector expect = {"170132", "170138", "170155"}; std::vector empty = {}; test_param_axis(user, axis, expect, /*expectWind*/ true); test_param_axis(user, axis, empty, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("mixed") { std::vector user = { "129", }; std::vector axis = {"129.128", "129"}; std::vector expect = {"129"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("ocean1") { std::vector user = {"145"}; std::vector axis = {"145.128", "164.128", "175.128", "148.128", "145.151", "164.151", "175.151", "148.151"}; std::vector expect = {"145.128"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("ocean2") { std::vector user = {"145", "151145"}; std::vector axis = {"145.128", "164.128", "175.128", "148.128", "145.151", "164.151", "175.151", "148.151"}; std::vector expect = {"145.128", "145.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("ocean3") { std::vector user = {"145", "164", "175", "148", "151145"}; std::vector axis = {"145.128", "164.128", "175.128", "148.128", "145.151", "164.151", "175.151", "148.151"}; std::vector expect = {"145.128", "164.128", "175.128", "148.128", "145.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("ocean4") { std::vector user = {"145", "164", "175", "148"}; std::vector axis = {"145.151", "164.151", "175.151", "148.151"}; std::vector expect = {"145.151", "164.151", "175.151", "148.151"}; std::vector empty = {}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, empty, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("MARS-794 - GRIB1 and GRIB2 in axis") { // Note the paramId's at the end std::vector axis = { "1.228", "3.228", "7.228", "8.128", "8.228", "9.128", "9.228", "10.228", "11.228", "12.228", "13.228", "14.228", "20.3", "20.128", "21.228", "22.228", "23.228", "24.228", "26.128", "29.228", "31.128", "32.128", "33.128", "34.128", "35.128", "36.128", "37.128", "38.128", "39.128", "40.128", "41.128", "42.128", "44.128", "44.228", "45.128", "46.228", "47.128", "47.228", "48.228", "49.128", "50.128", "57.128", "58.128", "59.128", "66.128", "67.128", "78.128", "79.128", "80.228", "81.228", "82.228", "83.228", "84.228", "85.228", "88.228", "89.228", "90.228", "98.174", "129.128", "130.151", "131.151", "131.228", "132.151", "132.228", "134.128", "136.128", "137.128", "139.128", "141.128", "142.128", "143.128", "144.128", "145.128", "145.151", "146.128", "147.128", "148.128", "148.151", "151.128", "159.128", "163.151", "164.128", "164.151", "165.128", "166.128", "167.128", "168.128", "169.128", "170.128", "172.128", "175.128", "175.151", "176.128", "177.128", "178.128", "179.128", "180.128", "181.128", "182.128", "183.128", "186.128", "187.128", "188.128", "189.128", "195.128", "196.128", "197.128", "198.128", "201.128", "202.128", "205.128", "206.128", "208.128", "209.128", "210.128", "211.128", "212.128", "213.128", "216.228", "217.228", "218.228", "219.228", "220.228", "221.228", "226.228", "227.228", "228.128", "229.128", "230.128", "231.128", "232.128", "235.128", "236.128", "238.128", "239.228", "240.228", "241.228", "243.128", "244.128", "245.128", "246.228", "247.228", "251.228", "162071", "162072", "228050", "228051", "260015", "260048", "260109", "260121", "260123", "26.228", "27.228", "28.228", "121.128", "122.128", "123.128", "222.228", "223.228", "224.228", "225.228", "228035", "228036", "228057", "228058"}; // by parameter xxx SECTION("by xxx only - priority to table 128") { std::vector user = {"148", "145", "164", "175"}; std::vector expect = {"148.128", "145.128", "164.128", "175.128"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by xxx and param.table") { std::vector user = {"148", "145.128", "164", "175.151"}; std::vector expect = {"148.128", "145.128", "164.128", "175.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by xxx and paramId") { std::vector user = {"148", "128145", "164", "151175"}; std::vector expect = {"148.128", "145.128", "164.128", "175.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by xxx and paramId and param.table") { std::vector user = {"148", "128145", "164", "175.151"}; std::vector expect = {"148.128", "145.128", "164.128", "175.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by xxx and paramId all ") { std::vector user = {"148", "145", "164", "175", "151148", "151145", "151164", "151175"}; std::vector expect = {"148.128", "145.128", "164.128", "175.128", "148.151", "145.151", "164.151", "175.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } // by paramId SECTION("by paramId only 128") { std::vector user = {"128148", "128145", "128164", "128175"}; std::vector expect = {"148.128", "145.128", "164.128", "175.128"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by paramId only 151") { std::vector user = {"151148", "151145", "151164", "151175"}; std::vector expect = {"148.151", "145.151", "164.151", "175.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by paramId mixed 128 and 151") { std::vector user = {"151148", "128145", "128164", "151175"}; std::vector expect = {"148.151", "145.128", "164.128", "175.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by paramId all") { std::vector user = {"151148", "151145", "151164", "151175", "128148", "128145", "128164", "128175"}; std::vector expect = {"148.151", "145.151", "164.151", "175.151", "148.128", "145.128", "164.128", "175.128"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } // by param.table SECTION("by param.table only 128") { std::vector user = {"148.128", "145.128", "164.128", "175.128"}; std::vector expect = {"148.128", "145.128", "164.128", "175.128"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by param.table only 151") { std::vector user = {"148.151", "145.151", "164.151", "175.151"}; std::vector expect = {"148.151", "145.151", "164.151", "175.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by param.table mixed 128 and 151") { std::vector user = {"148.128", "145.151", "164.128", "175.151"}; std::vector expect = {"148.128", "145.151", "164.128", "175.151"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } SECTION("by param.table all") { std::vector user = {"148.151", "145.151", "164.151", "175.151", "148.128", "145.128", "164.128", "175.128"}; std::vector expect = {"148.151", "145.151", "164.151", "175.151", "148.128", "145.128", "164.128", "175.128"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } } CASE("table1") { std::vector user = {"129", "130.128"}; std::vector axis = {"129.128", "130"}; std::vector expect = {"129.128", "130"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table2") { std::vector user = {"129", "130.128"}; std::vector axis = {"129.128", "130"}; std::vector expect = {"129.128", "130"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table3") { std::vector user = { "129", }; std::vector axis = { "140129", }; std::vector expect = {"140129"}; std::vector empty = {}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, empty, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table4") { std::vector user = { "129", }; std::vector axis = { "129.140", }; std::vector expect = {"129.140"}; std::vector empty = {}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, empty, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table5") { std::vector user = { "129", }; std::vector axis = {"129.128", "129.140"}; std::vector expect = {"129.128"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table6") { std::vector user = { "129.128", }; std::vector axis = {"129.128", "129.140"}; std::vector expect = {"129.128"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table7") { std::vector user = { "129", }; std::vector axis = {"129", "140129"}; std::vector expect = {"129"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table8") { std::vector user = { "129.128", }; std::vector axis = {"129", "140129"}; std::vector expect = {"129"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table9") { std::vector user = { "129.128", }; std::vector axis = {"129", "140129"}; std::vector expect = {"129"}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table10") { std::vector user = {"131"}; std::vector axis = {"210131"}; std::vector expect = {}; test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Loose); test_param_axis(user, axis, axis, /*expectWind*/ false, metkit::NormalisationMode::FullTableDropping); } CASE("table11") { std::vector user = {"131"}; std::vector axis = {"131.210"}; std::vector expect = {}; test_param_axis(user, axis, expect, /*expectWind*/ false, metkit::NormalisationMode::Loose); test_param_axis(user, axis, axis, /*expectWind*/ false, metkit::NormalisationMode::FullTableDropping); } CASE("table12") { std::vector user = {"131", "132"}; std::vector axis = {"210131", "170131", "180131", "160132"}; std::vector expect = {"170131", "160132"}; test_param_axis(user, axis, expect, /*expectWind*/ false); } CASE("table13") { std::vector user = {"131", "132"}; std::vector axis = {"210131", "131.170", "180131", "160132"}; std::vector expect = {"131.170", "160132"}; std::vector empty = {}; test_param_axis(user, axis, expect, /*expectWind*/ false); test_param_axis(user, axis, empty, /*expectWind*/ false, metkit::NormalisationMode::Strict); } CASE("table14") { std::vector user = {"134", "133"}; std::vector axis = {"210131", "133.170", "180134"}; std::vector expect = {"180134", "133.170"}; test_param_axis(user, axis, expect, /*expectWind*/ false); } CASE("table15") { std::vector user = {"134", "133.128"}; std::vector axis = {"210131", "133.170", "180134"}; std::vector expect = {"180134"}; test_param_axis(user, axis, expect, /*expectWind*/ false); } } // namespace metkit::mars::test //----------------------------------------------------------------------------- int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_codes_api.cc0000664000175000017500000002541115232567717017745 0ustar alastairalastair/* * (C) Copyright 2025- 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 operator<< via LocalConfiguration #include #include "eccodes.h" #include "eckit/config/LocalConfiguration.h" #include "eckit/io/Buffer.h" #include "eckit/testing/Test.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/codes/api/CodesTypes.h" #include "metkit/codes/api/KeyIterator.h" namespace metkit::grib::test { //----------------------------------------------------------------------------- // No explicit expectations here // However, we iterate the whole sample and print all keys // Keys are fetched by inspecting the native type and calling a specialized getXXX // A non-throwing behaviour is at least CASE("Test iterate sample, getting all keys by native type on iterator") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); for (auto& k : handle->keys()) { // std::cout << "\t" << k.name() << ": "; auto valFromIt = k.get(); auto valFromHandle = handle->get(k.name()); auto type = k.type(); // std::visit([](const auto& v) { std::cout << v << std::endl; }, valFromIt); if (k.name() != "sectionNumber" && k.name() != "numberOfSection") { EXPECT_EQUAL(valFromIt.index(), valFromHandle.index()); std::visit( [&](const auto& v) { // std::cout << "\t(from handle): " << v << std::endl; EXPECT_EQUAL(v, std::get>(valFromHandle)); }, valFromIt); } } } CASE("Test iterate and rewrite keys") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); using IF = KeyIteratorFlags; std::vector> values; for (auto& k : handle->keys(IF::SkipReadOnly)) { values.emplace_back(k.name(), k.get()); } EXPECT(values.size() > 0); for (const auto& p : std::move(values)) { // TODO(pgeier) Raise eccodes bug about not readonly keys if (p.first == "validityDateTime") continue; if (p.first == "productType") continue; if (p.first == "isTemplateDeprecated") continue; if (p.first == "isTemplateExperimental") continue; if (p.first == "datasetForLocal") continue; if (p.first == "isMessageValid") continue; try { std::visit([&](const auto& val) { handle->set(p.first, val); }, p.second); } catch (...) { std::cerr << "Error setting " << p.first << ": "; std::visit([&](const auto& val) { std::cerr << val << std::endl; }, p.second); throw; } }; } CASE("Test geo iterator") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); long numberValues = handle->getLong("numberOfValues"); auto lons = handle->getDoubleArray("longitudes"); auto lats = handle->getDoubleArray("latitudes"); auto values = handle->getDoubleArray("values"); EXPECT_EQUAL(lons.size(), numberValues); EXPECT_EQUAL(lats.size(), numberValues); EXPECT_EQUAL(values.size(), numberValues); long count = 0; // TODO(pgeier) Use structured binding with C++20; for (const auto& {longitude, latitude, value}: handle->values()) // {} for (const auto& data : handle->values()) { // std::cout << "\t" << count << ": " << data.longitude << "/" << data.latitude << ": " << data.value << // std::endl; EXPECT_EQUAL(lons[count], data.longitude); EXPECT_EQUAL(lats[count], data.latitude); EXPECT_EQUAL(values[count], data.value); ++count; } EXPECT_EQUAL(count, numberValues); } CASE("Test setting values") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); long numberValues = handle->getLong("numberOfValues"); std::vector newVals; for (int i = 0; i < numberValues; ++i) { newVals.push_back((double)i); } EXPECT_NO_THROW(handle->set("values", newVals)); auto values = handle->getDoubleArray("values"); EXPECT_EQUAL(newVals, values); long count = 0; for (const auto& data : handle->values()) { EXPECT_EQUAL(newVals[count], data.value); ++count; } EXPECT_EQUAL(count, numberValues); } CASE("Test load and iterate mars keys") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); handle->set("date", 20250101); handle->set("time", 1400); handle->set("step", (long)18); handle->set("param", (long)132); for (auto& k : handle->keys(namespaces::mars)) { if (k.name() == "date") { EXPECT_EQUAL(std::get(k.get()), 20250101); } if (k.name() == "time") { EXPECT_EQUAL(std::get(k.get()), 1400); } if (k.name() == "step") { EXPECT_EQUAL(std::get(k.get()), 18); } if (k.name() == "levtype") { EXPECT_EQUAL(std::get(k.get()), "sfc"); } if (k.name() == "param") { EXPECT_EQUAL(std::get(k.get()), 132); } } } CASE("Test isDefined, has, isMissing, set MARS key \"class\"") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); EXPECT(!handle->isDefined("class")); EXPECT(!handle->has("class")); EXPECT(!handle->isDefined("anoffset")); EXPECT(!handle->has("anoffset")); // Set a local definition template with mars keys EXPECT_NO_THROW(handle->set("setLocalDefinition", 1)); EXPECT_NO_THROW(handle->set("localDefinitionNumber", 15)); // Mars directly get a "default" value instead of being set to missing EXPECT(handle->isDefined("class")); EXPECT(!handle->isMissing("class")); EXPECT(handle->has("class")); EXPECT_NO_THROW(handle->set("class", "od")); EXPECT_EQUAL(handle->getString("class"), std::string("od")); EXPECT(!handle->isMissing("class")); EXPECT(handle->has("class")); // Mars key can not set to be missing EXPECT_THROWS(handle->setMissing("class")); } CASE("Test set missing") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); EXPECT_NO_THROW(handle->set("productDefinitionTemplateNumber", 0)); EXPECT(handle->isDefined("scaledValueOfFirstFixedSurface")); EXPECT(handle->isMissing("scaledValueOfFirstFixedSurface")); EXPECT(!handle->has("scaledValueOfFirstFixedSurface")); EXPECT_NO_THROW(handle->set("scaledValueOfFirstFixedSurface", 123)); EXPECT_EQUAL(handle->getLong("scaledValueOfFirstFixedSurface"), 123); EXPECT(!handle->isMissing("scaledValueOfFirstFixedSurface")); EXPECT(handle->has("scaledValueOfFirstFixedSurface")); EXPECT_NO_THROW(handle->setMissing("scaledValueOfFirstFixedSurface")); EXPECT(handle->isMissing("scaledValueOfFirstFixedSurface")); EXPECT(!handle->has("scaledValueOfFirstFixedSurface")); } CASE("Test copyInto and clone") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); size_t size = handle->messageSize(); std::vector bytes(size); EXPECT_NO_THROW(handle->copyInto(bytes.data(), size)); auto handle2 = handle->clone(); EXPECT_EQUAL(handle2->messageSize(), size); size_t size2 = handle2->messageSize(); std::vector bytes2(size2); EXPECT_NO_THROW(handle2->copyInto(bytes2.data(), size2)); EXPECT_EQUAL(size, size2); for (int i = 0; i < size; ++i) { EXPECT_EQUAL(bytes[i], bytes2[i]); } } CASE("Test copyInto and codesHandleFromMessage") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); size_t size = handle->messageSize(); std::vector bytes(size); EXPECT_NO_THROW(handle->copyInto(bytes.data(), size)); auto handle2 = codesHandleFromMessage({bytes.data(), size}); EXPECT_EQUAL(handle2->messageSize(), size); size_t size2 = handle2->messageSize(); std::vector bytes2(size2); EXPECT_NO_THROW(handle2->copyInto(bytes2.data(), size2)); EXPECT_EQUAL(size, size2); for (int i = 0; i < size; ++i) { EXPECT_EQUAL(bytes[i], bytes2[i]); } } CASE("Test copyInto and codesHandleFromMessageCopy") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); size_t size = handle->messageSize(); std::vector bytes(size); EXPECT_NO_THROW(handle->copyInto(bytes.data(), size)); auto handle2 = codesHandleFromMessageCopy({bytes.data(), size}); EXPECT_EQUAL(handle2->messageSize(), size); size_t size2 = handle2->messageSize(); std::vector bytes2(size2); EXPECT_NO_THROW(handle2->copyInto(bytes2.data(), size2)); EXPECT_EQUAL(size, size2); for (int i = 0; i < size; ++i) { EXPECT_EQUAL(bytes[i], bytes2[i]); } } CASE("Test copyInto and codesHandleFromFile") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); size_t size = handle->messageSize(); std::vector bytes(size); EXPECT_NO_THROW(handle->copyInto(bytes.data(), size)); std::string ofname{"GRIB2.tmpl"}; { std::ofstream of(ofname, std::ios::binary); if (!of) { throw std::runtime_error("Failed to open file: " + ofname); } of.write(reinterpret_cast(bytes.data()), size); of.close(); } auto handle2 = codesHandleFromFile(ofname, Product::GRIB); EXPECT_EQUAL(handle2->messageSize(), size); size_t size2 = handle2->messageSize(); std::vector bytes2(size2); EXPECT_NO_THROW(handle2->copyInto(bytes2.data(), size2)); EXPECT_EQUAL(size, size2); for (int i = 0; i < size; ++i) { EXPECT_EQUAL(bytes[i], bytes2[i]); } } } // namespace metkit::grib::test namespace std { template <> struct default_delete { void operator()(codes_handle* h) { ::codes_handle_delete(h); } }; } // namespace std namespace metkit::grib::test { CASE("Test release handle") { using namespace codes; auto handle = codesHandleFromSample("GRIB2"); std::unique_ptr raw = std::unique_ptr(reinterpret_cast(handle->release())); EXPECT(raw); EXPECT_THROWS_AS(handle->getLong("discipline"), CodesException); } //----------------------------------------------------------------------------- } // namespace metkit::grib::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/sol.grib0000664000175000017500000001604015232567717016111 0ustar alastairalastairGRIBb$ ( 5659 @( Z#Z#q $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($ " @* 7777GRIBb$ ( 5659 @( Z#Z#q $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($ " @* 7777GRIBb$ ( 5659 @( Z#Z#q $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($ " @* 7777GRIBb$ ( 5659 @( Z#Z#q $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($  |xtplhd`\XTPLHD@<840,($ " @* 7777metkit-1.19.2/tests/test_step.cc0000664000175000017500000000705315232567717016774 0ustar alastairalastair/* * (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. */ /// @file test_step.cc /// @author Emanuele Danovaro /// @date March 2025 #include #include #include "eckit/testing/Test.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/Type.h" namespace metkit::mars::test { using ::eckit::BadValue; //----------------------------------------------------------------------------- void assertTypeExpansion(const std::string& name, std::vector values, const std::vector& expected) { static MarsLanguage language("retrieve"); language.type(name)->expand(values); EXPECT_EQUAL(expected, values); } CASE("Test Step expansions") { // times with units assertTypeExpansion("step", {"0"}, {"0"}); assertTypeExpansion("step", {"12"}, {"12"}); assertTypeExpansion("step", {"260m"}, {"4h20m"}); assertTypeExpansion("step", {"30m", "1h", "1h30m", "120m"}, {"30m", "1", "1h30m", "2"}); assertTypeExpansion("step", {"0-1"}, {"0-1"}); assertTypeExpansion("step", {"30m-60m"}, {"30m-1"}); EXPECT_THROWS_AS(assertTypeExpansion("step", {"-1"}, {""}), BadValue); EXPECT_THROWS_AS(assertTypeExpansion("step", {"2-1"}, {""}), BadValue); assertTypeExpansion("step", {"0-3", "to", "9-12", "by", "3h"}, {"0-3", "3-6", "6-9", "9-12"}); assertTypeExpansion("step", {"0-3", "to", "0-12", "by", "3"}, {"0-3"}); assertTypeExpansion("step", {"0-30m", "to", "1h30m-2h", "by", "30m"}, {"0-30m", "30m-1", "1-1h30m", "1h30m-2"}); assertTypeExpansion( "step", {"0m", "to", "1440m", "by", "10m"}, {"0", "10m", "20m", "30m", "40m", "50m", "1", "1h10m", "1h20m", "1h30m", "1h40m", "1h50m", "2", "2h10m", "2h20m", "2h30m", "2h40m", "2h50m", "3", "3h10m", "3h20m", "3h30m", "3h40m", "3h50m", "4", "4h10m", "4h20m", "4h30m", "4h40m", "4h50m", "5", "5h10m", "5h20m", "5h30m", "5h40m", "5h50m", "6", "6h10m", "6h20m", "6h30m", "6h40m", "6h50m", "7", "7h10m", "7h20m", "7h30m", "7h40m", "7h50m", "8", "8h10m", "8h20m", "8h30m", "8h40m", "8h50m", "9", "9h10m", "9h20m", "9h30m", "9h40m", "9h50m", "10", "10h10m", "10h20m", "10h30m", "10h40m", "10h50m", "11", "11h10m", "11h20m", "11h30m", "11h40m", "11h50m", "12", "12h10m", "12h20m", "12h30m", "12h40m", "12h50m", "13", "13h10m", "13h20m", "13h30m", "13h40m", "13h50m", "14", "14h10m", "14h20m", "14h30m", "14h40m", "14h50m", "15", "15h10m", "15h20m", "15h30m", "15h40m", "15h50m", "16", "16h10m", "16h20m", "16h30m", "16h40m", "16h50m", "17", "17h10m", "17h20m", "17h30m", "17h40m", "17h50m", "18", "18h10m", "18h20m", "18h30m", "18h40m", "18h50m", "19", "19h10m", "19h20m", "19h30m", "19h40m", "19h50m", "20", "20h10m", "20h20m", "20h30m", "20h40m", "20h50m", "21", "21h10m", "21h20m", "21h30m", "21h40m", "21h50m", "22", "22h10m", "22h20m", "22h30m", "22h40m", "22h50m", "23", "23h10m", "23h20m", "23h30m", "23h40m", "23h50m", "24"}); } //----------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_matcher.cc0000664000175000017500000001474615232567717017453 0ustar alastairalastair/* * (C) Copyright 2025- 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 "eckit/testing/Test.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Matcher.h" #include using namespace eckit::testing; using namespace metkit::mars; namespace metkit::test { // ---------------------------------------------------------------------------------------------------------------------- CASE("parse string with spaces") { std::map map = parseMatchString("expver=(x[0-9a-z]{3}), number = (1|2) , stream= ^enfo$ "); EXPECT(map.size() == 3); EXPECT(map.find("expver") != map.end()); EXPECT(map.find("number") != map.end()); EXPECT(map.find("stream") != map.end()); } CASE("parse string errors") { // Check basic parsing EXPECT_THROWS_AS(parseMatchString(""), eckit::BadValue); EXPECT_THROWS_AS(parseMatchString("expver"), eckit::BadValue); EXPECT_THROWS_AS(parseMatchString("=expver"), eckit::BadValue); EXPECT_THROWS_AS(parseMatchString("expver="), eckit::BadValue); EXPECT_THROWS_AS(parseMatchString("expver,number=(1|2)"), eckit::BadValue); EXPECT_THROWS_AS(parseMatchString("number=(1|2),number=(3|4)"), eckit::BadValue); } CASE("match basic") { Matcher match_any("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::Any); Matcher match_all("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::All); MarsRequest req("retrieve"); req.setValue("expver", "xxxx"); req.values("number", {"1", "2"}); req.setValue("stream", "enfo"); req.setValue("step ", "0"); // step is not in the matcher. This should have no effect on matching EXPECT_EQUAL(req.count(), 2); EXPECT_EQUAL(match_any.match(req), true); EXPECT_EQUAL(match_all.match(req), true); } CASE("partially matching request") { Matcher match_any("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::Any); Matcher match_all("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::All); MarsRequest req("retrieve"); req.setValue("expver", "xxxx"); req.values("number", {"1", "2", "3"}); // number=3 does not match, the others do req.setValue("stream", "enfo"); EXPECT_EQUAL(req.count(), 3); EXPECT_EQUAL(match_any.match(req), true); EXPECT_EQUAL(match_all.match(req), false); } CASE("request entirely not matching") { Matcher match_any("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::Any); Matcher match_all("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::All); MarsRequest req("retrieve"); req.setValue("expver", "yyyy"); // expver=yyyy does not match req.values("number", {"1", "2"}); req.setValue("stream", "enfo"); EXPECT_EQUAL(req.count(), 2); EXPECT_EQUAL(match_any.match(req), false); EXPECT_EQUAL(match_all.match(req), false); } CASE("match with missing keys") { Matcher match_any("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::Any); Matcher match_all("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::All); MarsRequest req("retrieve"); req.setValue("expver", "xxxx"); req.values("number", {"1", "2"}); // stream is not set: matching depends on MatchMissingPolicy EXPECT_EQUAL(req.count(), 2); EXPECT_EQUAL(match_any.match(req, Matcher::DontMatchOnMissing), false); EXPECT_EQUAL(match_all.match(req, Matcher::DontMatchOnMissing), false); EXPECT_EQUAL(match_any.match(req, Matcher::MatchOnMissing), true); EXPECT_EQUAL(match_all.match(req, Matcher::MatchOnMissing), true); } CASE("match missing key and wrong values") { // -- Combinations of the above some of the above Matcher match_any("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::Any); Matcher match_all("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::All); MarsRequest req("retrieve"); req.setValue("expver", "yyyy"); // yyyy does not match req.values("number", {"1", "2", "3"}); // number=3 does not match EXPECT_EQUAL(req.count(), 3); EXPECT_EQUAL(match_any.match(req, Matcher::DontMatchOnMissing), false); EXPECT_EQUAL(match_all.match(req, Matcher::DontMatchOnMissing), false); EXPECT_EQUAL(match_any.match(req, Matcher::MatchOnMissing), false); EXPECT_EQUAL(match_all.match(req, Matcher::MatchOnMissing), false); } CASE("match missing key but some values match") { // Missing key, but expver matches, number partially matches Matcher match_any("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::Any); Matcher match_all("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::All); MarsRequest req("retrieve"); req.setValue("expver", "xxxx"); req.values("number", {"1", "2", "3"}); EXPECT_EQUAL(match_any.match(req, Matcher::DontMatchOnMissing), false); EXPECT_EQUAL(match_all.match(req, Matcher::DontMatchOnMissing), false); EXPECT_EQUAL(match_any.match(req, Matcher::MatchOnMissing), true); EXPECT_EQUAL(match_all.match(req, Matcher::MatchOnMissing), false); } CASE("match empty request") { // Testing that this does not raise an exception Matcher match_any("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::Any); Matcher match_all("expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$", Matcher::Policy::All); MarsRequest req("retrieve"); EXPECT_EQUAL(match_any.match(req, Matcher::DontMatchOnMissing), false); EXPECT_EQUAL(match_all.match(req, Matcher::DontMatchOnMissing), false); EXPECT_EQUAL(match_any.match(req, Matcher::MatchOnMissing), true); EXPECT_EQUAL(match_all.match(req, Matcher::MatchOnMissing), true); } CASE("streaming matcher is deterministic") { Matcher matcher("stream=^enfo$, expver=(x[0-9a-z]{3}), number=(1|2)", Matcher::Policy::Any); std::ostringstream oss; oss << matcher; EXPECT_EQUAL(oss.str(), "{expver=(x[0-9a-z]{3}),number=(1|2),stream=^enfo$}"); } // ---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/expand/0000775000175000017500000000000015232567717015725 5ustar alastairalastairmetkit-1.19.2/tests/expand/MARSC-263.req0000664000175000017500000000050315232567717017611 0ustar alastairalastairretrieve, levelist=0/5, section=h, stream=ocea, system=3, method=1, number=0/to/4, time=00, levtype=dp, product=inst, expver=1, param=129/130/145, class=od, date=20120101, type=of, step=24/to/96/by/24, expect=60, database=marsod, target=data.referencemetkit-1.19.2/tests/expand/MARSC-231.req0000664000175000017500000000036015232567717017605 0ustar alastairalastairretrieve, area=58./-10./35./80,class=od,date=20231231,expver=1,grid=0.1/0.1,levtype=surf,param=2t,step=0,stream=oper,time=00:00:00/12:00:00,type=fc,target=target.bbafa703-e7da-4383-8c34-d36a7cbee4f5,repres=gg, target="reference.vEDx0X.data"metkit-1.19.2/tests/expand/MARSC-227.expected0000664000175000017500000000100715232567717020623 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = FC, STREAM = OPER, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 33, DATE = 20231221, TIME = 0000, STEP = 0, DOMAIN = G, TARGET = reference.zcbyW7.data, RESOL = AUTO, GRID = .125/.125, PADDING = 0, USE = INFREQUENT metkit-1.19.2/tests/expand/PGEN-569.expected0000664000175000017500000000055315232567717020525 0ustar alastairalastairdisseminate, expver = 0080, class = mc, stream = oper, time = 0000/1200, type = an, step = 0, levtype = ml, levelist = 1, param = 131/132, date = 20260420, domain = gmetkit-1.19.2/tests/expand/MARSC-254.re0000664000175000017500000000013315232567717017427 0ustar alastairalastairretrieve, type=ob, date=-3, time=00, range=1439, target=data.reference metkit-1.19.2/tests/expand/MARSC-267.expected0000664000175000017500000000114715232567717020634 0ustar alastairalastairRETRIEVE, CLASS = S2, TYPE = PF, STREAM = ENFH, EXPVER = prod, REPRES = SH, LEVTYPE = PL, LEVELIST = 200/300/500/700/850/925/1000, PARAM = 130, DATE = 20240111, HDATE = 20040111, TIME = 0000, STEP = 0/24/48/72/96/120/144/168/192/216/240/264/288/312/336/360/384/408/432/456/480/504/528/552/576/600/624/648/672/696/720/744/768/792/816/840/864/888/912/936/960/984/1008/1032/1056/1080/1104, NUMBER = 1, ORIGIN = ECMF, TARGET = target.out, AREA = 40/-180/-40/180, PADDING = 0metkit-1.19.2/tests/expand/MARSC-252.req0000664000175000017500000000041015232567717017604 0ustar alastairalastairretrieve, class=od, stream=da, type=an, levtype=sfc, levelist=off, resol=off, date=20250416, time=00, step=00, param=tcc/10u/10v/2t/msl, param=tcc, repres=gg, format=p, expver=0001, target=data.reference metkit-1.19.2/tests/expand/MARSC-248.req0000664000175000017500000000017215232567717017616 0ustar alastairalastairretrieve, levtype=pl, level=1000/500, date=20250416, param=t, accuracy=LOW, target=data.reference metkit-1.19.2/tests/expand/MARSC-253.expected0000664000175000017500000000044215232567717020624 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = TF, STREAM = ENFO, EXPVER = 0001, REPRES = BU, OBSTYPE = 32, DATE = 20060112, TIME = 0000/1200, STEP = 0, DOMAIN = G, TARGET = data.reference, DUPLICATES = KEEP metkit-1.19.2/tests/expand/MARSC-255.req0000664000175000017500000000025415232567717017615 0ustar alastairalastairretrieve, date=20250416, area=75/60/10/-20, grid=2.5/2.5, levtype=ml, levelist=50, param=248, interpolation=bilinear, target=data.reference metkit-1.19.2/tests/expand/MARSC-228.re0000664000175000017500000000022015232567717017425 0ustar alastairalastairretrieve, date=20210501,time=2101/to/2400,type=ob,target="/ec/fws2/sb/work/rd/erds/i7ug/LWDA/2021050200/an/scat", target="reference.xoB8Cx.data"metkit-1.19.2/tests/expand/MARSC-241.req0000664000175000017500000000024315232567717017606 0ustar alastairalastairretrieve, DOMAIN=G,LEVTYPE=ML,LEVELIST=1,DATE=20041015,TIME=1200,STEP=0,PARAM=73.210/74.210,CLASS=RD,TYPE=FC,STREAM=OPER,EXPVER=f36f, target=reference.datametkit-1.19.2/tests/expand/MARSC-245.expected0000664000175000017500000000062415232567717020627 0ustar alastairalastairRETRIEVE, CLASS = MS, COUNTRY = IT, MODEL = HRM, BCMODEL = IFS, ICMODEL = MVOI, DOMAIN = EUROATL, STREAM = OPER, TYPE = FG, EXPVER = 0001, LEVTYPE = 110, PARAM = 201033, TIME = 1800, DATE = 20040102, STEP = 6, LEVELIST = 1, TARGET = reference.data, GRIB = ECMWFmetkit-1.19.2/tests/expand/MARSC-228.expected0000664000175000017500000000052715232567717020632 0ustar alastairalastairRETRIEVE,     CLASS      = OD,     TYPE       = OB,     STREAM     = OPER,     EXPVER     = 0001,     REPRES     = BU,     OBSTYPE    = 1,     DATE       = 20210501,     TIME       = 2101,     DOMAIN     = G,     TARGET     = reference.xoB8Cx.data,     DUPLICATES = KEEP,     RANGE      = 179metkit-1.19.2/tests/expand/MARSC-244.expected0000664000175000017500000000060215232567717020622 0ustar alastairalastairRETRIEVE, CLASS = TI, TYPE = PF, STREAM = ENFO, EXPVER = test, MODEL = LAM, REPRES = SH, LEVTYPE = SFC, PARAM = 166, DATE = 20130225, TIME = 0000, STEP = 3, NUMBER = 1, ORIGIN = SRNWPPEPS-DWD-EUA, TARGET = reference.data, PADDING = 0, PROCESS = LOCALmetkit-1.19.2/tests/expand/MARSC-236.req0000664000175000017500000000033315232567717017612 0ustar alastairalastairretrieve, accuracy=12,area=90.0/0.0/-90.0/359.5,date=20240102,domain=g,grid=0.5/0.5,leve=off,levtype=sfc,padding=0,param=134/137/165/166/167/168/235,stream=da,style=dissemination,time=00,type=an, target="data.reference"metkit-1.19.2/tests/expand/MARSC-258.expected0000664000175000017500000000053515232567717020634 0ustar alastairalastairRETRIEVE, CLASS = RD, TYPE = FC, STREAM = LWDA, EXPVER = h3f7, REPRES = SH, LEVTYPE = PL, LEVELIST = 1000/850/700/500/400/300, PARAM = 129, DATE = 20181101, TIME = 0000, STEP = ALL, ANOFFSET = 9, DOMAIN = G, TARGET = data.reference metkit-1.19.2/tests/expand/MARSC-241.expected0000664000175000017500000000047115232567717020623 0ustar alastairalastairRETRIEVE, CLASS = RD, TYPE = FC, STREAM = OPER, EXPVER = f36f, REPRES = SH, LEVTYPE = ML, LEVELIST = 1, PARAM = 210073/210074, DATE = 20041015, TIME = 1200, STEP = 0, DOMAIN = G, TARGET = reference.datametkit-1.19.2/tests/expand/MARSC-385.expected0000664000175000017500000000067715232567717020644 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = PF, STREAM = WAEF, EXPVER = 0001, REPRES = SH, LEVTYPE = SFC, PARAM = 140229/140232/140245, DATE = 20201101, TIME = 0000/0600/1200/1800, STEP = 9, NUMBER = 1, DOMAIN = G, TARGET = reference.0GhkzD.data, RESOL = AUTO, AREA = 39.5/140/39.5/140, GRID = .1/.1, PADDING = 0 metkit-1.19.2/tests/expand/MARSC-227.req0000664000175000017500000000025615232567717017616 0ustar alastairalastairretrieve, date=2023-12-21,expver=1,grid=0.125/0.125,levtype=sfc,padding=0,param=33.128,step=0,stream=oper,time=00:00:00,type=fc,use=infrequent, target="reference.zcbyW7.data"metkit-1.19.2/tests/expand/MARSC-248.expected0000664000175000017500000000051315232567717020627 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = AN, STREAM = OPER, EXPVER = 0001, REPRES = SH, LEVTYPE = PL, LEVELIST = 1000/500, PARAM = 130, DATE = 20250416, TIME = 1200, STEP = 0, DOMAIN = G, TARGET = data.reference, ACCURACY = 8 metkit-1.19.2/tests/expand/MARSC-264.expected0000664000175000017500000000064015232567717020626 0ustar alastairalastair RETRIEVE, CLASS = OD, TYPE = FCMAX, STREAM = SFMM, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 167, DATE = 19911001, FCMONTH = 2/3/4, TIME = 0000, NUMBER = 0/1/2/3/4, DOMAIN = G, SYSTEM = 2, METHOD = 1, SOURCE = mod.cl.surface, TARGET = data.reference, DATABASE = filemetkit-1.19.2/tests/expand/MARSC-234.req0000664000175000017500000000033515232567717017612 0ustar alastairalastairretrieve, area=Europe,class=od,date=2023-12-20,expver=1,grid=0.125/0.125,levtype=sfc,param=22.228,step=0,stream=oper,time=12:00:00,type=fc,target=target.27f72f51-1e60-437f-b1ce-e57129e2dff6, target="reference.A0FgVE.data"metkit-1.19.2/tests/expand/MARSC-249.req0000664000175000017500000000021415232567717017614 0ustar alastairalastairretrieve, levtype=pl, level=1000/500, date=20250416, param=t, accuracy=REDUCED, grid=5/5, target=data.reference metkit-1.19.2/tests/expand/MARSC-233.expected0000664000175000017500000000073115232567717020623 0ustar alastairalastairRETRIEVE, DATASET = tigge, CLASS = TI, TYPE = FC, STREAM = OPER, EXPVER = prod, REPRES = SH, LEVTYPE = PL, LEVELIST = 250/500/850, PARAM = 156, DATE = 20231224, TIME = 0000/1200, STEP = 0, ORIGIN = RKSL, TARGET = reference.data, RESOL = AUTO, AREA = 90/0/-90/359.75, GRID = .25/.25, PADDING = 0, PROCESS = LOCAL metkit-1.19.2/tests/expand/MARSC-263.expected0000664000175000017500000000074015232567717020626 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = OF, STREAM = OCEA, EXPVER = 0001, REPRES = SH, LEVTYPE = DP, LEVELIST = 0/5, PARAM = 129/130/145, DATE = 20120101, TIME = 0000, STEP = 24/48/72/96, NUMBER = 0/1/2/3/4, DOMAIN = G, SYSTEM = 3, METHOD = 1, PRODUCT = INST, SECTION = H, TARGET = data.reference, DATABASE = marsod, EXPECT = 60metkit-1.19.2/tests/expand/MARSC-264.req0000664000175000017500000000045315232567717017616 0ustar alastairalastairretrieve, class=od, type=fcmax, stream=sfmm, expver=0001, repres=gg, levtype=sfc, param=167, date=19911001, fcmonth=02/03/04, time=0000, number=0/1/2/3/4, domain=g, system=02, method=01, source="mod.cl.surface", target="data.reference"metkit-1.19.2/tests/expand/MARSC-255.expected0000664000175000017500000000063715232567717020634 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = AN, STREAM = OPER, EXPVER = 0001, REPRES = SH, LEVTYPE = ML, LEVELIST = 50, PARAM = 248, DATE = 20250416, TIME = 1200, STEP = 0, DOMAIN = G, TARGET = data.reference, RESOL = AUTO, INTERPOLATION = BILINEAR, AREA = 75/60/10/-20, GRID = 2.5/2.5 metkit-1.19.2/tests/expand/PGEN-569.req0000664000175000017500000000050615232567717017511 0ustar alastairalastairdisseminate, expver = 80, class = mc, stream = oper, time = 0/12, type = an, step = 0, levtype = ml, levelist = 1, param = u/v, date = 20260420metkit-1.19.2/tests/expand/MARSC-240.req0000664000175000017500000000036015232567717017605 0ustar alastairalastairretrieve, DOMAIN=G,LEVTYPE=DP,DATE=20070601,TIME=0000,STEP=288,PARAM=145.151,CLASS=OD,TYPE=OF,STREAM=OCEA,EXPVER=0001,NUMBER=0,SYSTEM=2,METHOD=1,PRODUCT=TIMS,SECTION=Z,LEVELIST=0.000,LATITUDE=-9.967,RANGE=264, target="reference.0DY4f2.data"metkit-1.19.2/tests/expand/MARSC-233 copy.expected0000664000175000017500000000070015232567717021552 0ustar alastairalastairRETRIEVE, DATASET = tigge, CLASS = TI, TYPE = FC, STREAM = OPER, EXPVER = prod, REPRES = SH, LEVTYPE = PL, LEVELIST = 250/500/850, PARAM = 156, DATE = 20231224, TIME = 0000/1200, STEP = 0, ORIGIN = RKSL, TARGET = reference.data, RESOL = AUTO, AREA = 90/0/-90/359.75, GRID = .25/.25, PADDING = 0metkit-1.19.2/tests/expand/MARSC-258.req0000664000175000017500000000025715232567717017623 0ustar alastairalastairretrieve, class=rd, stream=lwda, levtype=pl, type=fc, expver=h3f7, step=all, anoffset=9, time=00, date=20181101, target=data.reference metkit-1.19.2/tests/expand/MARSC-233.req0000664000175000017500000000033515232567717017611 0ustar alastairalastairretrieve,area=90.0/0.0/-90.0/359.75,class=ti,date=20231224,expver=prod,grid=0.25/0.25,levtype=pl,padding=0,param=156,step=0,time=0000/1200,type=fc,levelist=250/500/850,origin=rksl,dataset=tigge, target=reference.data metkit-1.19.2/tests/expand/MARSC-225.req0000664000175000017500000000027115232567717017611 0ustar alastairalastairretrieve, class=ti,date=2023-12-30,expver=prod,grid=0.2/0.2,levtype=sfc,padding=0,param=167,step=0/to/360/by/6,time=00:00:00,type=pf,number=1,origin=ecmf, target="reference.8nWO8K.data"metkit-1.19.2/tests/expand/MARSC-251.expected0000664000175000017500000000056415232567717020627 0ustar alastairalastairRETRIEVE, CLASS = TI, TYPE = CF, STREAM = ENFO, EXPVER = ALL, REPRES = SH, LEVTYPE = SFC, PARAM = 167, DATE = 20250414, TIME = 1200, STEP = 24, ORIGIN = ALL, TARGET = data.reference, PADDING = 0, PROCESS = LOCAL, EXPECT = ANY, NUMBER = 0metkit-1.19.2/tests/expand/MARSC-242.expected0000664000175000017500000000037715232567717020631 0ustar alastairalastairRETRIEVE, CLASS = E4, TYPE = CL, STREAM = MODA, EXPVER = 0001, REPRES = SH, LEVTYPE = ML, LEVELIST = 1, PARAM = 155, DATE = JAN, DOMAIN = G, TARGET = reference.datametkit-1.19.2/tests/expand/MARSC-249.expected0000664000175000017500000000057015232567717020633 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = AN, STREAM = OPER, EXPVER = 0001, REPRES = SH, LEVTYPE = PL, LEVELIST = 1000/500, PARAM = 130, DATE = 20250416, TIME = 1200, STEP = 0, DOMAIN = G, TARGET = data.reference, RESOL = AUTO, ACCURACY = 8, GRID = 5/5 metkit-1.19.2/tests/expand/MARSC-251.req0000664000175000017500000000031415232567717017606 0ustar alastairalastairretrieve, class=ti, date=20250414, time=12, origin=all, expver=all, type=cf, stream=enfo, levtype=sfc, param=2t, step=24, expect=any, target=data.reference metkit-1.19.2/tests/expand/MARSC-385.req0000664000175000017500000000041115232567717017614 0ustar alastairalastairretrieve, area=39.5/140/39.5/140, class=od, date=20201101, expver=1, grid=0.1/0.1, number=1, padding=0, param=229.140/232.140/245.140, step=9, stream=waef, time=00/06/12/18, type=pf, target=reference.0GhkzD.data metkit-1.19.2/tests/expand/MARSC-236.expected0000664000175000017500000000071415232567717020627 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = AN, STREAM = OPER, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 134/137/165/166/167/168/235, DATE = 20240102, TIME = 0000, STEP = 0, DOMAIN = G, TARGET = data.reference, RESOL = AV, ACCURACY = 12, STYLE = DISSEMINATION, AREA = 90/0/-90/359.5, GRID = .5/.5, PADDING = 0 metkit-1.19.2/tests/expand/MARSC-231.expected0000664000175000017500000000056715232567717020630 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = FC, STREAM = OPER, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 167, DATE = 20231231, TIME = 0000/1200, STEP = 0, DOMAIN = G, TARGET = reference.vEDx0X.data, RESOL = AUTO, AREA = 58/-10/35/80, GRID = .1/.1 metkit-1.19.2/tests/expand/MARSC-242.req0000664000175000017500000000021015232567717017601 0ustar alastairalastairretrieve, DOMAIN=G,LEVTYPE=ML,LEVELIST=1,DATE=JAN,PARAM=155.128,CLASS=E4,TYPE=CL,STREAM=MODA,EXPVER=0001, target=reference.data metkit-1.19.2/tests/expand/MARSC-261.expected0000664000175000017500000000063215232567717020624 0ustar alastairalastair RETRIEVE, CLASS = OD, TYPE = PF, STREAM = ENFO, EXPVER = 0001, REPRES = SH, LEVTYPE = SFC, PARAM = 167/165/166/164/228, DATE = 20250416, TIME = 0000, STEP = 6/12, NUMBER = 1/2, DOMAIN = G, TARGET = data.reference, RESOL = N128, AREA = 70.5/-21/30/40.5, GRID = 1.5/1.5metkit-1.19.2/tests/expand/MARSC-247.expected0000664000175000017500000000050715232567717020631 0ustar alastairalastairRETRIEVE, CLASS = RD, TYPE = AN, STREAM = OPER, EXPVER = aaaa, REPRES = SH, LEVTYPE = PL, LEVELIST = 1000/850/700/500/400/300, PARAM = 129, DATE = 20250413, TIME = 1200, STEP = 0, DOMAIN = G, TARGET = data.reference metkit-1.19.2/tests/expand/MARSC-232.req0000664000175000017500000000035515232567717017612 0ustar alastairalastairretrieve, area=85.000/-180.000/-60.000/180.000,class=od,date=20231003/to/20231003,expver=1,grid=0.100/0.100,levtype=sfc,padding=0,param=144.128,step=0/to/12/by/1,stream=oper,time=00/12,type=fc,dataset=mars, target=reference.data metkit-1.19.2/tests/expand/MARSC-237.expected0000664000175000017500000000100715232567717020624 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = FC, STREAM = OPER, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 151, DATE = 20240101, TIME = 0000, STEP = 0/3/6/9/12/15/18/21/24/27/30/33/36/39/42/45/48/51/54/57/60/63/66/69/72/75/78/81/84/87/90/93/96/99/102/105/108/111/114/117/120, DOMAIN = G, TARGET = reference.Hx2aUM.data, RESOL = AUTO, AREA = 90/-180/-90/180, GRID = .25/.25, PADDING = 0 metkit-1.19.2/tests/expand/MARSC-252.expected0000664000175000017500000000043515232567717020625 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = AN, STREAM = OPER, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 164, DATE = 20250416, TIME = 0000, STEP = 0, DOMAIN = G, TARGET = data.reference metkit-1.19.2/tests/expand/MARSC-225.expected0000664000175000017500000000137415232567717020630 0ustar alastairalastairRETRIEVE, CLASS = TI, TYPE = PF, STREAM = ENFO, EXPVER = prod, REPRES = SH, LEVTYPE = SFC, PARAM = 167, DATE = 20231230, TIME = 0000, STEP = 0/6/12/18/24/30/36/42/48/54/60/66/72/78/84/90/96/102/108/114/120/126/132/138/144/150/156/162/168/174/180/186/192/198/204/210/216/222/228/234/240/246/252/258/264/270/276/282/288/294/300/306/312/318/324/330/336/342/348/354/360, NUMBER = 1, ORIGIN = ECMF, TARGET = reference.8nWO8K.data, RESOL = AUTO, GRID = .2/.2, PADDING = 0, PROCESS = LOCALmetkit-1.19.2/tests/expand/MARSC-237.re0000664000175000017500000000030715232567717017433 0ustar alastairalastairretrieve, area=90/-180/-90/180,class=od,date=2024-01-01,expver=1,grid=0.25/0.25,levtype=sfc,padding=0,param=mslp,step=0/to/120/by/3,stream=oper,time=00:00,type=fc, target="reference.Hx2aUM.data" metkit-1.19.2/tests/expand/MARSC-261.re0000664000175000017500000000045615232567717017435 0ustar alastairalastair retrieve, class=od, type=pf, stream=ef, expver=0001, repres=sh, levtype=sfc, param=167/165/166/164/228, date=20250416, time=0000, step=6/12, number=1/2, domain=g, resol=n128, area=70.5/-21.0/30.0/40.5, grid=1.5/1.5, target=data.referencemetkit-1.19.2/tests/expand/MARSC-247.req0000664000175000017500000000012615232567717017614 0ustar alastairalastair retrieve, class=rd, date=20250413, expver=aaaa, target=data.referencemetkit-1.19.2/tests/expand/MARSC-245.req0000664000175000017500000000032215232567717017610 0ustar alastairalastairretrieve, DOMAIN=EUROATL,LEVTYPE=110,LEVELIST=1,DATE=20040102,TIME=1800,STEP=6,PARAM=33.201,CLASS=MS,TYPE=FG,STREAM=OPER,EXPVER=0001,COUNTRY=IT,MODEL=HRM,BCMODEL=IFS,ICMODEL=MVOI, target=reference.data metkit-1.19.2/tests/expand/MARSC-254.expected0000664000175000017500000000043715232567717020631 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = OB, STREAM = OPER, EXPVER = 0001, REPRES = BU, OBSTYPE = 1, DATE = 20250414, TIME = 0000, RANGE = 1439, DOMAIN = G, TARGET = data.reference, DUPLICATES = KEEP metkit-1.19.2/tests/expand/MARSC-267.req0000664000175000017500000000036615232567717017624 0ustar alastairalastairretrieve,area=40/-180/-40/180,class=s2,date=2024-01-11,expver=prod,levtype=pl,padding=0,param=130,step=0/to/1104/by/24,stream=enfh,time=00:00:00,type=pf,levelist=200/300/500/700/850/925/1000,number=1,origin=ecmf,hdate=2004-01-11,target=target.outmetkit-1.19.2/tests/expand/MARSC-224.req0000664000175000017500000000027715232567717017616 0ustar alastairalastairretrieve, class=ti,date=20231226/to/20231226,expver=prod,grid=0.5/0.5,levtype=sfc,padding=0,param=151,step=0/to/240/by/6,time=12,type=fc,number=all,origin=ecmf, target="reference.OkV8i4.data"metkit-1.19.2/tests/expand/MARSC-232.expected0000664000175000017500000000067115232567717020625 0ustar alastairalastairRETRIEVE, DATASET = mars, CLASS = OD, TYPE = FC, STREAM = OPER, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 144, DATE = 20231003, TIME = 0000/1200, STEP = 0/1/2/3/4/5/6/7/8/9/10/11/12, DOMAIN = G, TARGET = reference.data, RESOL = AUTO, AREA = 85/-180/-60/180, GRID = .1/.1, PADDING = 0 metkit-1.19.2/tests/expand/MARSC-224.expected0000664000175000017500000000126715232567717020630 0ustar alastairalastair RETRIEVE, CLASS = TI, TYPE = FC, STREAM = OPER, EXPVER = prod, REPRES = SH, LEVTYPE = SFC, PARAM = 151, DATE = 20231226, TIME = 1200, STEP = 0/6/12/18/24/30/36/42/48/54/60/66/72/78/84/90/96/102/108/114/120/126/132/138/144/150/156/162/168/174/180/186/192/198/204/210/216/222/228/234/240, NUMBER = ALL, ORIGIN = ECMF, TARGET = reference.OkV8i4.data, RESOL = AUTO, GRID = .5/.5, PADDING = 0, PROCESS = LOCAL metkit-1.19.2/tests/expand/MARSC-223.expected0000664000175000017500000000101015232567717020611 0ustar alastairalastair RETRIEVE, CLASS = OD, TYPE = FC, STREAM = OPER, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 33, DATE = 20231221, TIME = 0000, STEP = 0, DOMAIN = G, TARGET = reference.zcbyW7.data, RESOL = AUTO, GRID = .125/.125, PADDING = 0, USE = INFREQUENT metkit-1.19.2/tests/expand/MARSC-223.req0000664000175000017500000000026615232567717017613 0ustar alastairalastairretrieve, date=2023-12-21,expver=1,grid=0.125/0.125,levtype=sfc,padding=0,param=33.128,step=0,stream=oper,time=00:00:00,type=fc,use=infrequent, target="reference.zcbyW7.data"metkit-1.19.2/tests/expand/MARSC-244.req0000664000175000017500000000026215232567717017612 0ustar alastairalastairretrieve, DATE=20130225,TIME=0000,ORIGIN=SRNWPPEPS-DWD-EUA,STEP=3,LEVTYPE=SFC,NUMBER=1,PARAM=166,EXPVER=test,CLASS=TI,MODEL=LAM,TYPE=PF,STREAM=ENFO, target=reference.datametkit-1.19.2/tests/expand/MARSC-234.expected0000664000175000017500000000057315232567717020630 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = FC, STREAM = OPER, EXPVER = 0001, REPRES = GG, LEVTYPE = SFC, PARAM = 228022, DATE = 20231220, TIME = 1200, STEP = 0, DOMAIN = G, TARGET = reference.A0FgVE.data, RESOL = AUTO, AREA = 73.5/-27/33/45, GRID = .125/.125 metkit-1.19.2/tests/expand/MARSC-240.expected0000664000175000017500000000072215232567717020621 0ustar alastairalastairRETRIEVE, CLASS = OD, TYPE = OF, STREAM = OCEA, EXPVER = 0001, REPRES = SH, LEVTYPE = DP, LEVELIST = 0, PARAM = 151145, DATE = 20070601, TIME = 0000, RANGE = 264, STEP = 288, NUMBER = 0, DOMAIN = G, SYSTEM = 2, METHOD = 1, PRODUCT = TIMS, SECTION = Z, LATITUDE = -9.967, TARGET = reference.0DY4f2.data metkit-1.19.2/tests/expand/MARSC-253.req0000664000175000017500000000031215232567717017606 0ustar alastairalastairretrieve, class=od, type=tf, stream=enfo, expver=0001, repres=bu, obstype=32, date=20060112, time=0000/1200, domain=g, duplicates=keep, target=data.reference metkit-1.19.2/tests/test_mars_language.cc0000664000175000017500000000357215232567717020630 0ustar alastairalastair #include #include "eckit/testing/Test.h" #include "metkit/mars/MarsLanguage.h" namespace metkit::mars::test { CASE("retrieve_best_match_param_matching") { const auto language = MarsLanguage("retrieve"); // Strict is defaulted to true and this is not matching auto match = language.bestMatch("parameter", {"parameter"}, false, false, false, {}); EXPECT(match == "parameter"); }; CASE("retrieve_best_match_param_not_matching") { const auto language = MarsLanguage("retrieve"); // Strict is defaulted to true and this is not matching auto match = language.bestMatch("param", {"parameter"}, false, false, false, {}); EXPECT(match == "parameter"); auto empty = language.bestMatch("param", {"car"}, false, false, false, {}); EXPECT(empty == ""); }; CASE("retrieve_best_match_param_not_matching_throw") { const auto language = MarsLanguage("retrieve"); // Strict is defaulted to true and this is not matching auto match = language.bestMatch("param", {"parameter"}, true, false, false, {}); EXPECT(match == "parameter"); EXPECT_THROWS(language.bestMatch("param", {"car"}, true, false, false, {})); }; CASE("retrieve_best_match_param_not_matching") { const auto language = MarsLanguage("retrieve"); // Strict is defaulted to true and this is not matching auto match = language.bestMatch("param", {"parameter"}, false, false, true, {}); EXPECT(match == "parameter"); match = language.bestMatch("par", {"parameter"}, false, false, true, {}); EXPECT(match == "parameter"); match = language.bestMatch("par", {"car"}, false, false, true, {}); EXPECT(match == ""); }; } // namespace metkit::mars::test int main(int argc, char** argv) { putenv("METKIT_LANGUAGE_STRICT_MODE=0"); auto res = eckit::testing::run_tests(argc, argv); unsetenv("METKIT_LANGUAGE_STRICT_MODE"); return res; } metkit-1.19.2/tests/test_hypercube.cc0000664000175000017500000001611415232567717020005 0ustar alastairalastair/* * (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. */ /// @file test_MetFile.cc /// @date Jan 2016 /// @author Florian Rathgeber #include #include "eckit/testing/Test.h" #include "metkit/hypercube/HyperCube.h" #include "metkit/mars/MarsRequest.h" namespace metkit::mars::test { //----------------------------------------------------------------------------- CASE("test_metkit_hypercube") { const char* text = "retrieve,class=rd,type=an,stream=oper,levtype=pl,date=20191110,time=0000,step=0,expver=xxxy,domain=g,levelist=" "500,param=138"; MarsRequest r = MarsRequest::parse(text); metkit::hypercube::HyperCube cube(r); EXPECT(cube.contains(r)); EXPECT(cube.size() == 1); EXPECT(cube.vacantRequests().size() == 1); EXPECT(!(r < *cube.vacantRequests().begin())); EXPECT(!(*cube.vacantRequests().begin() < r)); } CASE("test_metkit_hypercube_subset") { const char* text = "retrieve,class=rd,type=an,stream=oper,levtype=pl,date=20191110,time=0000,step=0,expver=xxxy,domain=g,levelist=" "500/600,param=138"; MarsRequest r = MarsRequest::parse(text); metkit::hypercube::HyperCube cube(r); EXPECT(cube.size() == 2); EXPECT(cube.countVacant() == 2); EXPECT(cube.vacantRequests().size() == 1); EXPECT(!(r < *cube.vacantRequests().begin())); EXPECT(!(*cube.vacantRequests().begin() < r)); const char* text500 = "retrieve,class=rd,type=an,stream=oper,levtype=pl,date=20191110,time=0000,step=0,expver=xxxy,domain=g,levelist=" "500,param=138"; MarsRequest r500 = MarsRequest::parse(text500); const char* text600 = "retrieve,class=rd,type=an,stream=oper,levtype=pl,date=20191110,time=0000,step=0,expver=xxxy,domain=g,levelist=" "600,param=138"; MarsRequest r600 = MarsRequest::parse(text600); EXPECT_THROWS(cube.contains(r)); EXPECT(cube.contains(r500)); EXPECT(cube.contains(r600)); cube.clear(r500); EXPECT(!cube.contains(r500)); EXPECT(cube.size() == 2); EXPECT(cube.countVacant() == 1); EXPECT(cube.vacantRequests().size() == 1); EXPECT(!(r600 < *cube.vacantRequests().begin())); EXPECT(!(*cube.vacantRequests().begin() < r600)); cube.clear(r600); EXPECT(cube.countVacant() == 0); } CASE("test_metkit_hypercube_request") { const char* text = "retrieve,class=rd,type=an,stream=oper,levtype=pl,date=20191110,time=0000,step=0,expver=xxxy,domain=g,levelist=" "500/600,param=138/155"; MarsRequest r = MarsRequest::parse(text); metkit::hypercube::HyperCube cube(r); EXPECT(cube.size() == 4); EXPECT(cube.countVacant() == 4); EXPECT(cube.vacantRequests().size() == 1); EXPECT(!(r < *cube.vacantRequests().begin())); EXPECT(!(*cube.vacantRequests().begin() < r)); const char* text500 = "retrieve,class=rd,type=an,stream=oper,levtype=pl,date=20191110,time=0000,step=0,expver=xxxy,domain=g,levelist=" "500,param=138"; MarsRequest r500 = MarsRequest::parse(text500); const char* text600 = "retrieve,class=rd,type=an,stream=oper,levtype=pl,date=20191110,time=0000,step=0,expver=xxxy,domain=g,levelist=" "600,param=138"; MarsRequest r600 = MarsRequest::parse(text600); EXPECT_THROWS(cube.contains(r)); EXPECT(cube.contains(r500)); EXPECT(cube.contains(r600)); cube.clear(r500); EXPECT(!cube.contains(r500)); EXPECT(cube.size() == 4); EXPECT(cube.countVacant() == 3); EXPECT(cube.vacantRequests().size() == 2); cube.clear(r600); EXPECT(cube.countVacant() == 2); EXPECT(cube.vacantRequests().size() == 1); const char* text155 = "retrieve,class=rd,type=an,stream=oper,levtype=pl,date=20191110,time=0000,step=0,expver=xxxy,domain=g,levelist=" "500/600,param=155"; MarsRequest r155 = MarsRequest::parse(text155); EXPECT(!(r155 < *cube.vacantRequests().begin())); EXPECT(!(*cube.vacantRequests().begin() < r155)); } CASE("test_metkit_hypercube_request METK-132") { std::vector keys = {"levelist", "param"}; MarsRequest r = MarsRequest::parse("retrieve,levelist=2/4/1/3/5,param=228038/235094/235077/235078"); metkit::hypercube::HyperCube cube{r}; // unset levelist 5 for params 235077/235094 cube.clear(MarsRequest::parse("retrieve,levelist=5,param=235077")); cube.clear(MarsRequest::parse("retrieve,levelist=5,param=235094")); std::vector expected_flat_requests = { MarsRequest::parse("retrieve,levelist=1,param=228038"), MarsRequest::parse("retrieve,levelist=2,param=228038"), MarsRequest::parse("retrieve,levelist=3,param=228038"), MarsRequest::parse("retrieve,levelist=4,param=228038"), MarsRequest::parse("retrieve,levelist=5,param=228038"), MarsRequest::parse("retrieve,levelist=1,param=235077"), MarsRequest::parse("retrieve,levelist=2,param=235077"), MarsRequest::parse("retrieve,levelist=3,param=235077"), MarsRequest::parse("retrieve,levelist=4,param=235077"), MarsRequest::parse("retrieve,levelist=1,param=235094"), MarsRequest::parse("retrieve,levelist=2,param=235094"), MarsRequest::parse("retrieve,levelist=3,param=235094"), MarsRequest::parse("retrieve,levelist=4,param=235094"), MarsRequest::parse("retrieve,levelist=1,param=235078"), MarsRequest::parse("retrieve,levelist=2,param=235078"), MarsRequest::parse("retrieve,levelist=3,param=235078"), MarsRequest::parse("retrieve,levelist=4,param=235078"), MarsRequest::parse("retrieve,levelist=5,param=235078"), // These two requests we have removed from the cube: // MarsRequest::parse("retrieve,levelist=5,param=235077"), // MarsRequest::parse("retrieve,levelist=5,param=235094"), }; auto dense_requests = cube.vacantRequests(); EXPECT_EQUAL(dense_requests.size(), 2); std::vector flattened_requests; for (const auto& req : cube.vacantRequests()) { // surely this should be requests, not vacantRequests... anyway... auto flattened = req.split(keys); for (const auto& f : flattened) { flattened_requests.push_back(f); } } EXPECT_EQUAL(flattened_requests.size(), 18); EXPECT_EQUAL(flattened_requests.size(), expected_flat_requests.size()); // compare both vectors std::sort(flattened_requests.begin(), flattened_requests.end()); std::sort(expected_flat_requests.begin(), expected_flat_requests.end()); for (size_t i = 0; i < expected_flat_requests.size(); ++i) { EXPECT_EQUAL(flattened_requests[i].values("levelist"), expected_flat_requests[i].values("levelist")); EXPECT_EQUAL(flattened_requests[i].values("param"), expected_flat_requests[i].values("param")); } } } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_steprange_axis.cc0000664000175000017500000001160115232567717021027 0ustar alastairalastair/* * (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. */ /// @file test_param_axis.cc /// @author Baudouin Raoult /// @date Mai 2019 #include #include #include #include #include "eckit/testing/Test.h" #include "eckit/types/FloatCompare.h" #include "metkit/mars/StepRange.h" #include "metkit/mars/StepRangeNormalise.h" namespace eckit { template <> struct VectorPrintSelector { using selector = VectorPrintSimple; }; } // namespace eckit namespace metkit::mars::test { //----------------------------------------------------------------------------- CASE("steprange") { { StepRange sr{0, 24}; EXPECT_EQUAL(sr.from(), 0); EXPECT_EQUAL(sr.to(), 24); } { StepRange sr{"0-24"}; EXPECT_EQUAL(sr.from(), 0); EXPECT_EQUAL(sr.to(), 24); } { StepRange sr{0, .5}; EXPECT_EQUAL(sr.from(), 0); EXPECT(eckit::types::is_approximately_equal(sr.to(), 0.5)); } { StepRange sr{"0-30m"}; EXPECT_EQUAL(sr.from(), 0); EXPECT(eckit::types::is_approximately_equal(sr.to(), 0.5)); } { StepRange sr{"0-24s"}; EXPECT_EQUAL(sr.from(), 0); EXPECT(eckit::types::is_approximately_equal(sr.to(), 24. / 3600.)); } { StepRange sr{"40m-260m"}; EXPECT(eckit::types::is_approximately_equal(sr.from(), 2. / 3.)); EXPECT(eckit::types::is_approximately_equal(sr.to(), 4 + 1. / 3.)); } { StepRange sr{"20m"}; EXPECT_EQUAL(sr.toString(), "20m"); EXPECT_EQUAL(sr.toString(true), "20m"); } { StepRange sr{"60m"}; EXPECT_EQUAL(sr.toString(), "1"); EXPECT_EQUAL(sr.toString(true, false), "1"); EXPECT_EQUAL(sr.toString(true, true), "1h"); } { StepRange sr{"1d"}; EXPECT_EQUAL(sr.toString(), "24"); EXPECT_EQUAL(sr.toString(true, false), "1d"); EXPECT_EQUAL(sr.toString(true, true), "1d"); } { StepRange sr{"25"}; EXPECT_EQUAL(sr.toString(), "25"); EXPECT_EQUAL(sr.toString(true, false), "1d1h"); EXPECT_EQUAL(sr.toString(true, true), "1d1h"); } } static void test_steprange_axis(const std::vector& user, const std::vector& axis, const std::vector& expect) { std::vector values(user.begin(), user.end()); std::vector result(expect.begin(), expect.end()); std::vector index(axis.begin(), axis.end()); std::sort(index.begin(), index.end()); std::cout << "User:" << values << std::endl; std::cout << "Axis:" << index << std::endl; StepRangeNormalise::normalise(values, index); std::cout << "Result:" << values << std::endl; EXPECT(values == result); } CASE("trivial") { std::vector user = {"1", "2", "3"}; std::vector axis = {"1", "2", "3"}; std::vector expect = {"1", "2", "3"}; test_steprange_axis(user, axis, expect); } CASE("subselection") { std::vector user = {"2", "3"}; std::vector axis = {"1", "2", "3"}; std::vector expect = {"2", "3"}; test_steprange_axis(user, axis, expect); } CASE("missing values") { std::vector user = {"1", "2", "3"}; std::vector axis = {"1", "3"}; std::vector expect = {"1", "3"}; test_steprange_axis(user, axis, expect); } CASE("ranges") { std::vector user = {"0-24", "24-48", "3-9"}; std::vector axis = {"0-24", "6-30", "12-36", "18-42", "24-48"}; std::vector expect = {"0-24", "24-48"}; test_steprange_axis(user, axis, expect); } CASE("default start-point") { std::vector user = {"1", "2", "24", "25"}; std::vector axis = {"1", "0-1", "3", "0-3", "0-24", "25"}; std::vector expect = {"1", "0-1", "0-24", "25"}; test_steprange_axis(user, axis, expect); } CASE("match range start") { // SDS: I'm not really sure why this is supported, but the original // MARS code did it... std::vector user = {"2-24"}; std::vector axis = {"1", "2", "3"}; std::vector expect = {"2"}; test_steprange_axis(user, axis, expect); } } // namespace metkit::mars::test //----------------------------------------------------------------------------- int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_obstype.cc0000664000175000017500000000533315232567717017505 0ustar alastairalastair/* * (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. */ /// @file test_obstype.cc /// @author Emanuele Danovaro /// @date July 2025 #include #include #include "eckit/testing/Test.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/Type.h" namespace metkit::mars::test { using ::eckit::UserError; //----------------------------------------------------------------------------- void assertTypeExpansion(const std::string& name, std::vector values, const std::vector& expected) { static MarsLanguage language("retrieve"); language.type(name)->expand(values); EXPECT_EQUAL(expected, values); } CASE("Test Obstype expansions") { // times with units assertTypeExpansion("obstype", {"1"}, {"1"}); assertTypeExpansion("obstype", {"ssmi"}, {"126"}); assertTypeExpansion("obstype", {"trmm"}, {"129", "130"}); assertTypeExpansion("obstype", {"ti3r", "trmm"}, {"130", "129"}); assertTypeExpansion("obstype", {"130", "trmm"}, {"130", "129"}); assertTypeExpansion("obstype", {"trmm", "qscat"}, {"129", "130", "137", "138"}); { std::vector values{"sd"}; static MarsLanguage language("retrieve"); language.type("obstype")->expand(values); std::vector expected{"121", "122", "123", "124", "210", "212", "213", "214", "216", "217", "218", "51", "53", "54", "55", "56", "57", "59", "60", "61", "62", "63", "65", "71", "72", "73", "75", "138", "139", "153", "155", "211", "240", "250", "126", "49", "127", "129", "130", "137", "206", "207", "208", "209", "156", "154", "201", "202", "252", "245", "246"}; EXPECT(values.size() >= expected.size()); std::set valuesSet(values.begin(), values.end()); EXPECT_EQUAL(values.size(), valuesSet.size()); for (const auto& v : expected) { EXPECT(valuesSet.find(v) != valuesSet.end()); } } EXPECT_THROWS_AS(assertTypeExpansion("obstype", {"foo"}, {""}), UserError); } //----------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_request.cc0000664000175000017500000002171015232567717017505 0ustar alastairalastair/* * (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. */ /// @file test_request.cc /// @date Jul 2024 /// @author Emanuele Danovaro #include "eckit/log/JSON.h" #include "eckit/types/Date.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/MarsParser.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Type.h" #include "eckit/testing/Test.h" #include "eckit/utils/Tokenizer.h" using namespace eckit::testing; namespace metkit { namespace mars { namespace test { //----------------------------------------------------------------------------- CASE("test_request_json") { { const char* text = "retrieve,class=od,expver=0079,stream=enfh,date=20240729,time=00/" "12,type=fcmean,levtype=sfc,step=24,number=1/to/2,param=mucin/mucape/tprate"; MarsRequest r = MarsRequest::parse(text); { std::stringstream ss; eckit::JSON plain(ss); r.json(plain); EXPECT_EQUAL( ss.str(), "{\"class\":\"od\",\"type\":\"fcmean\",\"stream\":\"enfh\",\"levtype\":\"sfc\",\"date\":\"20240729\"," "\"time\":[\"0000\",\"1200\"],\"step\":\"24\",\"expver\":\"0079\",\"number\":[\"1\",\"2\"],\"param\":[" "\"228236\",\"228235\",\"172228\"],\"domain\":\"g\"}"); } { std::stringstream ss; eckit::JSON array(ss); r.json(array, true); EXPECT_EQUAL( ss.str(), "{\"class\":\"od\",\"type\":\"fcmean\",\"stream\":\"enfh\",\"levtype\":\"sfc\",\"date\":[\"20240729\"]," "\"time\":[\"0000\",\"1200\"],\"step\":[\"24\"],\"expver\":\"0079\",\"number\":[\"1\",\"2\"],\"param\":" "[\"228236\",\"228235\",\"172228\"],\"domain\":\"g\"}"); } } { const char* text = "retrieve,class=od,expver=1,stream=wave,date=20240729,time=00,type=an,levtype=sfc,step=24,param=2dfd "; MarsRequest r = MarsRequest::parse(text); { std::stringstream ss; eckit::JSON plain(ss); r.json(plain); EXPECT_EQUAL(ss.str(), "{\"class\":\"od\",\"type\":\"an\",\"stream\":\"wave\",\"levtype\":\"sfc\",\"date\":" "\"20240729\",\"time\":\"0000\",\"step\":\"24\",\"expver\":\"0001\",\"param\":\"140251\"," "\"domain\":\"g\"}"); } { std::stringstream ss; eckit::JSON array(ss); r.json(array, true); EXPECT_EQUAL(ss.str(), "{\"class\":\"od\",\"type\":\"an\",\"stream\":\"wave\",\"levtype\":\"sfc\",\"date\":[" "\"20240729\"],\"time\":[\"0000\"],\"step\":[\"24\"],\"expver\":\"0001\",\"param\":[" "\"140251\"],\"domain\":\"g\"}"); } } } CASE("test_request_count") { { const char* text = "retrieve,class=od,expver=0079,stream=enfh,date=20240729,time=00/" "12,type=fcmean,levtype=sfc,step=24,number=1/to/2,param=mucin/mucape/tprate"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(12, r.count()); } { const char* text = "retrieve,accuracy=16,class=od,date=20230810,expver=1,levelist=1/to/" "137,levtype=ml,number=-1,param=z,process=local,step=000,stream=scda,time=18,type=an,target=reference.data"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(1, r.count()); } { const char* text = "retrieve,accuracy=16,class=od,date=20230810,expver=1,levelist=1/to/137,levtype=ml,number=-1,param=z/" "t,process=local,step=000,stream=scda,time=18,type=an,target=reference.data"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(138, r.count()); } { const char* text = "retrieve,accuracy=16,class=od,date=20230810,expver=1,levelist=1/to/137,levtype=ml,number=-1,param=22/127/" "128/129/152/u/v,process=local,step=000,stream=scda,time=18,type=an,target=reference.data"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(279, r.count()); } } CASE("test_request_count_single_level_params") { // Test that single-level parameters (like 'z') are only counted when level "1" is in levelist { // levelist includes "1" - single-level param 'z' should be counted const char* text = "retrieve,class=od,date=20230810,expver=1,levelist=1/to/" "137,levtype=ml,param=z,step=000,stream=scda,time=18,type=an"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(1, r.count()); // Only level 1 for param z } { // levelist does NOT include "1" - single-level param 'z' should NOT be counted const char* text = "retrieve,class=od,date=20230810,expver=1,levelist=2/to/" "137,levtype=ml,param=z,step=000,stream=scda,time=18,type=an"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(0, r.count()); // No levels counted for param z when level 1 is absent } { // levelist includes "1" - mix of single-level (z) and multi-level (t) params const char* text = "retrieve,class=od,date=20230810,expver=1,levelist=1/to/137,levtype=ml,param=z/" "t,step=000,stream=scda,time=18,type=an"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(138, r.count()); // 137 levels for t + 1 for z } { // levelist does NOT include "1" - only multi-level param 't' should be counted const char* text = "retrieve,class=od,date=20230810,expver=1,levelist=2/to/137,levtype=ml,param=z/" "t,step=000,stream=scda,time=18,type=an"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(136, r.count()); // 136 levels (2-137) for t, 0 for z } { // levelist includes "1" in the middle of range - single-level params should be counted const char* text = "retrieve,class=od,date=20230810,expver=1,levelist=1/50/100,levtype=ml,param=z/" "t,step=000,stream=scda,time=18,type=an"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(4, r.count()); // 3 levels for t + 1 for z } { // levelist with only level "1" - both param types should be counted const char* text = "retrieve,class=od,date=20230810,expver=1,levelist=1,levtype=ml,param=z/" "t,step=000,stream=scda,time=18,type=an"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(2, r.count()); // 1 level for t + 1 for z } { // Multiple single-level params with level "1" present const char* text = "retrieve,class=od,date=20230810,expver=1,levelist=1/to/10,levtype=ml,param=152/" "z,step=000,stream=scda,time=18,type=an"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(2, r.count()); // 1 for z + 1 for 152 (both single-level params) } { // Multiple single-level params without level "1" const char* text = "retrieve,class=od,date=20230810,expver=1,levelist=2/to/10,levtype=ml,param=152/" "z,step=000,stream=scda,time=18,type=an"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(0, r.count()); // 0 for both single-level params when level 1 is absent } { const char* text = "retrieve,accuracy=16,class=od,date=20230810,expver=1,levelist=1/to/137,levtype=ml,number=-1,param=22/127/" "128/129/152/u/v,process=local,step=000,stream=scda,time=6/18,type=an,target=reference.data"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(558, r.count()); } { const char* text = "retrieve,accuracy=16,class=od,date=20230810,expver=1,levelist=3/to/137,levtype=ml,number=-1,param=22/127/" "128/129/152/u/v,process=local,step=000,stream=scda,time=6/18,type=an,target=reference.data"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(540, r.count()); } { const char* text = "retrieve,accuracy=16,class=od,date=20230810,expver=1,levelist=1/2,levtype=ml,number=-1,param=22/127/" "128/129/152/u/v,process=local,step=0/1/2,stream=scda,time=6/18,type=an,target=reference.data"; MarsRequest r = MarsRequest::parse(text); EXPECT_EQUAL(54, r.count()); } } //----------------------------------------------------------------------------- } // namespace test } // namespace mars } // namespace metkit int main(int argc, char** argv) { return run_tests(argc, argv); } metkit-1.19.2/tests/test_context.cc0000664000175000017500000000272315232567717017504 0ustar alastairalastair/* * (C) Copyright 2025- 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. */ /// @file test_step.cc /// @author Emanuele Danovaro /// @date March 2025 #include #include #include #include "eckit/testing/Test.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/Type.h" namespace metkit::mars::test { using ::eckit::BadValue; //----------------------------------------------------------------------------- CASE("Context match") { Context c; std::set cc{"s2", "ti"}; c.add(std::make_unique("class", cc)); std::set tt{"cf"}; c.add(std::make_unique("type", tt)); std::string text = "retrieve, " "class=ti,date=20250414,time=12,origin=all,expver=all,type=cf,stream=enfo,levtype=sfc,param=2t,step=24,expect=" "any,target=data.reference"; metkit::mars::MarsRequest r = MarsRequest::parse(text, true); EXPECT(c.matches(r)); } //----------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_codes_decoder.cc0000664000175000017500000006571415232567717020613 0ustar alastairalastair/* * (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. */ /// @date Nov 2022 /// @author Philipp Geier #include #include #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" #include "eckit/testing/Test.h" namespace metkit::codes::test { //---------------------------------------------------------------------------------------------------------------------- class MetadataSetter : public eckit::LocalConfiguration { public: using eckit::LocalConfiguration::getDouble; using eckit::LocalConfiguration::getLong; using eckit::LocalConfiguration::getString; using eckit::LocalConfiguration::has; template void setValue(const std::string& key, const T& value) { set(key, value); } template T get(const std::string& key) { T value; eckit::LocalConfiguration::get(key, value); return value; } std::vector keys() { return eckit::LocalConfiguration::keys(); } }; static unsigned char unstr_latlon[] = { 0x47, 0x52, 0x49, 0x42, 0xff, 0xff, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x62, 0x00, 0xff, 0x19, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x04, 0x01, 0x30, 0x30, 0x30, 0x31, 0x00, 0x00, 0x00, 0x23, 0x03, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x65, 0x06, 0x00, 0x00, 0x0a, 0x01, 0x66, 0xa3, 0x41, 0xd2, 0x1d, 0xcf, 0x11, 0xb2, 0x88, 0x0c, 0x0f, 0x16, 0x45, 0xf3, 0xd1, 0xdc, 0x00, 0x00, 0x00, 0x22, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x15, 0x05, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0xff, 0x00, 0x00, 0x00, 0x05, 0x07, 0x37, 0x37, 0x37, 0x37}; #define MD_EXPECT_STRING(md, name, eq) \ EXPECT(md.has(name)); \ std::cout << "expect string for " << name << " to equal " << eq << " (got " << md.getString(name) << ")" \ << std::endl; \ EXPECT_EQUAL(eq, md.getString(name)); // we accept two possible encodings, to enable testing with different versions of ecCodes (ECC-1704) #define MD_EXPECT_STRINGS(md, name, eq1, eq2) \ EXPECT(md.has(name)); \ std::cout << "expect string for " << name << " to equal " << eq1 << " or " << eq2 << " (got " \ << md.getString(name) << ")" << std::endl; \ EXPECT(md.getString(name) == eq1 || md.getString(name) == eq2); #define MD_EXPECT_LONG(md, name, eq) \ EXPECT(md.has(name)); \ std::cout << "expect long for " << name << " to equal " << eq << " (got " << md.getLong(name) << ")" << std::endl; \ EXPECT_EQUAL(eq, md.getLong(name)); #define MD_EXPECT_LONGS(md, name, eq1, eq2) \ EXPECT(md.has(name)); \ std::cout << "expect long for " << name << " to equal " << eq1 << " or " << eq2 << " (got " << md.getLong(name) \ << ")" << std::endl; \ EXPECT(md.getLong(name) == eq1 || md.getLong(name) == eq2); #define MD_EXPECT_GE_LONG(md, name, eq) \ EXPECT(md.has(name)); \ std::cout << "expect long for " << name << " to be greater than or equal to " << eq << " (got " \ << md.getLong(name) << ")" << std::endl; \ EXPECT(md.getLong(name) >= eq); #define MD_EXPECT_DOUBLE(md, name, eq) \ EXPECT(md.has(name)); \ std::cout << "expect double for " << name << " to equal " << std::to_string(eq) << " (got " \ << std::to_string(md.getDouble(name)) << ")" << std::endl; \ EXPECT_EQUAL(std::to_string(eq), std::to_string(md.getDouble(name))); //---------------------------------------------------------------------------------------------------------------------- CASE("test codessplitter unstr_latlot.tmpl Native") { eckit::MemoryHandle data(static_cast(unstr_latlon), sizeof(unstr_latlon)); std::cout << "Data location " << ((size_t)&unstr_latlon) << std::endl; eckit::message::Reader reader(data); eckit::message::Message msg; msg = reader.next(); EXPECT(msg); MetadataSetter md; eckit::message::TypedSetter gatherer{md}; eckit::message::GetMetadataOptions mdOpts{}; mdOpts.valueRepresentation = eckit::message::ValueRepresentation::Native; mdOpts.nameSpace = ""; msg.getMetadata(gatherer, mdOpts); { MD_EXPECT_STRING(md, "globalDomain", "g"); MD_EXPECT_LONG(md, "GRIBEditionNumber", 2); MD_EXPECT_GE_LONG(md, "tablesVersionLatestOfficial", 30); MD_EXPECT_GE_LONG(md, "tablesVersionLatest", 30); MD_EXPECT_LONG(md, "grib2divider", 1000000); MD_EXPECT_LONG(md, "angleSubdivisions", 1000000); MD_EXPECT_LONG(md, "missingValue", 9999); MD_EXPECT_LONG(md, "ieeeFloats", 1); MD_EXPECT_LONG(md, "isHindcast", 0); MD_EXPECT_LONG(md, "section0Length", 16); MD_EXPECT_STRING(md, "identifier", "GRIB"); MD_EXPECT_LONG(md, "discipline", 2); MD_EXPECT_LONG(md, "editionNumber", 2); MD_EXPECT_LONG(md, "totalLength", 159); MD_EXPECT_LONG(md, "section1Length", 21); MD_EXPECT_STRING(md, "centre", "ecmf"); MD_EXPECT_STRING(md, "centreDescription", "European Centre for Medium-Range Weather Forecasts"); MD_EXPECT_LONG(md, "subCentre", 255); MD_EXPECT_LONG(md, "tablesVersion", 25); MD_EXPECT_STRING(md, "masterDir", "grib2/tables/[tablesVersion]"); MD_EXPECT_LONG(md, "localTablesVersion", 0); MD_EXPECT_LONG(md, "significanceOfReferenceTime", 0); MD_EXPECT_LONG(md, "year", 1); MD_EXPECT_LONG(md, "month", 1); MD_EXPECT_LONG(md, "day", 1); MD_EXPECT_LONG(md, "hour", 0); MD_EXPECT_LONG(md, "minute", 0); MD_EXPECT_LONG(md, "second", 0); MD_EXPECT_LONG(md, "dataDate", 10101); MD_EXPECT_DOUBLE(md, "julianDay", 1.7214235e+06); MD_EXPECT_LONG(md, "dataTime", 0); MD_EXPECT_LONG(md, "productionStatusOfProcessedData", 0); MD_EXPECT_STRING(md, "typeOfProcessedData", "an"); MD_EXPECT_LONG(md, "selectStepTemplateInterval", 1); MD_EXPECT_LONG(md, "selectStepTemplateInstant", 1); MD_EXPECT_STRING(md, "stepType", "instant"); MD_EXPECT_LONG(md, "is_chemical", 0); MD_EXPECT_LONG(md, "is_chemical_distfn", 0); MD_EXPECT_LONG(md, "is_chemical_srcsink", 0); MD_EXPECT_LONG(md, "is_aerosol", 0); MD_EXPECT_LONG(md, "is_aerosol_optical", 0); MD_EXPECT_LONG(md, "setCalendarId", 0); MD_EXPECT_LONG(md, "deleteCalendarId", 0); MD_EXPECT_LONG(md, "grib2LocalSectionPresent", 0); MD_EXPECT_LONG(md, "deleteLocalDefinition", 0); MD_EXPECT_LONG(md, "section2Length", 17); MD_EXPECT_LONG(md, "addEmptySection2", 0); MD_EXPECT_LONG(md, "grib2LocalSectionNumber", 1); MD_EXPECT_STRING(md, "marsClass", "od"); MD_EXPECT_STRING(md, "marsType", "an"); MD_EXPECT_STRING(md, "marsStream", "oper"); MD_EXPECT_STRING(md, "experimentVersionNumber", "0001"); MD_EXPECT_STRING(md, "class", "od"); MD_EXPECT_STRING(md, "type", "an"); MD_EXPECT_STRING(md, "stream", "oper"); MD_EXPECT_LONG(md, "productDefinitionTemplateNumberInternal", -1); MD_EXPECT_LONG(md, "localDefinitionNumber", 1); MD_EXPECT_LONG(md, "eps", 0); MD_EXPECT_LONG(md, "addExtraLocalSection", 0); MD_EXPECT_LONG(md, "deleteExtraLocalSection", 0); MD_EXPECT_LONG(md, "extraLocalSectionPresent", 0); MD_EXPECT_LONG(md, "gridDescriptionSectionPresent", 1); MD_EXPECT_LONG(md, "section3Length", 35); MD_EXPECT_LONG(md, "sourceOfGridDefinition", 0); MD_EXPECT_LONG(md, "numberOfDataPoints", 496); MD_EXPECT_LONG(md, "numberOfOctectsForNumberOfPoints", 0); MD_EXPECT_LONG(md, "interpretationOfNumberOfPoints", 0); MD_EXPECT_LONG(md, "PLPresent", 0); MD_EXPECT_LONG(md, "gridDefinitionTemplateNumber", 101); MD_EXPECT_STRING(md, "gridDefinitionDescription", "General unstructured grid"); MD_EXPECT_LONG(md, "shapeOfTheEarth", 6); MD_EXPECT_LONG(md, "numberOfGridUsed", 10); MD_EXPECT_LONG(md, "numberOfGridInReference", 1); MD_EXPECT_STRINGS(md, "unstructuredGridType", "unknown", "DART"); MD_EXPECT_STRING(md, "unstructuredGridSubtype", "T"); MD_EXPECT_STRING(md, "unstructuredGridUUID", "unknown"); MD_EXPECT_STRINGS(md, "gridName", "unknown", "DART_T"); MD_EXPECT_STRING(md, "gridType", "unstructured_grid"); MD_EXPECT_LONG(md, "section4Length", 34); MD_EXPECT_LONG(md, "NV", 0); MD_EXPECT_LONG(md, "neitherPresent", 0); MD_EXPECT_STRING(md, "datasetForLocal", "unknown"); MD_EXPECT_LONG(md, "productDefinitionTemplateNumber", 0); MD_EXPECT_LONG(md, "genVertHeightCoords", 0); MD_EXPECT_LONG(md, "parameterCategory", 0); MD_EXPECT_LONG(md, "parameterNumber", 0); MD_EXPECT_STRING(md, "parameterUnits", "Proportion"); MD_EXPECT_STRING(md, "parameterName", "Land cover (0 = sea, 1 = land)"); MD_EXPECT_LONG(md, "typeOfGeneratingProcess", 2); MD_EXPECT_LONG(md, "backgroundProcess", 0); MD_EXPECT_LONG(md, "generatingProcessIdentifier", 1); MD_EXPECT_LONG(md, "hoursAfterDataCutoff", 0); MD_EXPECT_LONG(md, "minutesAfterDataCutoff", 0); // MD_EXPECT_LONG(md, "indicatorOfUnitOfTimeRange", 1); MD_EXPECT_LONG(md, "stepUnits", 1); MD_EXPECT_LONG(md, "forecastTime", 0); MD_EXPECT_LONG(md, "startStep", 0); MD_EXPECT_LONG(md, "endStep", 0); MD_EXPECT_STRING(md, "stepRange", "0"); MD_EXPECT_LONG(md, "validityDate", 10101); MD_EXPECT_STRINGS(md, "validityTime", "0", "0000"); MD_EXPECT_STRING(md, "typeOfFirstFixedSurface", "168"); MD_EXPECT_STRING(md, "unitsOfFirstFixedSurface", "Numeric"); MD_EXPECT_STRING(md, "nameOfFirstFixedSurface", "Ocean model level"); MD_EXPECT_LONG(md, "scaleFactorOfFirstFixedSurface", 0); MD_EXPECT_LONG(md, "scaledValueOfFirstFixedSurface", 2147483647); MD_EXPECT_LONG(md, "typeOfSecondFixedSurface", 255); MD_EXPECT_STRING(md, "unitsOfSecondFixedSurface", "unknown"); MD_EXPECT_STRING(md, "nameOfSecondFixedSurface", "Missing"); MD_EXPECT_LONG(md, "scaleFactorOfSecondFixedSurface", 0); MD_EXPECT_LONG(md, "scaledValueOfSecondFixedSurface", 2147483647); MD_EXPECT_STRING(md, "pressureUnits", "hPa"); MD_EXPECT_STRING(md, "typeOfLevel", "oceanModel"); MD_EXPECT_LONG(md, "level", 0); MD_EXPECT_LONG(md, "bottomLevel", 0); MD_EXPECT_LONG(md, "topLevel", 0); MD_EXPECT_STRING(md, "tempPressureUnits", "hPa"); MD_EXPECT_STRING(md, "levtype", "o3d"); MD_EXPECT_LONG(md, "PVPresent", 0); MD_EXPECT_STRING(md, "deletePV", "1"); MD_EXPECT_LONGS(md, "lengthOfHeaders", 107, 117); // changed, as a consequence of fixes for ECC-2268 MD_EXPECT_LONG(md, "section5Length", 21); MD_EXPECT_LONG(md, "numberOfValues", 496); MD_EXPECT_LONG(md, "dataRepresentationTemplateNumber", 0); MD_EXPECT_STRING(md, "packingType", "grid_simple"); MD_EXPECT_LONG(md, "referenceValue", 0); MD_EXPECT_DOUBLE(md, "referenceValueError", 1.17549e-38); MD_EXPECT_LONG(md, "binaryScaleFactor", -15); MD_EXPECT_LONG(md, "decimalScaleFactor", 0); MD_EXPECT_LONG(md, "optimizeScaleFactor", 0); MD_EXPECT_LONG(md, "bitsPerValue", 0); MD_EXPECT_LONG(md, "typeOfOriginalFieldValues", 0); MD_EXPECT_LONG(md, "section6Length", 6); MD_EXPECT_LONG(md, "bitMapIndicator", 255); MD_EXPECT_LONG(md, "bitmapPresent", 0); MD_EXPECT_LONG(md, "section7Length", 5); MD_EXPECT_DOUBLE(md, "packingError", 1.17549e-38); MD_EXPECT_DOUBLE(md, "unpackedError", 1.17549e-38); MD_EXPECT_LONG(md, "maximum", 0); MD_EXPECT_LONG(md, "minimum", 0); MD_EXPECT_LONG(md, "average", 0); MD_EXPECT_LONG(md, "numberOfMissing", 0); MD_EXPECT_LONG(md, "standardDeviation", 0); MD_EXPECT_LONG(md, "skewness", 0); MD_EXPECT_LONG(md, "kurtosis", 0); MD_EXPECT_LONG(md, "isConstant", 1); MD_EXPECT_LONG(md, "changeDecimalPrecision", 0); MD_EXPECT_LONG(md, "decimalPrecision", 0); MD_EXPECT_LONG(md, "setBitsPerValue", 0); MD_EXPECT_LONG(md, "getNumberOfValues", 496); MD_EXPECT_LONG(md, "scaleValuesBy", 1); MD_EXPECT_LONG(md, "offsetValuesBy", 0); MD_EXPECT_STRING(md, "productType", "unknown"); MD_EXPECT_LONG(md, "section8Length", 4); MD_EXPECT_STRING(md, "7777", "7777"); MD_EXPECT_STRING(md, "uuidOfHGrid", "66a341d21dcf11b2880c0f1645f3d1dc"); } } //---------------------------------------------------------------------------------------------------------------------- CASE("test codessplitter unstr_latlot.tmpl String") { eckit::MemoryHandle data(static_cast(unstr_latlon), sizeof(unstr_latlon)); std::cout << "Data location " << ((size_t)&unstr_latlon) << std::endl; eckit::message::Reader reader(data); eckit::message::Message msg; msg = reader.next(); EXPECT(msg); MetadataSetter md; eckit::message::TypedSetter gatherer{md}; eckit::message::GetMetadataOptions mdOpts{}; mdOpts.valueRepresentation = eckit::message::ValueRepresentation::String; mdOpts.nameSpace = ""; msg.getMetadata(gatherer, mdOpts); { MD_EXPECT_STRING(md, "globalDomain", "g"); MD_EXPECT_STRING(md, "GRIBEditionNumber", "2"); // This is not easy to test, as the latest official version can increment... // MD_EXPECT_STRING(md, "tablesVersionLatestOfficial", "30"); // MD_EXPECT_STRING(md, "tablesVersionLatest", "30"); MD_EXPECT_STRING(md, "grib2divider", "1e+06"); MD_EXPECT_STRING(md, "angleSubdivisions", "1e+06"); MD_EXPECT_STRING(md, "missingValue", "9999"); MD_EXPECT_STRING(md, "ieeeFloats", "1"); MD_EXPECT_STRING(md, "isHindcast", "0"); MD_EXPECT_STRING(md, "section0Length", "16"); MD_EXPECT_STRING(md, "identifier", "GRIB"); MD_EXPECT_STRING(md, "discipline", "2"); MD_EXPECT_STRING(md, "editionNumber", "2"); MD_EXPECT_STRING(md, "totalLength", "159"); MD_EXPECT_STRING(md, "section1Length", "21"); MD_EXPECT_STRING(md, "centre", "ecmf"); MD_EXPECT_STRING(md, "centreDescription", "European Centre for Medium-Range Weather Forecasts"); MD_EXPECT_STRING(md, "subCentre", "255"); MD_EXPECT_STRING(md, "tablesVersion", "25"); MD_EXPECT_STRING(md, "masterDir", "grib2/tables/[tablesVersion]"); MD_EXPECT_STRING(md, "localTablesVersion", "0"); MD_EXPECT_STRING(md, "significanceOfReferenceTime", "0"); MD_EXPECT_STRING(md, "year", "1"); MD_EXPECT_STRING(md, "month", "1"); MD_EXPECT_STRING(md, "day", "1"); MD_EXPECT_STRING(md, "hour", "0"); MD_EXPECT_STRING(md, "minute", "0"); MD_EXPECT_STRING(md, "second", "0"); MD_EXPECT_STRING(md, "dataDate", "10101"); MD_EXPECT_STRING(md, "julianDay", "1.72142e+06"); MD_EXPECT_STRING(md, "dataTime", "0000"); MD_EXPECT_STRING(md, "productionStatusOfProcessedData", "0"); MD_EXPECT_STRING(md, "typeOfProcessedData", "an"); MD_EXPECT_STRING(md, "selectStepTemplateInterval", "1"); MD_EXPECT_STRING(md, "selectStepTemplateInstant", "1"); MD_EXPECT_STRING(md, "stepType", "instant"); MD_EXPECT_STRING(md, "is_chemical", "0"); MD_EXPECT_STRING(md, "is_chemical_distfn", "0"); MD_EXPECT_STRING(md, "is_chemical_srcsink", "0"); MD_EXPECT_STRING(md, "is_aerosol", "0"); MD_EXPECT_STRING(md, "is_aerosol_optical", "0"); MD_EXPECT_STRING(md, "setCalendarId", "0"); MD_EXPECT_STRING(md, "deleteCalendarId", "0"); MD_EXPECT_STRING(md, "grib2LocalSectionPresent", "0"); MD_EXPECT_STRING(md, "deleteLocalDefinition", "0"); MD_EXPECT_STRING(md, "section2Length", "17"); MD_EXPECT_STRING(md, "addEmptySection2", "0"); MD_EXPECT_STRING(md, "grib2LocalSectionNumber", "1"); MD_EXPECT_STRING(md, "marsClass", "od"); MD_EXPECT_STRING(md, "marsType", "an"); MD_EXPECT_STRING(md, "marsStream", "oper"); MD_EXPECT_STRING(md, "experimentVersionNumber", "0001"); MD_EXPECT_STRING(md, "class", "od"); MD_EXPECT_STRING(md, "type", "an"); MD_EXPECT_STRING(md, "stream", "oper"); MD_EXPECT_STRING(md, "productDefinitionTemplateNumberInternal", "-1"); MD_EXPECT_STRING(md, "localDefinitionNumber", "1"); MD_EXPECT_STRING(md, "eps", "0"); MD_EXPECT_STRING(md, "addExtraLocalSection", "0"); MD_EXPECT_STRING(md, "deleteExtraLocalSection", "0"); MD_EXPECT_STRING(md, "extraLocalSectionPresent", "0"); MD_EXPECT_STRING(md, "gridDescriptionSectionPresent", "1"); MD_EXPECT_STRING(md, "section3Length", "35"); MD_EXPECT_STRING(md, "sourceOfGridDefinition", "0"); MD_EXPECT_STRING(md, "numberOfDataPoints", "496"); MD_EXPECT_STRING(md, "numberOfOctectsForNumberOfPoints", "0"); MD_EXPECT_STRING(md, "interpretationOfNumberOfPoints", "0"); MD_EXPECT_STRING(md, "PLPresent", "0"); MD_EXPECT_STRING(md, "gridDefinitionTemplateNumber", "101"); MD_EXPECT_STRING(md, "gridDefinitionDescription", "General unstructured grid"); MD_EXPECT_STRING(md, "shapeOfTheEarth", "6"); MD_EXPECT_STRING(md, "numberOfGridUsed", "10"); MD_EXPECT_STRING(md, "numberOfGridInReference", "1"); MD_EXPECT_STRING(md, "uuidOfHGrid", "66a341d21dcf11b2880c0f1645f3d1dc"); MD_EXPECT_STRINGS(md, "unstructuredGridType", "unknown", "DART"); MD_EXPECT_STRING(md, "unstructuredGridSubtype", "T"); MD_EXPECT_STRING(md, "unstructuredGridUUID", "unknown"); MD_EXPECT_STRINGS(md, "gridName", "unknown", "DART_T"); MD_EXPECT_STRING(md, "gridType", "unstructured_grid"); MD_EXPECT_STRING(md, "section4Length", "34"); MD_EXPECT_STRING(md, "NV", "0"); MD_EXPECT_STRING(md, "neitherPresent", "0"); MD_EXPECT_STRING(md, "datasetForLocal", "unknown"); MD_EXPECT_STRING(md, "productDefinitionTemplateNumber", "0"); MD_EXPECT_STRING(md, "genVertHeightCoords", "0"); MD_EXPECT_STRING(md, "parameterCategory", "0"); MD_EXPECT_STRING(md, "parameterNumber", "0"); MD_EXPECT_STRING(md, "parameterUnits", "Proportion"); MD_EXPECT_STRING(md, "parameterName", "Land cover (0 = sea, 1 = land)"); MD_EXPECT_STRING(md, "typeOfGeneratingProcess", "2"); MD_EXPECT_STRING(md, "backgroundProcess", "0"); MD_EXPECT_STRING(md, "generatingProcessIdentifier", "1"); MD_EXPECT_STRING(md, "hoursAfterDataCutoff", "0"); MD_EXPECT_STRING(md, "minutesAfterDataCutoff", "0"); // MD_EXPECT_STRING(md, "indicatorOfUnitOfTimeRange", "h"); MD_EXPECT_STRING(md, "stepUnits", "h"); MD_EXPECT_STRING(md, "forecastTime", "0"); MD_EXPECT_STRING(md, "startStep", "0"); MD_EXPECT_STRING(md, "endStep", "0"); MD_EXPECT_STRING(md, "stepRange", "0"); MD_EXPECT_STRING(md, "validityDate", "10101"); MD_EXPECT_STRINGS(md, "validityTime", "0", "0000"); MD_EXPECT_STRING(md, "typeOfFirstFixedSurface", "168"); MD_EXPECT_STRING(md, "unitsOfFirstFixedSurface", "Numeric"); MD_EXPECT_STRING(md, "nameOfFirstFixedSurface", "Ocean model level"); MD_EXPECT_STRING(md, "scaleFactorOfFirstFixedSurface", "0"); MD_EXPECT_STRING(md, "scaledValueOfFirstFixedSurface", "MISSING"); MD_EXPECT_STRING(md, "typeOfSecondFixedSurface", "255"); MD_EXPECT_STRING(md, "unitsOfSecondFixedSurface", "unknown"); MD_EXPECT_STRING(md, "nameOfSecondFixedSurface", "Missing"); MD_EXPECT_STRING(md, "scaleFactorOfSecondFixedSurface", "0"); MD_EXPECT_STRING(md, "scaledValueOfSecondFixedSurface", "MISSING"); MD_EXPECT_STRING(md, "pressureUnits", "hPa"); MD_EXPECT_STRING(md, "typeOfLevel", "oceanModel"); MD_EXPECT_STRING(md, "level", "0"); MD_EXPECT_STRING(md, "bottomLevel", "0"); MD_EXPECT_STRING(md, "topLevel", "0"); MD_EXPECT_STRING(md, "tempPressureUnits", "hPa"); MD_EXPECT_STRING(md, "levtype", "o3d"); MD_EXPECT_STRING(md, "PVPresent", "0"); MD_EXPECT_STRING(md, "deletePV", "1"); MD_EXPECT_STRINGS(md, "lengthOfHeaders", "107", "117"); // changed, as a consequence of fixes for ECC-2268 MD_EXPECT_STRING(md, "section5Length", "21"); MD_EXPECT_STRING(md, "numberOfValues", "496"); MD_EXPECT_STRING(md, "dataRepresentationTemplateNumber", "0"); MD_EXPECT_STRING(md, "packingType", "grid_simple"); MD_EXPECT_STRING(md, "referenceValue", "0"); MD_EXPECT_STRING(md, "referenceValueError", "1.17549e-38"); MD_EXPECT_STRING(md, "binaryScaleFactor", "-15"); MD_EXPECT_STRING(md, "decimalScaleFactor", "0"); MD_EXPECT_STRING(md, "optimizeScaleFactor", "0"); MD_EXPECT_STRING(md, "bitsPerValue", "0"); MD_EXPECT_STRING(md, "typeOfOriginalFieldValues", "0"); MD_EXPECT_STRING(md, "section6Length", "6"); MD_EXPECT_STRING(md, "bitMapIndicator", "255"); MD_EXPECT_STRING(md, "bitmapPresent", "0"); MD_EXPECT_STRING(md, "section7Length", "5"); MD_EXPECT_STRING(md, "packingError", "1.17549e-38"); MD_EXPECT_STRING(md, "unpackedError", "1.17549e-38"); MD_EXPECT_STRING(md, "maximum", "0"); MD_EXPECT_STRING(md, "minimum", "0"); MD_EXPECT_STRING(md, "average", "0"); MD_EXPECT_STRING(md, "numberOfMissing", "0"); MD_EXPECT_STRING(md, "standardDeviation", "0"); MD_EXPECT_STRING(md, "skewness", "0"); MD_EXPECT_STRING(md, "kurtosis", "0"); MD_EXPECT_STRING(md, "isConstant", "1"); MD_EXPECT_STRING(md, "changeDecimalPrecision", "0"); MD_EXPECT_STRING(md, "decimalPrecision", "0"); MD_EXPECT_STRING(md, "setBitsPerValue", "0"); MD_EXPECT_STRING(md, "getNumberOfValues", "496"); MD_EXPECT_STRING(md, "scaleValuesBy", "1"); MD_EXPECT_STRING(md, "offsetValuesBy", "0"); MD_EXPECT_STRING(md, "productType", "unknown"); MD_EXPECT_STRING(md, "section8Length", "4"); MD_EXPECT_STRING(md, "7777", "7777"); } } 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); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::codes::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/tools/0000775000175000017500000000000015232567717015606 5ustar alastairalastairmetkit-1.19.2/tests/tools/d1.grib0000664000175000017500000005661015232567717016767 0ustar alastairalastairGRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"eR* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"g R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"g R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"gR* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"gR* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($"R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($: R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($: R* 7777GRIBbT" s0000000. aad3HR(Z-Z-p^@$(-2<@HHKQZ`dlxx}   ,,@@@Dhhhhhhww@@@@@@XXXX      **```````````88888888888888eeeeeeeeeeeeeeeeeeeeeeeeeeee88888888888888```````````**      XXXX@@@@@@wwhhhhhhD@@@,,   }xxld`ZQKHH@<2-($: R* 7777metkit-1.19.2/tests/tools/grib-to-request.sh.in0000775000175000017500000000167215232567717021611 0ustar alastairalastair#!/usr/bin/env bash set -eux yell() { printf "$(basename "$0"): \033[0;31m!!! %s !!!\033[0m\\n" "$*" >&2; } die() { yell "$*"; exit 1; } line_count() { [[ $# -eq 1 ]] || die "line_count requires 1 argument; expected line count" val=$(wc -l < out) && val=$((val + 0)) [[ $val -eq $1 ]] || die "Incorrect count => [$val != $1]" } grep_count() { [[ $# -eq 2 ]] || die "grep_count requires 2; regex and expected count" val=$(grep -cE "$1" out) [[ $val -eq $2 ]] || die "Incorrect count [$val != $2] for regex [$1]" } g2r="$" srcdir=@CMAKE_CURRENT_SOURCE_DIR@ bindir=@CMAKE_CURRENT_BINARY_DIR@ export ECCODES_DEFINITION_PATH="@ECCODES_DEFINITION_PATH@" $g2r $srcdir/d1.grib | tee out line_count 16 grep_count "step=0" 13 grep_count "step=12" 3 grep_count "timespan=fs" 3 $g2r --one $srcdir/d1.grib | tee out line_count 1 $g2r --compact $srcdir/d1.grib | tee out line_count 2 grep_count "timespan=fs" 1 metkit-1.19.2/tests/tools/CMakeLists.txt0000664000175000017500000000050315232567717020344 0ustar alastairalastair if ( HAVE_GRIB AND HAVE_BUILD_TOOLS ) list( APPEND metkit_tools grib-to-request ) foreach( _test ${metkit_tools} ) ecbuild_configure_file( ${_test}.sh.in ${_test}.sh @ONLY ) ecbuild_add_test( TYPE SCRIPT COMMAND ${_test}.sh ) endforeach( ) endif() metkit-1.19.2/tests/test_multihandle.cc0000664000175000017500000000356415232567717020332 0ustar alastairalastair/* * (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. */ /// @file test_MetFile.cc /// @date Jan 2016 /// @author Florian Rathgeber #include "eckit/io/FileHandle.h" #include "eckit/io/MultiHandle.h" #include "eckit/testing/Test.h" #include "eccodes.h" namespace metkit::grib::test { //---------------------------------------------------------------------------------------------------------------------- CASE("openf filehandle") { eckit::FileHandle dh("latlon.grib"); FILE* f = dh.openf("r"); codes_handle* h; int err = 0; size_t count = 0; while ((h = codes_handle_new_from_file(nullptr, f, PRODUCT_ANY, &err))) { count++; codes_handle_delete(h); } fclose(f); EXPECT(count == 1); EXPECT(err == GRIB_SUCCESS); } //---------------------------------------------------------------------------------------------------------------------- CASE("openf multihandle") { eckit::MultiHandle dh; dh += new eckit::FileHandle("latlon.grib"); dh += new eckit::FileHandle("latlon.grib"); FILE* f = dh.openf("r"); codes_handle* h; int err = 0; size_t count = 0; while ((h = codes_handle_new_from_file(nullptr, f, PRODUCT_ANY, &err))) { count++; codes_handle_delete(h); } fclose(f); EXPECT(count == 2); EXPECT(err == GRIB_SUCCESS); } //----------------------------------------------------------------------------- } // namespace metkit::grib::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/marsgen/0000775000175000017500000000000015232567717016102 5ustar alastairalastairmetkit-1.19.2/tests/marsgen/metkit_marsgen_legacy.sh.in0000775000175000017500000000166215232567717023410 0ustar alastairalastair#!/bin/bash set -uex [ -e outfile.legacy ] && rm outfile.legacy MARSGEN="@CMAKE_BINARY_DIR@/bin/mars-archive-script" GENFILE="@CMAKE_CURRENT_SOURCE_DIR@/genfile.legacy" OUTFILE="outfile.legacy" CHECKFILE="checkfile.legacy" $MARSGEN --in="$GENFILE" --out="$OUTFILE" cat << EOF > $CHECKFILE \$RETRIEVE_MARS << @ retrieve, class=EA, stream=LWDA, expver=9999, type=analysis, anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass=runda, date=20200601, time=06, step=00, target=mars.source.0 @ \$RETRIEVE_COMPLETE \$UNBLOCK mars.source.0 mars.archive.0 \$ARCHIVE_MARS << @ archive, class=EA, stream=DA, expver=9999, type=analysis, anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass=runda, date=20200601, time=06, step=00, source=mars.archive.0 @ \$ARCHIVE_COMPLETE EOF cmp $CHECKFILE $OUTFILE metkit-1.19.2/tests/marsgen/metkit_marsgen_simple.sh.in0000775000175000017500000000165315232567717023435 0ustar alastairalastair#!/bin/bash set -uex [ -e outfile.simple ] && rm outfile.simple MARSGEN="@CMAKE_BINARY_DIR@/bin/mars-archive-script" GENFILE="@CMAKE_CURRENT_SOURCE_DIR@/genfile" OUTFILE="outfile.simple" CHECKFILE="checkfile.simple" $MARSGEN --in="$GENFILE" --out="$OUTFILE" cat << EOF > $CHECKFILE \$RETRIEVE_MARS << @ retrieve, class=EA, stream=LWDA, expver=9999, type=analysis, anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass=runda, date=20200601, time=06, step=00, target=mars.source.0 @ \$RETRIEVE_COMPLETE \$UNBLOCK mars.source.0 mars.archive.0 \$ARCHIVE_MARS << @ archive, class=EA, stream=DA, expver=9999, type=analysis, anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass=runda, date=20200601, time=06, step=00, source=mars.archive.0 @ \$ARCHIVE_COMPLETE EOF cmp $CHECKFILE $OUTFILE metkit-1.19.2/tests/marsgen/genfile.default0000664000175000017500000000055015232567717021061 0ustar alastairalastairverb, default@expver = 9999, default@class = EA, default@stream = LWDA, class=EA, stream=LWDA, expver=1001, type=analysis,anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass="runda", date=20200601, time=06, step=00 metkit-1.19.2/tests/marsgen/metkit_marsgen_compare.sh.in0000775000175000017500000000164315232567717023571 0ustar alastairalastair#!/bin/bash set -uex [ -e outfile.compare ] && rm outfile.compare MARSGEN="@CMAKE_BINARY_DIR@/bin/mars-archive-script" GENFILE="@CMAKE_CURRENT_SOURCE_DIR@/genfile" OUTFILE="outfile.compare" CHECKFILE="checkfile.compare" $MARSGEN < $GENFILE > $OUTFILE cat << EOF > $CHECKFILE \$RETRIEVE_MARS << @ retrieve, class=EA, stream=LWDA, expver=9999, type=analysis, anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass=runda, date=20200601, time=06, step=00, target=mars.source.0 @ \$RETRIEVE_COMPLETE \$UNBLOCK mars.source.0 mars.archive.0 \$ARCHIVE_MARS << @ archive, class=EA, stream=DA, expver=9999, type=analysis, anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass=runda, date=20200601, time=06, step=00, source=mars.archive.0 @ \$ARCHIVE_COMPLETE EOF cmp $CHECKFILE $OUTFILEmetkit-1.19.2/tests/marsgen/genfile.legacy0000664000175000017500000000071115232567717020700 0ustar alastairalastairverb, fdb_retrieve@expver = 9999, archive@expver = 9999, fdb_retrieve@class = EA, archive@class = EA, fdb_retrieve@stream = LWDA, archive@stream = DA, class=EA, stream=LWDA, expver=1001, type=analysis,anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass="runda", date=20200601, time=06, step=00 metkit-1.19.2/tests/marsgen/genfile0000664000175000017500000000067515232567717017446 0ustar alastairalastairverb, retrieve@expver = 9999, archive@expver = 9999, retrieve@class = EA, archive@class = EA, retrieve@stream = LWDA, archive@stream = DA, class=EA, stream=LWDA, expver=1001, type=analysis,anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass="runda", date=20200601, time=06, step=00 metkit-1.19.2/tests/marsgen/metkit_marsgen_obs.sh.in0000775000175000017500000000117115232567717022722 0ustar alastairalastair#!/bin/bash set -uex [ -e outfile.obs ] && rm outfile.obs MARSGEN="@CMAKE_BINARY_DIR@/bin/mars-archive-script" GENFILE="@CMAKE_CURRENT_SOURCE_DIR@/genfile" OUTFILE="outfile.obs" CHECKFILE="checkfile.obs" $MARSGEN --obs < $GENFILE > $OUTFILE cat << EOF > $CHECKFILE \$PREOBS \$header.1 \$data.1 mars.archive.0 \$ARCHIVE_MARS << @ archive, class=EA, stream=DA, expver=9999, type=analysis, anoffset=3, param=129/152/130/138/155/75/76/133/203.128/246/247/248, levtype=modellevels, levels=1/to/137, pass=runda, date=20200601, time=06, step=00, source=mars.archive.0 @ \$ARCHIVE_COMPLETE EOF cmp $CHECKFILE $OUTFILE metkit-1.19.2/tests/marsgen/CMakeLists.txt0000664000175000017500000000052315232567717020642 0ustar alastairalastair list( APPEND marsgen_tests simple compare obs legacy) foreach( _test ${marsgen_tests} ) configure_file( metkit_marsgen_${_test}.sh.in metkit_marsgen_${_test}.sh @ONLY ) ecbuild_add_test( TYPE SCRIPT CONDITION HAVE_BUILD_TOOLS COMMAND metkit_marsgen_${_test}.sh ) endforeach()metkit-1.19.2/tests/test_type_levelist.cc0000664000175000017500000000616015232567717020707 0ustar alastairalastair/* * (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. */ /// @file test_levelist.cc /// @author Metin Cakircali /// @author Emanuele Danovaro /// @date November 2023 #include #include #include #include "eckit/testing/Test.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/Type.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars::test { //----------------------------------------------------------------------------- CASE("test_metkit_exists_to-by-list-float") { std::stringstream ss; TypesFactory::list(ss); const std::string str(ss.str()); EXPECT(str.find_first_of("to-by-list-float")); } void assertTypeExpansion(const std::string& name, std::vector values, const std::vector& expected) { static MarsLanguage language("retrieve"); language.type(name)->expand(values); EXPECT_EQUAL(values, expected); } CASE("test_metkit_expand_levelist") { // by > 0 assertTypeExpansion("levelist", {"-1", "to", "2", "by", "0.5"}, {"-1", "-0.5", "0", ".5", "1", "1.5", "2"}); // by > 0 assertTypeExpansion("levelist", {"-10.0", "to", "2.0", "by", "1"}, {"-10", "-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", "0", "1", "2"}); // by > 0 && from < to assertTypeExpansion("levelist", {"4", "to", "20", "by", "4"}, {"4", "8", "12", "16", "20"}); assertTypeExpansion("levelist", {"4", "to", "18", "by", "4"}, {"4", "8", "12", "16"}); // by > 0 && from > to assertTypeExpansion("levelist", {"20", "to", "4", "by", "4"}, {"20", "16", "12", "8", "4"}); // by = 0 EXPECT_THROWS_AS(assertTypeExpansion("levelist", {"4", "to", "20", "by", "0"}, {"4"}), eckit::BadValue); EXPECT_THROWS_AS(assertTypeExpansion("levelist", {"-1", "to", "2", "by", "0"}, {"-1"}), eckit::BadValue); // by < 0 && from > to assertTypeExpansion("levelist", {"20", "to", "4", "by", "-4"}, {"20", "16", "12", "8", "4"}); assertTypeExpansion("levelist", {"10", "to", "4", "by", "-2"}, {"10", "8", "6", "4"}); assertTypeExpansion("levelist", {"-2", "to", "-4", "by", "-0.5"}, {"-2", "-2.5", "-3", "-3.5", "-4"}); assertTypeExpansion("levelist", {"0", "to", "-2", "by", "-0.5"}, {"0", "-0.5", "-1", "-1.5", "-2"}); // by < 0 && from < to EXPECT_THROWS_AS(assertTypeExpansion("levelist", {"4", "to", "10", "by", "-4"}, {"4", "8", "12", "16", "20"}), eckit::BadValue); EXPECT_THROWS_AS( assertTypeExpansion("levelist", {"-4", "to", "2", "by", "-0.5"}, {"0", "-0.5", "-1", "-1.5", "-2"}), eckit::BadValue); } } // namespace metkit::mars::test //----------------------------------------------------------------------------- int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_filter.cc0000664000175000017500000001213715232567717017305 0ustar alastairalastair/* * (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. */ /// @file test_filter.cc /// @date Apr 2025 /// @author Emanuele Danovaro #include #include #include #include #include #include #include "eckit/testing/Test.h" #include "eckit/types/Date.h" #include "eckit/utils/StringTools.h" #include "eckit/utils/Tokenizer.h" #include "metkit/mars/MarsParser.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Type.h" namespace metkit::mars::test { namespace { using ExpectedVals = std::map>; } //----------------------------------------------------------------------------- void filter(MarsRequest& r, const MarsRequest& f, const ExpectedVals& expected, const std::vector dates) { r.filter(f); for (const auto& e : expected) { if (!r.has(e.first)) { std::cerr << eckit::Colour::red << "Missing keyword: " << e.first << eckit::Colour::reset << std::endl; } EXPECT(r.has(e.first)); auto vv = r.values(e.first); if (e.first != "date") { // dates are verified at a later stage EXPECT_EQUAL(e.second.size(), vv.size()); } for (int i = 0; i < vv.size(); i++) { if (e.first == "grid") { EXPECT_EQUAL(eckit::StringTools::upper(e.second.at(i)), vv.at(i)); } else { EXPECT_EQUAL(e.second.at(i), vv.at(i)); } } } if (dates.size() > 0) { EXPECT(r.has("date")); auto dd = r.values("date"); EXPECT_EQUAL(dates.size(), dd.size()); for (int i = 0; i < dates.size(); i++) { long d = dates.at(i); if (d < 0) { eckit::Date day(d); d = day.yyyymmdd(); } EXPECT_EQUAL(std::to_string(d), dd.at(i)); } } } void filter(const std::string& text, const std::string& filter_text, const ExpectedVals& expected, std::vector dates, bool strict = true) { MarsRequest r = MarsRequest::parse(text, strict); std::string f_text = "filter," + filter_text; std::istringstream in(f_text); metkit::mars::MarsParser parser(in); std::vector f = parser.parse(); ASSERT(f.size() == 1); filter(r, f[0], expected, std::move(dates)); } void expand(const std::string& text, const std::string& filter_text, const std::string& expected, bool strict = true, std::vector dates = {}) { ExpectedVals out; eckit::Tokenizer c(","); eckit::Tokenizer e("="); eckit::Tokenizer s("/"); eckit::StringList tokens; c(expected, tokens); std::string verb; for (const auto& t : tokens) { if (verb.empty()) { verb = eckit::StringTools::lower(t); continue; } auto tt = eckit::StringTools::trim(t); eckit::StringList kv; e(tt, kv); EXPECT_EQUAL(2, kv.size()); auto key = eckit::StringTools::lower(eckit::StringTools::trim(kv[0])); if (key == "date") { EXPECT_EQUAL(0, dates.size()); } eckit::StringList vals; s(kv[1], vals); std::vector vv; for (auto v : vals) { auto val = eckit::StringTools::trim(v); if (key != "source" && key != "target") { val = eckit::StringTools::lower(val); } if (key == "date") { dates.push_back(std::stol(val)); } else { vv.push_back(val); } } if (key != "date") { out.emplace(key, vv); } } MarsRequest r = MarsRequest::parse(text, strict); std::string f_text = "filter," + filter_text; std::istringstream in(f_text); metkit::mars::MarsParser parser(in); std::vector f = parser.parse(); ASSERT(f.size() == 1); filter(r, f[0], out, std::move(dates)); } CASE("day") { const char* text = "ret,date=20250301/to/20250306"; const char* filter_text = "day=1/3/5/7/9/11/13/15/17/19/21/23/25/27/29/31"; ExpectedVals expected{{"class", {"od"}}, {"domain", {"g"}}, {"expver", {"0001"}}, {"levelist", {"1000", "850", "700", "500", "400", "300"}}, {"levtype", {"pl"}}, {"param", {"129"}}, {"step", {"0"}}, {"stream", {"oper"}}, {"time", {"1200"}}, {"type", {"an"}}}; filter(text, filter_text, expected, {20250301, 20250303, 20250305}); } //----------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_time.cc0000664000175000017500000001051215232567717016751 0ustar alastairalastair/* * (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. */ /// @file test_time.cc /// @author Simon Smart /// @author Emanuele Danovaro /// @date April 2020 #include #include #include "eckit/testing/Test.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/Type.h" #include "metkit/mars/TypeTime.h" namespace metkit::mars::test { using ::eckit::BadTime; using ::eckit::BadValue; using ::eckit::Value; //----------------------------------------------------------------------------- void assertTypeExpansion(const std::string& name, std::vector values, const std::vector& expected) { static MarsLanguage language("retrieve"); language.type(name)->expand(values); EXPECT_EQUAL(values.size(), expected.size()); EXPECT_EQUAL(expected, values); } void checkExpansion(const Type& tt, const std::string& value, const std::string& expected) { auto expanded = tt.tidy(value); EXPECT_EQUAL(expected, expanded); } CASE("Test TypeTime expansions") { TypeTime ttime("time", Value()); Type& tt(ttime); // 1 and 2-digit times checkExpansion(tt, "0", "0000"); checkExpansion(tt, "00", "0000"); checkExpansion(tt, "12", "1200"); checkExpansion(tt, "6", "0600"); checkExpansion(tt, "06", "0600"); // 3 and 4-digit times checkExpansion(tt, "000", "0000"); checkExpansion(tt, "0000", "0000"); checkExpansion(tt, "012", "0012"); checkExpansion(tt, "0012", "0012"); checkExpansion(tt, "1234", "1234"); checkExpansion(tt, "623", "0623"); checkExpansion(tt, "0623", "0623"); { EXPECT_THROWS_AS(auto vv = tt.tidy("675"), BadTime); } // 5 and 6-digit times checkExpansion(tt, "00000", "0000"); checkExpansion(tt, "001200", "0012"); checkExpansion(tt, "123400", "1234"); checkExpansion(tt, "062300", "0623"); checkExpansion(tt, "62300", "0623"); { // We don't support seconds yet. EXPECT_THROWS_AS(auto vv = tt.tidy("000012"), BadValue); } { // We don't support seconds yet. EXPECT_THROWS_AS(auto vv = tt.tidy("123456"), BadValue); } { // We don't support time > 24h EXPECT_THROWS_AS(auto vv = tt.tidy("283456"), BadValue); } // times with colons checkExpansion(tt, "0:0", "0000"); checkExpansion(tt, "00:00", "0000"); checkExpansion(tt, "00:00:00", "0000"); checkExpansion(tt, "0:12", "0012"); checkExpansion(tt, "00:12", "0012"); checkExpansion(tt, "12:34", "1234"); checkExpansion(tt, "6:23", "0623"); checkExpansion(tt, "06:23", "0623"); checkExpansion(tt, "00:12:00", "0012"); checkExpansion(tt, "12:34:00", "1234"); checkExpansion(tt, "06:23:00", "0623"); checkExpansion(tt, "6:23:00", "0623"); { EXPECT_THROWS_AS(auto vv = tt.tidy("00:62"), BadTime); } { // We don't support seconds yet. EXPECT_THROWS_AS(auto vv = tt.tidy("00:00:12"), BadValue); } { // We don't support seconds yet. EXPECT_THROWS_AS(auto vv = tt.tidy("12:34:56"), BadValue); } // times with units assertTypeExpansion("time", {"0h"}, {"0000"}); assertTypeExpansion("time", {"00H"}, {"0000"}); assertTypeExpansion("time", {"60m"}, {"0100"}); assertTypeExpansion("time", {"2h30m"}, {"0230"}); assertTypeExpansion("time", {"60s"}, {"0001"}); EXPECT_THROWS_AS(assertTypeExpansion("time", {"6s"}, {"0000"}), BadValue); EXPECT_THROWS_AS(assertTypeExpansion("time", {"25"}, {"0000"}), BadValue); // from to by assertTypeExpansion("time", {"0", "to", "18"}, {"0000", "0600", "1200", "1800"}); assertTypeExpansion("time", {"0", "to", "6", "by", "1"}, {"0000", "0100", "0200", "0300", "0400", "0500", "0600"}); assertTypeExpansion("time", {"0", "to", "1h30m", "by", "30m"}, {"0000", "0030", "0100", "0130"}); } //----------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_language.cc0000664000175000017500000003134315232567717017603 0ustar alastairalastair/* * (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 #include #include #include #include #include "eckit/exception/Exceptions.h" #include "eckit/testing/Test.h" #include "eckit/types/Date.h" #include "eckit/value/Value.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Type.h" #include "metkit/mars/TypeAny.h" #include "metkit/mars/TypeDate.h" #include "metkit/mars/TypeEnum.h" #include "metkit/mars/TypeExpver.h" #include "metkit/mars/TypeMixed.h" #include "metkit/mars/TypeParam.h" #include "metkit/mars/TypesFactory.h" #define EXPECT_EMPTY(request, key) EXPECT_EQUAL(request.values(key, true).size(), 0); namespace metkit::mars::test { struct Expected { const std::string verb; std::map> keyvalue; }; using Sequence = std::vector; Sequence make_random_sequence(Sequence::value_type min, Sequence::value_type max, Sequence::size_type N) { static std::mt19937 gen{std::random_device{}()}; static std::uniform_int_distribution dist(min, max); Sequence s(N); std::generate(s.begin(), s.end(), [&] { return dist(gen); }); return s; } Sequence make_power_sequence(Sequence::value_type min, Sequence::value_type max) { Sequence s; for (auto i = min; i <= max; i *= 2) { s.push_back(i); } return s; } void expect_mars(const std::string& text, const Expected& expected, bool strict = false) { auto r = MarsRequest::parse(text, strict); EXPECT_EQUAL(expected.verb, r.verb()); for (const auto& kv : expected.keyvalue) { auto v = r.values(kv.first); if (kv.first == "grid") { EXPECT(kv.second.size() == 1 && v.size() == 1 && v[0] == kv.second[0]); continue; } EXPECT_EQUAL(kv.second, kv.second); } } // ----------------------------------------------------------------------------- CASE("grid: regular Gaussian grids") { const Sequence F{16, 24, 32, 48, 64, 80, 96, 128, 160, 192, 200, 256, 320, 400, 512, 576, 640, 800, 912, 1024, 1280, 1600, 2000, 2560, 4000, 8000}; const auto Fn = [](const auto& other) { auto seq = make_random_sequence(2, 8000, 20); seq.insert(seq.end(), other.begin(), other.end()); return seq; }(F); for (const auto& n : Fn) { // known grids are expanded correctly (pattern matching doesn't work reliably here) Expected expected{"retrieve", {{"grid", {"F" + std::to_string(n)}}}}; if (std::find(F.begin(), F.end(), n) == F.end()) { expected.keyvalue.clear(); } expect_mars("ret, date=-1, grid=F" + std::to_string(n), expected); expect_mars("ret, date=-1, grid=f" + std::to_string(n), expected); expect_mars("ret, date=-1, grid=" + std::to_string(n), expected); } } // ----------------------------------------------------------------------------- CASE("grid: octahedral Gaussian grids") { for (const auto& n : make_random_sequence(2, 8000, 20 /*reduced number of tests*/)) { const Expected expected{"retrieve", {{"grid", {"O" + std::to_string(n)}}}}; expect_mars("ret, date=-1, grid=O" + std::to_string(n), expected); expect_mars("ret, date=-1, grid=o" + std::to_string(n), expected); } } // ----------------------------------------------------------------------------- CASE("grid: reduced classical Gaussian grids") { for (const auto& n : Sequence{32, 48, 64, 80, 96, 128, 160, 200, 256, 320, 400, 512, 640, 800, 1024, 1280, 8000}) { const Expected expected{"retrieve", {{"grid", {"N" + std::to_string(n)}}}}; expect_mars("ret, date=-1, grid=N" + std::to_string(n), expected); expect_mars("ret, date=-1, grid=n" + std::to_string(n), expected); } } // ----------------------------------------------------------------------------- CASE("grid: HEALPix grids") { for (const auto& n : make_power_sequence(1, 8192)) { const Expected expected{"retrieve", {{"grid", {"H" + std::to_string(n)}}}}; expect_mars("ret, date=-1, grid=H" + std::to_string(n), expected); expect_mars("ret, date=-1, grid=h" + std::to_string(n), expected); const Expected expected_ring{"retrieve", {{"grid", {"HR" + std::to_string(n)}}}}; expect_mars("ret, date=-1, grid=Hr" + std::to_string(n), expected_ring); expect_mars("ret, date=-1, grid=hR" + std::to_string(n), expected_ring); const Expected expected_nested{"retrieve", {{"grid", {"HN" + std::to_string(n)}}}}; expect_mars("ret, date=-1, grid=Hn" + std::to_string(n), expected_nested); expect_mars("ret, date=-1, grid=hN" + std::to_string(n), expected_nested); } } // ----------------------------------------------------------------------------- CASE("check language file") { EXPECT_THROWS(MarsLanguage::jsonFile("langauge.yaml")); eckit::Value file; EXPECT_NO_THROW(file = MarsLanguage::jsonFile("language.yaml")); EXPECT(file.contains("disseminate")); EXPECT(file.contains("archive")); EXPECT(file.contains("retrieve")); EXPECT(file.contains("read")); EXPECT(file.contains("get")); EXPECT(file.contains("list")); EXPECT(file.contains("compute")); EXPECT(file.contains("write")); EXPECT(file.contains("pointdb")); } CASE("check defaults and _clear_defaults") { { // check "retrieve" that _clear_defaults is not set auto request = MarsRequest::parse("retrieve,param=2t,step=10/to/12/by/1", true); EXPECT_EQUAL(request.values("class").size(), 1); EXPECT_EQUAL(request.values("class")[0], "od"); // check date is yesterday auto values = request.values("date"); EXPECT_EQUAL(values.size(), 1); std::ostringstream yesterday; yesterday << eckit::Date(-1).yyyymmdd(); EXPECT_EQUAL(values[0], yesterday.str()); } { // check "read" that _clear_defaults is set for class auto request = MarsRequest::parse("read,param=2t", true); EXPECT_EMPTY(request, "class"); EXPECT_EMPTY(request, "date"); EXPECT_EMPTY(request, "domain"); EXPECT_EMPTY(request, "expver"); EXPECT_EMPTY(request, "levelist"); EXPECT_EMPTY(request, "levtype"); EXPECT_EMPTY(request, "step"); EXPECT_EMPTY(request, "stream"); EXPECT_EMPTY(request, "time"); EXPECT_EMPTY(request, "type"); } { // check "list" that _clear_defaults is set for class auto request = MarsRequest::parse("list,param=2t", true); EXPECT_EQUAL(request.values("class").size(), 1); EXPECT_EQUAL(request.values("class")[0], "od"); EXPECT_EMPTY(request, "date"); EXPECT_EMPTY(request, "domain"); EXPECT_EMPTY(request, "expver"); EXPECT_EMPTY(request, "levelist"); EXPECT_EMPTY(request, "levtype"); EXPECT_EMPTY(request, "step"); EXPECT_EMPTY(request, "stream"); EXPECT_EMPTY(request, "time"); EXPECT_EMPTY(request, "type"); } { // check "pointdb" that _clear_defaults is set for class auto request = MarsRequest::parse("pointdb,param=2t", true); EXPECT_EMPTY(request, "class"); EXPECT_EMPTY(request, "date"); EXPECT_EMPTY(request, "domain"); EXPECT_EMPTY(request, "expver"); EXPECT_EMPTY(request, "levelist"); EXPECT_EMPTY(request, "levtype"); EXPECT_EMPTY(request, "step"); EXPECT_EMPTY(request, "stream"); EXPECT_EMPTY(request, "time"); EXPECT_EMPTY(request, "type"); } { // check "number" is not removed from the request for type=pf auto request = MarsRequest::parse("disseminate,class=od,stream=enfo,type=pf,number=2", true); EXPECT_EQUAL(request.values("number").size(), 1); EXPECT_EQUAL(request.values("number")[0], "2"); } { // check "number" is not removed from the request for type=wp auto request = MarsRequest::parse("disseminate,class=od,stream=enfo,type=wp,number=2", true); EXPECT_EQUAL(request.values("number").size(), 1); EXPECT_EQUAL(request.values("number")[0], "2"); } } CASE("check method: isData()") { EXPECT_EQUAL(MarsLanguage("retrieve").isData("class"), true); EXPECT_EQUAL(MarsLanguage("retrieve").isData("date"), true); EXPECT_EQUAL(MarsLanguage("retrieve").isData("time"), true); EXPECT_EQUAL(MarsLanguage("retrieve").isData("step"), true); EXPECT_EQUAL(MarsLanguage("retrieve").isData("number"), true); EXPECT_EQUAL(MarsLanguage("disseminate").isData("accuracy"), false); EXPECT_EQUAL(MarsLanguage("disseminate").isData("grid"), false); } CASE("check method: flatten()") { struct Output : public FlattenCallback { std::ostringstream oss; void operator()(const MarsRequest& request) override { oss << request << '\n'; } }; Output output; auto request = MarsRequest::parse( "retrieve,class=od,type=an,stream=oper,levtype=pl,time=1200,param=2t,step=10/to/14/by/2,levelist=300/400/" "500,date=20250717", true); MarsLanguage("retrieve").flatten(request, output); EXPECT_EQUAL(output.oss.str(), "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=10,levelist=300,param=" "167,expver=0001,domain=g\n" "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=10,levelist=400,param=" "167,expver=0001,domain=g\n" "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=10,levelist=500,param=" "167,expver=0001,domain=g\n" "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=12,levelist=300,param=" "167,expver=0001,domain=g\n" "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=12,levelist=400,param=" "167,expver=0001,domain=g\n" "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=12,levelist=500,param=" "167,expver=0001,domain=g\n" "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=14,levelist=300,param=" "167,expver=0001,domain=g\n" "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=14,levelist=400,param=" "167,expver=0001,domain=g\n" "retrieve,class=od,type=an,stream=oper,levtype=pl,date=20250717,time=1200,step=14,levelist=500,param=" "167,expver=0001,domain=g\n"); } CASE("check some types") { { EXPECT_THROWS(MarsLanguage("read").type("unknown")); EXPECT_THROWS(MarsLanguage("retrieve").type("unknown")); EXPECT_NO_THROW(MarsLanguage("retrieve").type("_hidden")); } { auto language = MarsLanguage("retrieve"); auto* type = language.type("class"); EXPECT(dynamic_cast(type) != nullptr); type = language.type("param"); EXPECT(dynamic_cast(type) != nullptr); type = language.type("expver"); EXPECT(dynamic_cast(type) != nullptr); type = language.type("domain"); EXPECT(dynamic_cast(type) != nullptr); type = language.type("date"); EXPECT(dynamic_cast(type) != nullptr); type = language.type("grid"); EXPECT(dynamic_cast(type) != nullptr); EXPECT_EQUAL(type->multiple(), true); type = language.type("area"); EXPECT(dynamic_cast(type) != nullptr); EXPECT_EQUAL(type->multiple(), true); type = language.type("accuracy"); EXPECT(dynamic_cast(type) != nullptr); type = language.type("resol"); EXPECT(dynamic_cast(type) != nullptr); } { auto language = MarsLanguage("archive"); auto* type = language.type("resol"); EXPECT(dynamic_cast(type) != nullptr); EXPECT_THROWS_AS(language.type("grid"), eckit::SeriousBug); EXPECT_THROWS_AS(language.type("area"), eckit::SeriousBug); } } // ----------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/mars2mars/0000775000175000017500000000000015232567717016355 5ustar alastairalastairmetkit-1.19.2/tests/mars2mars/mars2mars-test-02.cc0000664000175000017500000000675415232567717022003 0ustar alastairalastair/* * (C) Copyright 2026- 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. */ #include #include #include #include #include #include #include #include "eckit/testing/Test.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars2mars/api/Mars2Mars.h" #include "metkit/mars2mars/utils/dictionary_traits/dictaccess_mars_request.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #ifndef MARS2MARS_TEST_REQUEST_FILE #define MARS2MARS_TEST_REQUEST_FILE "mars2mars-test-requests.mars" #endif namespace { const std::vector& mars2marsSplitKeys() { static const std::vector keys = { "class", "stream", "type", "expver", "levtype", "param", "levelist", "step", "date", "time", "number", "chem", "wavelength", "timespan", "hdate", "htime", }; return keys; } std::vector readRequestsFromFile(const std::string& path) { std::ifstream in(path); if (!in) { throw std::runtime_error("Cannot open MARS request file: " + path); } return metkit::mars::MarsRequest::parse(in); } std::vector flattenRequest(const metkit::mars::MarsRequest& request) { return request.split(mars2marsSplitKeys()); } std::string jsonArrayFromRequests(const std::vector& requests) { using metkit::mars2mars::utils::dict_traits::dict_to_json; std::ostringstream out; out << "[\n"; for (std::size_t i = 0; i < requests.size(); ++i) { out << " " << dict_to_json(requests[i]); if (i + 1 != requests.size()) { out << ","; } out << "\n"; } out << "]"; return out.str(); } } // namespace CASE("mars2mars_flatten_convert_append_to_json") { // try { using metkit::mars2mars::Mars2Mars; const std::string reqFileName = MARS2MARS_TEST_REQUEST_FILE; const std::vector requests = readRequestsFromFile(reqFileName); EXPECT(!requests.empty()); metkit::mars::MarsExpansion expand(true, true); Mars2Mars mars2mars; std::vector convertedPoints; for (const auto& request : requests) { const metkit::mars::MarsRequest expanded = expand.expand(request); const std::vector points = flattenRequest(expanded); EXPECT(!points.empty()); for (const auto& point : points) { const auto [converted, misc] = mars2mars.convert(point); convertedPoints.push_back(converted); } } EXPECT(!convertedPoints.empty()); const std::string outputJson = jsonArrayFromRequests(convertedPoints); std::cout << outputJson << std::endl; EXPECT(!outputJson.empty()); EXPECT(outputJson.front() == '['); EXPECT(outputJson.back() == ']'); // } // catch (const eckit::Exception& e) { // std::cerr << "Test failed with exception: " << e.what() << std::endl; // } } int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); }metkit-1.19.2/tests/mars2mars/mars2mars-test-01.cc0000664000175000017500000000426515232567717021775 0ustar alastairalastair/* * (C) Copyright 2026- 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. */ #include #include #include #include "eckit/log/CodeLocation.h" #include "eckit/testing/Test.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars2mars/api/Mars2Mars.h" #include "metkit/mars2mars/utils/dictionary_traits/dictaccess_mars_request.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" namespace { metkit::mars::MarsRequest generateMarsRequest() { metkit::mars::MarsRequest mars; mars.verb("retrieve"); // String-only keys mars.setValue("class", "od"); mars.setValue("stream", "oper"); mars.setValue("type", "fc"); mars.setValue("expver", "1"); mars.setValue("date", "2026-02-09"); mars.setValue("time", "00:00:00"); mars.setValue("levtype", "sfc"); // Explicit scalar conversions supported by the MarsRequest trait mars.setValue("param", "261018"); mars.setValue("step", "0"); metkit::mars::MarsExpansion expand(true, true); mars = expand.expand(mars); return mars; } } // namespace CASE("mars2mars_marsrequest_convert_to_json") { using metkit::mars2mars::Mars2Mars; using metkit::mars2mars::utils::dict_traits::dict_to_json; try { metkit::mars::MarsRequest marsRequest = generateMarsRequest(); const auto [converted, misc] = Mars2Mars{}.convert(marsRequest); const std::string json = dict_to_json(converted); std::cout << json << std::endl; EXPECT(!json.empty()); } catch (const std::exception& e) { throw std::runtime_error(std::string("Test failed with exception: ") + e.what()); } } int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); }metkit-1.19.2/tests/mars2mars/mars2mars-test-requests.mars0000664000175000017500000000421115232567717023774 0ustar alastairalastairretrieve, class=od, date=2026-02-09, expver=1, levtype=sfc, param=3.228/7.228/8.128/8.228/9.128/9.228/10.228/11.228/12.228/13.228/14.228/20.3/20.128/20.228/21.228/22.228/23.228/24.228/26.128/26.228/27.128/27.228/28.128/28.228/29.128/29.228/30.128/31.128/32.128/33.128/34.128/35.128/36.128/37.128/38.128/39.128/40.128/41.128/42.128/43.128/44.128/44.228/45.128/46.228/47.128/47.228/48.228/49.128/50.128/57.128/58.128/66.128/67.128/74.128/78.128/79.128/80.228/81.228/82.228/83.228/84.228/85.228/88.228/89.228/90.228/96.174/98.174/121.128/122.128/123.128/129.128/129.228/130.151/130.228/131.151/131.228/132.151/132.228/134.128/136.128/137.128/139.128/141.128/142.128/143.128/144.128/145.128/145.151/146.128/147.128/148.128/148.151/151.128/159.128/160.128/161.128/162.128/163.128/163.151/164.128/164.151/165.128/166.128/167.128/168.128/169.128/170.128/172.128/173.128/174.128/175.128/175.151/176.128/177.128/178.128/179.128/180.128/181.128/182.128/183.128/186.128/186.210/187.128/187.210/188.128/188.210/189.128/189.210/190.210/191.210/195.128/196.128/197.128/198.128/201.128/202.128/205.128/206.128/207.128/208.128/209.128/210.128/211.128/212.128/213.128/216.228/217.228/218.228/219.228/220.228/221.228/222.228/223.228/224.228/225.228/226.228/227.228/228.128/229.128/230.128/231.128/232.128/234.128/235.128/236.128/238.128/239.228/240.228/241.228/243.128/244.128/245.128/246.228/247.228/249.228/251.228/162071/162072/228035/228036/228045/228050/228051/228057/228058/228231/228232/228233/228234/228235/228236/228237/260004/260005/260015/260048/260109/260121/260123/260242/260255/260318/260319/260320/260321/260338/260339/261001/261002/261014/261015/261016/261018, step=0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/47/48/49/50/51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/82/83/84/85/86/87/88/89/90/93/96/99/102/105/108/111/114/117/120/123/126/129/132/135/138/141/144/150/156/162/168/174/180/186/192/198/204/210/216/222/228/234/240/246/252/258/264/270/276/282/288/294/300/306/312/318/324/330/336/342/348/354/360, stream=oper, time=00:00:00/12:00:00, type=fcmetkit-1.19.2/tests/mars2mars/CMakeLists.txt0000664000175000017500000000076215232567717021122 0ustar alastairalastairif( HAVE_MARS2MARS ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/mars2mars-test-requests.mars ${CMAKE_CURRENT_BINARY_DIR}/mars2mars-test-requests.mars COPYONLY ) ecbuild_add_test( TARGET mars2mars-test-01 SOURCES mars2mars-test-01.cc NO_AS_NEEDED LIBS eckit metkit ) ecbuild_add_test( TARGET mars2mars-test-02 SOURCES mars2mars-test-02.cc NO_AS_NEEDED LIBS eckit metkit ) endif() metkit-1.19.2/tests/test_date.cc0000664000175000017500000000670515232567717016741 0ustar alastairalastair/* * (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. */ /// @file test_date.cc /// @author Emanuele Danovaro /// @date March 2025 #include #include #include "eckit/testing/Test.h" #include "eckit/types/Date.h" #include "eckit/value/Value.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/TypeDate.h" namespace metkit::mars::test { using ::eckit::BadValue; using ::eckit::Value; //----------------------------------------------------------------------------- void assertTypeExpansion(const std::string& name, std::vector values, const std::vector& expected) { static MarsLanguage language("retrieve"); language.type(name)->expand(values); EXPECT_EQUAL(expected, values); } std::string date(long d) { if (d <= 0) { eckit::Date day(d); d = day.yyyymmdd(); } return std::to_string(d); } CASE("Test TypeDate expansions") { TypeDate tdate("date", Value()); Type& td(tdate); assertTypeExpansion("date", {"20140506"}, {"20140506"}); assertTypeExpansion("date", {"2014-05-06"}, {"20140506"}); assertTypeExpansion("date", {"20140506", "20140507"}, {"20140506", "20140507"}); assertTypeExpansion("date", {"20140506", "to", "20140506"}, {"20140506"}); assertTypeExpansion("date", {"20140506", "to", "20140507"}, {"20140506", "20140507"}); assertTypeExpansion("date", {"20140506", "to", "20140508"}, {"20140506", "20140507", "20140508"}); assertTypeExpansion("date", {"20140504", "20140506", "to", "20140508"}, {"20140504", "20140506", "20140507", "20140508"}); assertTypeExpansion("date", {"-1", "0"}, {date(-1), date(0)}); assertTypeExpansion("date", {"-1", "to", "-3"}, {date(-1), date(-2), date(-3)}); assertTypeExpansion("date", {"-3", "to", "-1"}, {date(-3), date(-2), date(-1)}); assertTypeExpansion("date", {"-5", "to", "-1", "by", "2"}, {date(-5), date(-3), date(-1)}); assertTypeExpansion("date", {"2"}, {"feb"}); assertTypeExpansion("date", {"jan"}, {"jan"}); assertTypeExpansion("date", {"september"}, {"sep"}); assertTypeExpansion("date", {"9"}, {"sep"}); assertTypeExpansion("date", {"1-01"}, {"jan-1"}); assertTypeExpansion("date", {"jan-01"}, {"jan-1"}); assertTypeExpansion("date", {"january-01"}, {"jan-1"}); assertTypeExpansion("date", {"feb-23"}, {"feb-23"}); assertTypeExpansion("date", {"2018-23"}, {"20180123"}); assertTypeExpansion("date", {"2018-41"}, {"20180210"}); { EXPECT_THROWS(auto vv = td.tidy("20141506")); // throws BadDate that is not exported } { EXPECT_THROWS(auto vv = td.tidy("20180132")); // throws BadDate that is not exported } { EXPECT_THROWS(auto vv = td.tidy("202401366")); // throws BadDate that is not exported } { EXPECT_THROWS_AS(auto vv = td.tidy("abc"), BadValue); } { EXPECT_THROWS_AS(auto vv = td.tidy("abc-01"), BadValue); } } //----------------------------------------------------------------------------- } // namespace metkit::mars::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/tests/test_c_api.c0000664000175000017500000000132315232567717016723 0ustar alastairalastair/* * (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. */ /// @file test_c_api.c /// @date Dec 2024 /// @author Christopher Bradley #include #include "metkit/api/metkit_c.h" int main(int argc, char** argv) { const char* version = metkit_version(); metkit_error_t err = metkit_initialise(); fprintf(stdout, "MetKit version: %s\n", version); return 0; } metkit-1.19.2/tests/CMakeLists.txt0000664000175000017500000000757115232567717017220 0ustar alastairalastair# (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. list(APPEND metkit_env "METKIT_HOME=${CMAKE_BINARY_DIR}") if( DEFINED ECCODES_DEFINITION_PATH ) list(APPEND metkit_env "ECCODES_DEFINITION_PATH=${ECCODES_DEFINITION_PATH}") elseif( IS_DIRECTORY "${CMAKE_BINARY_DIR}/share/eccodes/definitions" ) list(APPEND metkit_env "ECCODES_DEFINITION_PATH=${CMAKE_BINARY_DIR}/share/eccodes/definitions") endif() ecbuild_get_test_multidata( TARGET grib_get_data DIRNAME grib-api/test-data/data NAMES latlon.grib synthetic_2msgs.grib ) ecbuild_get_test_multidata( TARGET metkit_get_odb_data NAMES multiodb.odb 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") ecbuild_add_test( TARGET metkit_test_multihandle CONDITION HAVE_GRIB INCLUDES "${ECCODES_INCLUDE_DIRS}" SOURCES test_multihandle.cc LIBS metkit NO_AS_NEEDED TEST_DEPENDS grib_get_data ENVIRONMENT "${metkit_env}" ) ecbuild_add_test( TARGET "metkit_test_codes_decoder" CONDITION HAVE_GRIB OR HAVE_BUFR SOURCES "test_codes_decoder.cc" INCLUDES "${ECKIT_INCLUDE_DIRS}" "${ECCODES_INCLUDE_DIR}" LIBS metkit NO_AS_NEEDED ENVIRONMENT "${metkit_env}" ) ecbuild_add_test( TARGET metkit_test_odbsplitter CONDITION HAVE_ODB SOURCES test_odbsplitter.cc INCLUDES "${ECKIT_INCLUDE_DIRS}" TEST_DEPENDS metkit_get_odb_data NO_AS_NEEDED LIBS metkit ) ecbuild_add_test( TARGET metkit_test_codes_api CONDITION HAVE_GRIB OR HAVE_BUFR SOURCES test_codes_api.cc INCLUDES "${ECKIT_INCLUDE_DIRS}" ENVIRONMENT "${metkit_env}" NO_AS_NEEDED LIBS metkit) foreach( test c_api context date expand filter hypercube integer_range matcher language mars_language mars_language_strict obstype param_axis request step steprange_axis time type_levelist typesfactory ) ecbuild_add_test( TARGET "metkit_test_${test}" SOURCES "test_${test}.cc" INCLUDES "${ECKIT_INCLUDE_DIRS}" ENVIRONMENT "${metkit_env}" NO_AS_NEEDED LIBS metkit) endforeach() # Compile C test ecbuild_add_test( TARGET metkit_test_c_compiled SOURCES test_c_api.c INCLUDES "${ECKIT_INCLUDE_DIRS}" LIBS metkit NO_AS_NEEDED ENVIRONMENT "${metkit_env}") # if ( HAVE_NETCDF ) # add_subdirectory(netcdf) # endif() add_subdirectory(regressions) add_subdirectory(marsgen) add_subdirectory(mars2grib) add_subdirectory(mars2mars) add_subdirectory(tools) metkit-1.19.2/tests/test_c_api.cc0000664000175000017500000002014615232567717017072 0ustar alastairalastair/* * (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. */ /// @file test_c_api.cc /// @date Dec 2024 /// @author Christopher Bradley #include #include #include #include #include "eckit/testing/Test.h" #include "eckit/types/Date.h" #include "metkit/api/metkit_c.h" #include "metkit/mars/MarsRequest.h" namespace metkit::test { using ::eckit::testing::TestException; // Wrapper around the C function calls that will throw an exception if the function fails // i.e. if the return value is not METKIT_SUCCESS void test_success(int e) { metkit_error_values_t err = static_cast(e); if (err != METKIT_SUCCESS) throw TestException("C-API error: " + std::string(metkit_get_error_string(err)), Here()); } void EXPECT_STR_EQUAL(const char* a, const char* b) { if (std::strcmp(a, b) != 0) { throw TestException("Expected: " + std::string(a) + " == " + std::string(b), Here()); } } // Fairly minimal test coverage CASE("metkit_marsrequest") { // ----------------------------------------------------------------- // Basics // ----------------------------------------------------------------- metkit_marsrequest_t* request{}; test_success(metkit_marsrequest_new(&request)); EXPECT(request); // set/get verb test_success(metkit_marsrequest_set_verb(request, "retrieve")); const char* verb{}; test_success(metkit_marsrequest_verb(request, &verb)); EXPECT_STR_EQUAL(verb, "retrieve"); // set array of values const char* dates[] = {"20200101", "20200102", "-1"}; test_success(metkit_marsrequest_set(request, "date", dates, 3)); // set single value const char* expver = "xxxx"; test_success(metkit_marsrequest_set_one(request, "expver", expver)); test_success(metkit_marsrequest_set_one(request, "param", "2t")); // check values bool has = false; test_success(metkit_marsrequest_has_param(request, "date", &has)); EXPECT(has); test_success(metkit_marsrequest_has_param(request, "random", &has)); EXPECT(!has); size_t count = 0; test_success(metkit_marsrequest_count_values(request, "date", &count)); EXPECT_EQUAL(count, 3); for (size_t i = 0; i < count; i++) { const char* value{}; test_success(metkit_marsrequest_value(request, "date", i, &value)); EXPECT_STR_EQUAL(value, dates[i]); } // ----------------------------------------------------------------- // Expand // ----------------------------------------------------------------- metkit_marsrequest_t* expandedRequest{}; test_success(metkit_marsrequest_new(&expandedRequest)); test_success(metkit_marsrequest_expand(request, false, true, expandedRequest)); // Check date expanded -1 -> yesterday test_success(metkit_marsrequest_count_values(expandedRequest, "date", &count)); EXPECT_EQUAL(count, 3); std::array dates_expanded; for (size_t i = 0; i < count; i++) { test_success(metkit_marsrequest_value(expandedRequest, "date", i, &dates_expanded[i])); } EXPECT_STR_EQUAL(dates_expanded[2], std::to_string(eckit::Date(-1).yyyymmdd()).c_str()); // check param expanded 2t -> 167 const char* param{}; test_success(metkit_marsrequest_value(expandedRequest, "param", 0, ¶m)); EXPECT_STR_EQUAL(param, "167"); // ----------------------------------------------------------------- // Merge // ----------------------------------------------------------------- metkit_marsrequest_t* req_manydates{}; test_success(metkit_marsrequest_new(&req_manydates)); const char* dates_many[] = {"19000101", "19000102", "19000103"}; test_success(metkit_marsrequest_set(req_manydates, "date", dates_many, 3)); test_success(metkit_marsrequest_merge(request, req_manydates)); test_success(metkit_marsrequest_count_values(request, "date", &count)); EXPECT_EQUAL(count, 6); // ----------------------------------------------------------------- // done metkit_marsrequest_delete(request); metkit_marsrequest_delete(expandedRequest); metkit_marsrequest_delete(req_manydates); } //----------------------------------------------------------------------------- CASE("metkit_requestiterator_t parsing") { metkit_requestiterator_t* it{}; test_success( metkit_parse_marsrequests("retrieve,date=-1,param=2t \n retrieve,date=20200102,param=2t,step=10/to/20/by/2", &it, true)); // two separate requests std::vector requests; metkit_iterator_status_t status; while ((status = metkit_requestiterator_next(it)) == METKIT_ITERATOR_SUCCESS) { metkit_marsrequest_t* req{}; test_success(metkit_marsrequest_new(&req)); EXPECT_EQUAL(metkit_requestiterator_current(it, req), METKIT_ITERATOR_SUCCESS); requests.push_back(req); } EXPECT_EQUAL(status, METKIT_ITERATOR_COMPLETE); EXPECT_EQUAL(requests.size(), 2); // check the date const char* date{}; test_success(metkit_marsrequest_value(requests[0], "date", 0, &date)); EXPECT_STR_EQUAL(date, std::to_string(eckit::Date(-1).yyyymmdd()).c_str()); // parser also calls expand test_success(metkit_marsrequest_value(requests[1], "date", 0, &date)); EXPECT_STR_EQUAL(date, "20200102"); // Check steps have been parsed size_t count = 0; test_success(metkit_marsrequest_count_values(requests[1], "step", &count)); EXPECT_EQUAL(count, 6); for (size_t i = 0; i < count; i++) { const char* step{}; test_success(metkit_marsrequest_value(requests[1], "step", i, &step)); EXPECT_STR_EQUAL(step, std::to_string(10 + i * 2).c_str()); } // cleanup metkit_requestiterator_delete(it); // NB: requests have been moved out of the iterator for (auto& req : requests) { metkit_marsrequest_delete(req); } } // Ensure that the param iterator works as expected CASE("metkit_paramiterator_t ") { std::string str = "retrieve,date=20200102,param=2t,step=10/to/20/by/2"; metkit_marsrequest_t* request{}; test_success(metkit_marsrequest_new(&request)); test_success(metkit_parse_marsrequest(str.c_str(), request, true)); metkit_paramiterator_t* it{}; test_success(metkit_marsrequest_params(request, &it)); metkit_iterator_status_t status; std::set keys; while ((status = metkit_paramiterator_next(it)) == METKIT_ITERATOR_SUCCESS) { const char* key{}; EXPECT_EQUAL(metkit_paramiterator_current(it, &key), METKIT_ITERATOR_SUCCESS); keys.insert(key); } EXPECT_EQUAL(status, METKIT_ITERATOR_COMPLETE); // Compare with C++ impl for consistency mars::MarsRequest req = mars::MarsRequest::parse(str, true); std::set keys_cpp; for (const auto& k : req.params()) { keys_cpp.insert(k); } EXPECT_EQUAL(keys, keys_cpp); } CASE("metkit_requestiterator_t 1 item") { // Edge case: verify iterator with one item works the same way. metkit_requestiterator_t* it{}; test_success(metkit_parse_marsrequests("retrieve,date=-1,param=2t", &it, true)); std::vector requests; metkit_iterator_status_t status; while ((status = metkit_requestiterator_next(it)) == METKIT_ITERATOR_SUCCESS) { metkit_marsrequest_t* req{}; test_success(metkit_marsrequest_new(&req)); EXPECT_EQUAL(metkit_requestiterator_current(it, req), METKIT_ITERATOR_SUCCESS); requests.push_back(req); } EXPECT_EQUAL(status, METKIT_ITERATOR_COMPLETE); EXPECT_EQUAL(requests.size(), 1); // cleanup metkit_requestiterator_delete(it); for (auto& req : requests) { metkit_marsrequest_delete(req); } } } // namespace metkit::test int main(int argc, char** argv) { return eckit::testing::run_tests(argc, argv); } metkit-1.19.2/rust/0000775000175000017500000000000015232567717014301 5ustar alastairalastairmetkit-1.19.2/rust/.cargo/0000775000175000017500000000000015232567717015452 5ustar alastairalastairmetkit-1.19.2/rust/.cargo/config.toml0000664000175000017500000000041415232567717017613 0ustar alastairalastair[build] jobs = -1 [target.'cfg(all())'] rustflags = [ "-Wclippy::all", "-Wclippy::pedantic", "-Wclippy::nursery", "-Wclippy::unwrap_used", "-Aclippy::module_name_repetitions", "-Aclippy::missing_errors_doc", ] [net] git-fetch-with-cli = true metkit-1.19.2/rust/crates/0000775000175000017500000000000015232567717015562 5ustar alastairalastairmetkit-1.19.2/rust/crates/metkit-sys/0000775000175000017500000000000015232567717017673 5ustar alastairalastairmetkit-1.19.2/rust/crates/metkit-sys/src/0000775000175000017500000000000015232567717020462 5ustar alastairalastairmetkit-1.19.2/rust/crates/metkit-sys/src/lib.rs0000664000175000017500000001743615232567717021611 0ustar alastairalastair//! FFI bindings to ECMWF metkit C++ library. //! //! Provides cxx bridge to: //! - `MarsRequest` — MARS request parsing, access, modification //! - `RequestEnvironment` — environment metadata //! - `MarsLanguage` — keyword categorization use bindman::track_cpp_api; // Auto-generated metkit Error enum + From impl include!(concat!(env!("OUT_DIR"), "/metkit_exceptions.rs")); #[track_cpp_api( ("metkit/mars/MarsRequest.h", class = "MarsRequest"), ("metkit/mars/MarsLanguage.h", class = "MarsLanguage"), )] #[cxx::bridge(namespace = "metkit_bridge")] pub mod ffi { unsafe extern "C++" { include!("MetkitBridge.h"); // ==================== MarsRequest ==================== type MarsRequestWrapper; fn verb(self: &MarsRequestWrapper) -> String; fn has(self: &MarsRequestWrapper, key: &str) -> bool; fn values(self: &MarsRequestWrapper, key: &str) -> Result>; fn get_first(self: &MarsRequestWrapper, key: &str) -> Result; fn empty(self: &MarsRequestWrapper) -> bool; fn count(self: &MarsRequestWrapper) -> usize; fn matches(self: &MarsRequestWrapper, filter: &MarsRequestWrapper) -> bool; fn params(self: &MarsRequestWrapper) -> Vec; // Mutation fn set_verb(self: Pin<&mut MarsRequestWrapper>, verb: &str); fn set_value_string(self: Pin<&mut MarsRequestWrapper>, key: &str, value: &str); fn set_values(self: Pin<&mut MarsRequestWrapper>, key: &str, values: Vec); fn set_value_long(self: Pin<&mut MarsRequestWrapper>, key: &str, value: i64); fn unset_values(self: Pin<&mut MarsRequestWrapper>, key: &str); // Extract parameters by category (e.g. "postproc") fn extract( self: &MarsRequestWrapper, category: &str, ) -> Result>; // Expansion fn expand( self: &MarsRequestWrapper, inherit: bool, strict: bool, ) -> Result>; // Cross-crate ExternTypes from eckit-sys #[namespace = "eckit_bridge"] type StreamWrapper = eckit_sys::StreamWrapper; #[namespace = "eckit_bridge"] type DataHandleWrapper = eckit_sys::DataHandleWrapper; #[namespace = "eckit_bridge"] type ConfigWrapper = eckit_sys::ConfigWrapper; #[namespace = "eckit_bridge"] type MessageWrapper = eckit_sys::MessageWrapper; fn encode(self: &MarsRequestWrapper, stream: Pin<&mut StreamWrapper>) -> Result<()>; /// Create a `MarsRequestHandle` — DataHandle for Hermes protocol. fn make_handle( self: &MarsRequestWrapper, config: &ConfigWrapper, ) -> Result>; // Output fn to_json(self: &MarsRequestWrapper) -> Result; fn dump(self: &MarsRequestWrapper) -> String; // Factories #[Self = "MarsRequestWrapper"] #[must_use] fn create(verb: &str) -> UniquePtr; /// Create a `MarsRequest` from a GRIB message (reads metadata keys). #[Self = "MarsRequestWrapper"] fn from_message(msg: &MessageWrapper) -> Result>; /// Decode a `MarsRequest` from a stream. #[Self = "MarsRequestWrapper"] fn decode(stream: Pin<&mut StreamWrapper>) -> Result>; // ==================== CodesHandle ==================== type CodesHandleWrapper; // Query fn is_defined(self: &CodesHandleWrapper, key: &str) -> Result; fn is_missing(self: &CodesHandleWrapper, key: &str) -> Result; fn has(self: &CodesHandleWrapper, key: &str) -> Result; // Scalar get fn get_string(self: &CodesHandleWrapper, key: &str) -> Result; fn get_long(self: &CodesHandleWrapper, key: &str) -> Result; fn get_double(self: &CodesHandleWrapper, key: &str) -> Result; // Array get fn get_double_array(self: &CodesHandleWrapper, key: &str) -> Result>; fn get_long_array(self: &CodesHandleWrapper, key: &str) -> Result>; // Scalar set fn set_string(self: Pin<&mut CodesHandleWrapper>, key: &str, value: &str) -> Result<()>; fn set_long(self: Pin<&mut CodesHandleWrapper>, key: &str, value: i64) -> Result<()>; fn set_double(self: Pin<&mut CodesHandleWrapper>, key: &str, value: f64) -> Result<()>; // Array set fn set_double_array( self: Pin<&mut CodesHandleWrapper>, key: &str, values: &[f64], ) -> Result<()>; // Missing fn set_missing(self: Pin<&mut CodesHandleWrapper>, key: &str) -> Result<()>; // Size and data fn value_count(self: &CodesHandleWrapper, key: &str) -> Result; fn message_size(self: &CodesHandleWrapper) -> Result; fn message_data(self: &CodesHandleWrapper) -> Result<&[u8]>; // Clone #[rust_name = "clone_handle"] fn clone(self: &CodesHandleWrapper) -> Result>; // Factories — static methods on CodesHandleWrapper, called from Rust as // `ffi::CodesHandleWrapper::from_message(...)` etc. #[Self = "CodesHandleWrapper"] fn from_message(data: &[u8]) -> Result>; #[Self = "CodesHandleWrapper"] fn from_file(path: &str) -> Result>; #[Self = "CodesHandleWrapper"] fn from_file_at_offset(path: &str, offset: i64) -> Result>; #[Self = "CodesHandleWrapper"] fn from_sample(sample: &str) -> Result>; // ==================== HyperCube ==================== type HyperCubeWrapper; fn size(self: &HyperCubeWrapper) -> usize; fn count(self: &HyperCubeWrapper) -> usize; fn count_vacant(self: &HyperCubeWrapper) -> usize; fn contains(self: &HyperCubeWrapper, request: &MarsRequestWrapper) -> Result; fn clear(self: Pin<&mut HyperCubeWrapper>, request: &MarsRequestWrapper) -> Result; fn field_ordinal(self: &HyperCubeWrapper, request: &MarsRequestWrapper) -> Result; #[Self = "HyperCubeWrapper"] fn create(request: &MarsRequestWrapper) -> Result>; // ==================== ParsedRequests ==================== type ParsedRequestsWrapper; fn count(self: &ParsedRequestsWrapper) -> usize; fn at(self: &ParsedRequestsWrapper, index: usize) -> Result>; #[Self = "ParsedRequestsWrapper"] fn parse(input: &str, strict: bool) -> Result>; /// Raw parse without verb validation — uses `MarsParser` directly. #[Self = "ParsedRequestsWrapper"] fn parse_raw(input: &str) -> Result>; // ==================== RequestEnvironment ==================== type RequestEnvironmentWrapper; #[Self = "RequestEnvironmentWrapper"] fn initialise(keys: Vec, values: Vec); #[Self = "RequestEnvironmentWrapper"] fn request() -> Result>; // ==================== MarsLanguage ==================== type MarsLanguageWrapper; fn sink_keywords(self: &MarsLanguageWrapper) -> Vec; fn is_data(self: &MarsLanguageWrapper, keyword: &str) -> bool; #[Self = "MarsLanguageWrapper"] fn create(verb: &str) -> Result>; } } // Public re-exports pub use cxx::{Exception, UniquePtr}; pub use ffi::*; metkit-1.19.2/rust/crates/metkit-sys/cpp/0000775000175000017500000000000015232567717020455 5ustar alastairalastairmetkit-1.19.2/rust/crates/metkit-sys/cpp/MetkitBridge.h0000664000175000017500000000062315232567717023201 0ustar alastairalastair// metkit C++ bridge for Rust FFI — umbrella header pulled in by the // cxx-generated bridge (`include!("MetkitBridge.h")` in lib.rs) and by // downstream `-sys` crates. Real declarations live in the per-topic headers // below. #pragma once #include "CodesHandle.h" #include "HyperCube.h" #include "MarsLanguage.h" #include "MarsRequest.h" #include "ParsedRequests.h" #include "RequestEnvironment.h" metkit-1.19.2/rust/crates/metkit-sys/cpp/CodesHandle.cc0000664000175000017500000000753715232567717023151 0ustar alastairalastair// metkit CodesHandle bridge — implementation. #include "metkit_exceptions.h" #include "CodesHandle.h" namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- bool CodesHandleWrapper::is_defined(rust::Str key) const { return handle_->isDefined(std::string(key)); } bool CodesHandleWrapper::is_missing(rust::Str key) const { return handle_->isMissing(std::string(key)); } bool CodesHandleWrapper::has(rust::Str key) const { return handle_->has(std::string(key)); } rust::String CodesHandleWrapper::get_string(rust::Str key) const { return rust::String(handle_->getString(std::string(key))); } int64_t CodesHandleWrapper::get_long(rust::Str key) const { return static_cast(handle_->getLong(std::string(key))); } double CodesHandleWrapper::get_double(rust::Str key) const { return handle_->getDouble(std::string(key)); } rust::Vec CodesHandleWrapper::get_double_array(rust::Str key) const { auto vec = handle_->getDoubleArray(std::string(key)); rust::Vec result; result.reserve(vec.size()); for (double v : vec) { result.push_back(v); } return result; } rust::Vec CodesHandleWrapper::get_long_array(rust::Str key) const { auto vec = handle_->getLongArray(std::string(key)); rust::Vec result; result.reserve(vec.size()); for (long v : vec) { result.push_back(static_cast(v)); } return result; } void CodesHandleWrapper::set_string(rust::Str key, rust::Str value) { handle_->set(std::string(key), std::string(value)); } void CodesHandleWrapper::set_long(rust::Str key, int64_t value) { handle_->set(std::string(key), static_cast(value)); } void CodesHandleWrapper::set_double(rust::Str key, double value) { handle_->set(std::string(key), value); } void CodesHandleWrapper::set_double_array(rust::Str key, rust::Slice values) { handle_->set(std::string(key), metkit::codes::Span(values.data(), values.size())); } void CodesHandleWrapper::set_missing(rust::Str key) { handle_->setMissing(std::string(key)); } size_t CodesHandleWrapper::value_count(rust::Str key) const { return handle_->size(std::string(key)); } size_t CodesHandleWrapper::message_size() const { return handle_->messageSize(); } rust::Slice CodesHandleWrapper::message_data() const { auto span = handle_->messageData(); return {span.data(), span.size()}; } std::unique_ptr CodesHandleWrapper::clone() const { return std::make_unique(handle_->clone()); } //---------------------------------------------------------------------------------------------------------------------- std::unique_ptr CodesHandleWrapper::from_message(rust::Slice data) { return std::make_unique( metkit::codes::codesHandleFromMessageCopy(metkit::codes::Span(data.data(), data.size()))); } std::unique_ptr CodesHandleWrapper::from_file(rust::Str path) { return std::make_unique( metkit::codes::codesHandleFromFile(std::string(path), metkit::codes::Product::GRIB)); } std::unique_ptr CodesHandleWrapper::from_file_at_offset(rust::Str path, int64_t offset) { return std::make_unique( metkit::codes::codesHandleFromFile(std::string(path), metkit::codes::Product::GRIB, offset)); } std::unique_ptr CodesHandleWrapper::from_sample(rust::Str sample) { return std::make_unique(metkit::codes::codesHandleFromSample(std::string(sample))); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/HyperCube.cc0000664000175000017500000000262115232567717022653 0ustar alastairalastair// metkit HyperCube bridge — implementation. #include "metkit_exceptions.h" #include "HyperCube.h" namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- HyperCubeWrapper::HyperCubeWrapper(const MarsRequestWrapper& request) : cube_(std::make_unique(request.inner())) {} size_t HyperCubeWrapper::size() const { return cube_->size(); } size_t HyperCubeWrapper::count() const { return cube_->count(); } size_t HyperCubeWrapper::count_vacant() const { return cube_->countVacant(); } bool HyperCubeWrapper::contains(const MarsRequestWrapper& request) const { return cube_->contains(request.inner()); } bool HyperCubeWrapper::clear(const MarsRequestWrapper& request) { return cube_->clear(request.inner()); } size_t HyperCubeWrapper::field_ordinal(const MarsRequestWrapper& request) const { return cube_->fieldOrdinal(request.inner()); } //---------------------------------------------------------------------------------------------------------------------- std::unique_ptr HyperCubeWrapper::create(const MarsRequestWrapper& request) { return std::make_unique(request); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/CodesHandle.h0000664000175000017500000000445415232567717023006 0ustar alastairalastair// metkit CodesHandle bridge — wraps `metkit::codes::CodesHandle`. #pragma once #include "metkit/codes/api/CodesAPI.h" #include "rust/cxx.h" #include #include #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- /// Wraps `metkit::codes::CodesHandle` for Rust FFI. class CodesHandleWrapper { std::unique_ptr handle_; public: explicit CodesHandleWrapper(std::unique_ptr h) : handle_(std::move(h)) {} // Query bool is_defined(rust::Str key) const; bool is_missing(rust::Str key) const; bool has(rust::Str key) const; // Scalar get rust::String get_string(rust::Str key) const; int64_t get_long(rust::Str key) const; double get_double(rust::Str key) const; // Array get rust::Vec get_double_array(rust::Str key) const; rust::Vec get_long_array(rust::Str key) const; // Scalar set void set_string(rust::Str key, rust::Str value); void set_long(rust::Str key, int64_t value); void set_double(rust::Str key, double value); // Array set void set_double_array(rust::Str key, rust::Slice values); // Missing void set_missing(rust::Str key); // Size and data size_t value_count(rust::Str key) const; size_t message_size() const; rust::Slice message_data() const; /// Deep-copy this handle (wraps `metkit::codes::CodesHandle::clone()`). std::unique_ptr clone() const; // Access underlying const metkit::codes::CodesHandle& inner() const { return *handle_; } metkit::codes::CodesHandle& inner() { return *handle_; } // ============== Factories ============== static std::unique_ptr from_message(rust::Slice data); static std::unique_ptr from_file(rust::Str path); static std::unique_ptr from_file_at_offset(rust::Str path, int64_t offset); static std::unique_ptr from_sample(rust::Str sample); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/ParsedRequests.cc0000664000175000017500000000304515232567717023740 0ustar alastairalastair// metkit ParsedRequests bridge — implementation. #include "metkit_exceptions.h" #include "ParsedRequests.h" #include "metkit/mars/MarsParser.h" #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- ParsedRequestsWrapper::ParsedRequestsWrapper(rust::Str input, bool strict) { auto str = std::string(input); std::istringstream iss(str); requests_ = metkit::mars::MarsRequest::parse(iss, strict); } size_t ParsedRequestsWrapper::count() const { return requests_.size(); } std::unique_ptr ParsedRequestsWrapper::at(size_t index) const { return std::make_unique(requests_.at(index)); } //---------------------------------------------------------------------------------------------------------------------- std::unique_ptr ParsedRequestsWrapper::parse(rust::Str input, bool strict) { return std::make_unique(input, strict); } std::unique_ptr ParsedRequestsWrapper::parse_raw(rust::Str input) { auto str = std::string(input); std::istringstream iss(str); auto parsed = metkit::mars::MarsParser(iss).parse(); auto wrapper = std::make_unique(); for (auto& r : parsed) { wrapper->push(r); } return wrapper; } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/RequestEnvironment.cc0000664000175000017500000000203715232567717024643 0ustar alastairalastair// metkit RequestEnvironment bridge — implementation. #include "metkit_exceptions.h" #include "RequestEnvironment.h" #include "metkit/mars/RequestEnvironment.h" #include #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- void RequestEnvironmentWrapper::initialise(rust::Vec keys, rust::Vec values) { std::map env; for (size_t i = 0; i < keys.size() && i < values.size(); ++i) { env[std::string(keys[i])] = std::string(values[i]); } metkit::mars::RequestEnvironment::initialize(env); } std::unique_ptr RequestEnvironmentWrapper::request() { const auto& env = metkit::mars::RequestEnvironment::instance(); return std::make_unique(env.request()); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/MarsRequest.cc0000664000175000017500000001033515232567717023241 0ustar alastairalastair// metkit MarsRequest bridge — implementation. #include "metkit_exceptions.h" #include "MarsRequest.h" #include "eckit/log/JSON.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/MarsRequestHandle.h" #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- rust::String MarsRequestWrapper::verb() const { return rust::String(request_.verb()); } bool MarsRequestWrapper::has(rust::Str key) const { return request_.has(std::string(key)); } rust::Vec MarsRequestWrapper::values(rust::Str key) const { const auto& vals = request_.values(std::string(key)); rust::Vec result; result.reserve(vals.size()); for (const auto& v : vals) { result.push_back(rust::String(v)); } return result; } rust::String MarsRequestWrapper::get_first(rust::Str key) const { return rust::String(request_[std::string(key)]); } bool MarsRequestWrapper::empty() const { return request_.empty(); } size_t MarsRequestWrapper::count() const { return request_.count(); } bool MarsRequestWrapper::matches(const MarsRequestWrapper& filter) const { return request_.matches(filter.request_); } rust::Vec MarsRequestWrapper::params() const { auto p = request_.params(); rust::Vec result; result.reserve(p.size()); for (const auto& name : p) { result.push_back(rust::String(name)); } return result; } void MarsRequestWrapper::set_verb(rust::Str verb) { request_.verb(std::string(verb)); } void MarsRequestWrapper::set_value_string(rust::Str key, rust::Str value) { request_.setValue(std::string(key), std::string(value)); } void MarsRequestWrapper::set_values(rust::Str key, rust::Vec values) { std::vector vec; vec.reserve(values.size()); for (const auto& v : values) { vec.emplace_back(std::string(v)); } request_.values(std::string(key), vec); } void MarsRequestWrapper::set_value_long(rust::Str key, int64_t value) { request_.setValue(std::string(key), static_cast(value)); } void MarsRequestWrapper::unset_values(rust::Str key) { request_.unsetValues(std::string(key)); } std::unique_ptr MarsRequestWrapper::extract(rust::Str category) const { return std::make_unique(request_.extract(std::string(category))); } std::unique_ptr MarsRequestWrapper::expand(bool inherit, bool strict) const { metkit::mars::MarsLanguage lang(request_.verb()); auto expanded = lang.expand(request_, inherit, strict); return std::make_unique(std::move(expanded)); } rust::String MarsRequestWrapper::to_json() const { std::ostringstream oss; eckit::JSON json(oss); json << request_; return rust::String(oss.str()); } rust::String MarsRequestWrapper::dump() const { std::ostringstream oss; request_.dump(oss); return rust::String(oss.str()); } void MarsRequestWrapper::encode(eckit_bridge::StreamWrapper& stream) const { stream.inner() << request_; } std::unique_ptr MarsRequestWrapper::make_handle( const eckit_bridge::ConfigWrapper& config) const { return std::make_unique( new metkit::mars::MarsRequestHandle(request_, config.inner())); } //---------------------------------------------------------------------------------------------------------------------- std::unique_ptr MarsRequestWrapper::create(rust::Str verb) { return std::make_unique(metkit::mars::MarsRequest(std::string(verb))); } std::unique_ptr MarsRequestWrapper::from_message(const eckit_bridge::MessageWrapper& msg) { return std::make_unique(metkit::mars::MarsRequest(msg.inner())); } std::unique_ptr MarsRequestWrapper::decode(eckit_bridge::StreamWrapper& stream) { return std::make_unique(metkit::mars::MarsRequest(stream.inner())); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/MarsLanguage.h0000664000175000017500000000164715232567717023204 0ustar alastairalastair// metkit MarsLanguage bridge — wraps `metkit::mars::MarsLanguage`. #pragma once #include "metkit/mars/MarsLanguage.h" #include "rust/cxx.h" #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- /// Wraps `metkit::mars::MarsLanguage` for Rust FFI. class MarsLanguageWrapper { std::unique_ptr lang_; public: explicit MarsLanguageWrapper(rust::Str verb); rust::Vec sink_keywords() const; bool is_data(rust::Str keyword) const; // ============== Factories ============== /// Construct a MarsLanguage for the given verb. static std::unique_ptr create(rust::Str verb); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/ParsedRequests.h0000664000175000017500000000240715232567717023603 0ustar alastairalastair// metkit MarsRequest batch-parse bridge — holds parsed requests. #pragma once #include "MarsRequest.h" #include "metkit/mars/MarsRequest.h" #include "rust/cxx.h" #include #include #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- /// Holds parsed requests — parse once, iterate by index. class ParsedRequestsWrapper { std::vector requests_; public: ParsedRequestsWrapper() = default; ParsedRequestsWrapper(rust::Str input, bool strict); void push(const metkit::mars::MarsRequest& r) { requests_.push_back(r); } size_t count() const; std::unique_ptr at(size_t index) const; // ============== Factories ============== /// Parse MARS requests with verb validation. static std::unique_ptr parse(rust::Str input, bool strict); /// Raw parse without verb validation — uses `MarsParser` directly. static std::unique_ptr parse_raw(rust::Str input); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/RequestEnvironment.h0000664000175000017500000000170615232567717024507 0ustar alastairalastair// metkit RequestEnvironment bridge — wraps the singleton // `metkit::mars::RequestEnvironment`. #pragma once #include "MarsRequest.h" #include "rust/cxx.h" #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- /// Wraps the singleton `metkit::mars::RequestEnvironment`. All members are /// static — the underlying C++ type is itself a process-wide singleton. class RequestEnvironmentWrapper { public: /// Initialise the global request environment from a list of key/value pairs. static void initialise(rust::Vec keys, rust::Vec values); /// Return the current environment's MARS request. static std::unique_ptr request(); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/HyperCube.h0000664000175000017500000000237115232567717022517 0ustar alastairalastair// metkit HyperCube bridge — wraps `metkit::hypercube::HyperCube`. #pragma once #include "MarsRequest.h" #include "metkit/hypercube/HyperCube.h" #include "rust/cxx.h" #include #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- /// Wraps `metkit::hypercube::HyperCube` for Rust FFI. class HyperCubeWrapper { std::unique_ptr cube_; public: explicit HyperCubeWrapper(const MarsRequestWrapper& request); size_t size() const; size_t count() const; size_t count_vacant() const; bool contains(const MarsRequestWrapper& request) const; bool clear(const MarsRequestWrapper& request); size_t field_ordinal(const MarsRequestWrapper& request) const; // Access underlying const metkit::hypercube::HyperCube& inner() const { return *cube_; } // ============== Factories ============== /// Build a HyperCube from a MarsRequest. static std::unique_ptr create(const MarsRequestWrapper& request); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/MarsRequest.h0000664000175000017500000000501615232567717023103 0ustar alastairalastair// metkit MarsRequest bridge — wraps `metkit::mars::MarsRequest`. #pragma once #include "EckitBridge.h" #include "metkit/mars/MarsRequest.h" #include "rust/cxx.h" #include #include namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- /// Wraps `metkit::mars::MarsRequest` for Rust FFI. class MarsRequestWrapper { metkit::mars::MarsRequest request_; public: MarsRequestWrapper() = default; explicit MarsRequestWrapper(metkit::mars::MarsRequest r) : request_(std::move(r)) {} // Query rust::String verb() const; bool has(rust::Str key) const; rust::Vec values(rust::Str key) const; rust::String get_first(rust::Str key) const; bool empty() const; size_t count() const; bool matches(const MarsRequestWrapper& filter) const; rust::Vec params() const; // Mutation void set_verb(rust::Str verb); void set_value_string(rust::Str key, rust::Str value); void set_values(rust::Str key, rust::Vec values); void set_value_long(rust::Str key, int64_t value); void unset_values(rust::Str key); // Extract parameters by category std::unique_ptr extract(rust::Str category) const; // Expansion std::unique_ptr expand(bool inherit, bool strict) const; // Stream serialization void encode(eckit_bridge::StreamWrapper& stream) const; // Output rust::String to_json() const; rust::String dump() const; /// Create a `MarsRequestHandle` — DataHandle for Hermes retrieve/list/get. std::unique_ptr make_handle(const eckit_bridge::ConfigWrapper& config) const; // Access underlying const metkit::mars::MarsRequest& inner() const { return request_; } metkit::mars::MarsRequest& inner() { return request_; } // ============== Factories ============== /// Create a MarsRequest with the given verb. static std::unique_ptr create(rust::Str verb); /// Build a MarsRequest from an eckit message. static std::unique_ptr from_message(const eckit_bridge::MessageWrapper& msg); /// Decode a MarsRequest from a stream. static std::unique_ptr decode(eckit_bridge::StreamWrapper& stream); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/cpp/MarsLanguage.cc0000664000175000017500000000226715232567717023341 0ustar alastairalastair// metkit MarsLanguage bridge — implementation. #include "metkit_exceptions.h" #include "MarsLanguage.h" namespace metkit_bridge { //---------------------------------------------------------------------------------------------------------------------- MarsLanguageWrapper::MarsLanguageWrapper(rust::Str verb) : lang_(std::make_unique(std::string(verb))) {} rust::Vec MarsLanguageWrapper::sink_keywords() const { const auto& kw = lang_->sinkKeywords(); rust::Vec result; result.reserve(kw.size()); for (const auto& k : kw) { result.push_back(rust::String(k)); } return result; } bool MarsLanguageWrapper::is_data(rust::Str keyword) const { return lang_->isData(std::string(keyword)); } //---------------------------------------------------------------------------------------------------------------------- std::unique_ptr MarsLanguageWrapper::create(rust::Str verb) { return std::make_unique(verb); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit_bridge metkit-1.19.2/rust/crates/metkit-sys/build.rs0000664000175000017500000002271015232567717021342 0ustar alastairalastair//! Build script for metkit-sys //! //! Supports two build modes: //! - `vendored` (default): Clone and build metkit from source using ecbuild //! - `system`: Use `CMake` `find_package` to find system-installed metkit const METKIT_VERSION: &str = "1.18.1"; fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-env-changed=METKIT_DIR"); println!("cargo:rerun-if-env-changed=CMAKE_PREFIX_PATH"); println!("cargo:rerun-if-env-changed=DOCS_RS"); if bindman_utils::is_docs_rs() { return; } bindman_utils::validate_build_mode(cfg!(feature = "system"), cfg!(feature = "vendored")); let include = if cfg!(feature = "system") { build_system() } else { build_vendored() }; generate_exceptions(&include); build_cxx_bridge(&include); let crate_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR")); bindman_build::check_cpp_api(&include, &crate_dir.join("src/lib.rs")); // Export cpp directory for downstream crates (MetkitBridge.h) println!("cargo:cpp_dir={}", crate_dir.join("cpp").display()); } /// Generate `metkit_exceptions.{h,rs}`. The bridge exposes `CodesHandleWrapper` /// methods that can throw `metkit::codes::CodesException` / `CodesWrongLength`; /// nothing in the bridge currently calls `mars2grib`, so its exception headers /// aren't a source here. eckit's exceptions are inherited from eckit-sys. fn generate_exceptions(include: &std::path::Path) { let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR not set")); let own = vec![bindman_build::ExceptionSource { header: include.join("metkit/codes/api/CodesTypes.h"), include_path: "metkit/codes/api/CodesTypes.h".to_string(), cpp_namespace: "metkit::codes".to_string(), message_prefix: "metkit".to_string(), base_class: "eckit::Exception".to_string(), recursive: true, }]; let inherited = bindman_build::collect_dep_exception_sources(); bindman_build::generate_exception_bridge(&bindman_build::ExceptionBridgeConfig { primary_namespace: "metkit", out_dir: &out_dir, own: &own, inherited: &inherited, }); bindman_build::publish_exception_sources(&own, &out_dir); } /// Compile the CXX bridge. fn build_cxx_bridge(include: &std::path::Path) { let crate_dir = std::path::PathBuf::from( std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"), ); let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR not set")); let eckit_include = std::env::var("DEP_ECKIT_SYS_INCLUDE") .expect("DEP_ECKIT_SYS_INCLUDE not set — eckit-sys must be a dependency"); println!("cargo:rerun-if-changed=cpp/MetkitBridge.h"); println!("cargo:rerun-if-changed=cpp/MarsRequest.h"); println!("cargo:rerun-if-changed=cpp/MarsRequest.cc"); println!("cargo:rerun-if-changed=cpp/CodesHandle.h"); println!("cargo:rerun-if-changed=cpp/CodesHandle.cc"); println!("cargo:rerun-if-changed=cpp/HyperCube.h"); println!("cargo:rerun-if-changed=cpp/HyperCube.cc"); println!("cargo:rerun-if-changed=cpp/ParsedRequests.h"); println!("cargo:rerun-if-changed=cpp/ParsedRequests.cc"); println!("cargo:rerun-if-changed=cpp/MarsLanguage.h"); println!("cargo:rerun-if-changed=cpp/MarsLanguage.cc"); println!("cargo:rerun-if-changed=cpp/RequestEnvironment.h"); println!("cargo:rerun-if-changed=cpp/RequestEnvironment.cc"); let mut build = cxx_build::bridge("src/lib.rs"); build .file(crate_dir.join("cpp/MarsRequest.cc")) .file(crate_dir.join("cpp/CodesHandle.cc")) .file(crate_dir.join("cpp/HyperCube.cc")) .file(crate_dir.join("cpp/ParsedRequests.cc")) .file(crate_dir.join("cpp/MarsLanguage.cc")) .file(crate_dir.join("cpp/RequestEnvironment.cc")) .include(include) .include(crate_dir.join("cpp")) .include(&eckit_include) .include(&out_dir); // for metkit_exceptions.h (generated) // Include eckit's cpp dir for EckitBridge.h (needed for StreamWrapper) if let Ok(eckit_cpp_dir) = std::env::var("DEP_ECKIT_SYS_CPP_DIR") { build.include(&eckit_cpp_dir); } build .flag_if_supported("-std=c++17") .compile("metkit_sys_bridge"); bindman_utils::link_cpp_stdlib(); } #[cfg(feature = "system")] fn build_system() -> std::path::PathBuf { let (root, include, lib_dir) = bindman_utils::cmake_find_package("metkit", METKIT_VERSION); println!("cargo:rustc-link-search=native={}", lib_dir.display()); println!("cargo:rustc-link-lib=dylib=metkit"); bindman_utils::link_cpp_stdlib(); // Export for downstream crates println!("cargo:root={}", root.display()); println!("cargo:include={}", include.display()); include } #[cfg(not(feature = "system"))] fn build_system() -> std::path::PathBuf { unreachable!("build_system called without system feature"); } /// Build metkit from source using ecbuild #[cfg(feature = "vendored")] #[allow(clippy::too_many_lines)] fn build_vendored() -> std::path::PathBuf { use std::env; use std::fs; use std::path::PathBuf; use std::process::Command; const ECBUILD_REPO: &str = "https://github.com/ecmwf/ecbuild.git"; const ECBUILD_TAG: &str = "3.13.1"; const METKIT_REPO: &str = "https://github.com/ecmwf/metkit.git"; let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); let src_dir = out_dir.join("src"); let build_dir = out_dir.join("build"); let install_dir = out_dir.join("install"); fs::create_dir_all(&src_dir).expect("Failed to create src directory"); fs::create_dir_all(&build_dir).expect("Failed to create build directory"); // Get dependency paths let eckit_root = env::var("DEP_ECKIT_SYS_ROOT") .expect("DEP_ECKIT_SYS_ROOT not set - eckit-sys must be a dependency"); let eccodes_root = env::var("DEP_ECCODES_SYS_ROOT") .expect("DEP_ECCODES_SYS_ROOT not set - eccodes-sys must be a dependency"); // Clone sources let ecbuild_src = bindman_utils::git_clone(ECBUILD_REPO, ECBUILD_TAG, &src_dir.join("ecbuild")); let metkit_src = bindman_utils::git_clone(METKIT_REPO, METKIT_VERSION, &src_dir.join("metkit")); let ecbuild_bin = ecbuild_src.join("bin/ecbuild"); let num_jobs = bindman_utils::build_parallelism(); let cmake_prefix_path = format!("{eckit_root};{eccodes_root}"); // Build metkit let mut cmd = Command::new(&ecbuild_bin); cmd.current_dir(&build_dir) .arg(format!("--prefix={}", install_dir.display())) .arg("--") .arg(&metkit_src) .arg(format!("-DCMAKE_PREFIX_PATH={cmake_prefix_path}")) .arg(format!( "-DCMAKE_BUILD_TYPE={}", bindman_utils::cmake_build_type() )) // Always disabled (no features) .arg("-DENABLE_TESTS=OFF") .arg("-DENABLE_DOCS=OFF") .arg("-DENABLE_BUILD_TOOLS=OFF") .arg("-DENABLE_MARS2GRIB_PYTHON=OFF"); // Feature-gated options cmd.arg(format!( "-DENABLE_GRIB={}", bindman_utils::on_off(cfg!(feature = "grib")) )); cmd.arg(format!( "-DENABLE_BUFR={}", bindman_utils::on_off(cfg!(feature = "bufr")) )); cmd.arg(format!( "-DENABLE_NETCDF={}", bindman_utils::on_off(cfg!(feature = "netcdf")) )); cmd.arg(format!( "-DENABLE_ODB={}", bindman_utils::on_off(cfg!(feature = "odb")) )); cmd.arg(format!( "-DENABLE_MARS2GRIB={}", bindman_utils::on_off(cfg!(feature = "mars2grib")) )); cmd.arg(format!( "-DENABLE_METKIT_CONFIG={}", bindman_utils::on_off(cfg!(feature = "metkit-config")) )); cmd.arg(format!( "-DENABLE_EXPERIMENTAL={}", bindman_utils::on_off(cfg!(feature = "experimental")) )); cmd.arg(format!( "-DENABLE_FAIL_ON_CCSDS={}", bindman_utils::on_off(cfg!(feature = "fail-on-ccsds")) )); // Use @rpath install names — the leaf binary sets rpaths via bindman_utils::emit_rpaths() #[cfg(target_os = "macos")] cmd.arg("-DCMAKE_INSTALL_NAME_DIR=@rpath"); bindman_utils::run_command(&mut cmd, "ecbuild configure metkit"); bindman_utils::run_command( Command::new("cmake") .args(["--build", ".", "--parallel", &num_jobs]) .current_dir(&build_dir), "cmake build metkit", ); bindman_utils::run_command( Command::new("cmake") .args(["--install", "."]) .current_dir(&build_dir), "cmake install metkit", ); // Copy share directory (contains language.yaml needed at runtime) let share_src = build_dir.join("share"); let share_dst = install_dir.join("share"); if share_src.exists() { bindman_utils::copy_dir_all(&share_src, &share_dst) .expect("Failed to copy share directory"); } // Link directives let lib_dir = bindman_utils::resolve_lib_dir(&install_dir); println!("cargo:rustc-link-search=native={}", lib_dir.display()); println!("cargo:rustc-link-lib=dylib=metkit"); bindman_utils::link_cpp_stdlib(); // Export for downstream crates let include = install_dir.join("include"); println!("cargo:root={}", install_dir.display()); println!("cargo:include={}", include.display()); include } #[cfg(not(feature = "vendored"))] fn build_vendored() -> std::path::PathBuf { unreachable!("build_vendored called without vendored feature"); } metkit-1.19.2/rust/crates/metkit-sys/README.md0000664000175000017500000000336315232567717021157 0ustar alastairalastair# metkit-sys Low-level Rust bindings to ECMWF's [metkit](https://github.com/ecmwf/metkit) C++ library. This crate provides raw FFI bindings using [cxx](https://cxx.rs/). For a safe, ergonomic API, use the higher-level `metkit` crate (forthcoming). ## Cargo build features These flags control what the underlying C++ metkit library is compiled with. Each maps to a `-DENABLE_=ON/OFF` flag in upstream [`metkit/CMakeLists.txt`](https://github.com/ecmwf/metkit/blob/develop/CMakeLists.txt). ### Build strategy (mutually exclusive) - `vendored` (default) - Clone and build metkit (and its eckit and ecCodes dependencies) from source. - `system` - Link against a system-installed metkit, located via CMake `find_package(metkit)`. Honours `METKIT_DIR` and `CMAKE_PREFIX_PATH`. ### Format support (enabled by default) - `grib` - GRIB format support. Pulls in `eccodes-sys/product-grib`. - `bufr` - BUFR format support. Pulls in `eccodes-sys/product-bufr`. ### Format support (off by default; require external libraries) - `netcdf` - NetCDF data support. - `odb` - ODB data support (requires `odc`). ### Encoding (enabled by default) - `mars2grib` - MARS2GRIB encoder. Pulls in `eckit-sys/geo-codec-grids` for ORCA / FESOM / ICON grid support. ### Configuration (enabled by default) - `metkit-config` - Install metkit configuration files (e.g. `language.yaml`). ### Other (off by default) - `experimental` - Experimental upstream features. - `fail-on-ccsds` - Fail when encountering CCSDS-encoded messages. ## Environment variables - `METKIT_DIR` - Install prefix of a metkit build, used by `system` mode. - `CMAKE_PREFIX_PATH` - Additional CMake search paths. - `DOCS_RS` - When set, the build script becomes a no-op (for docs.rs). ## License Apache-2.0 metkit-1.19.2/rust/crates/metkit-sys/Cargo.toml0000664000175000017500000000310015232567717021615 0ustar alastairalastair[package] name = "metkit-sys" version = "1.18.1" edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true readme.workspace = true keywords.workspace = true categories.workspace = true description = "C++ bindings to ECMWF metkit library using cxx" links = "metkit_sys" build = "build.rs" [features] # Defaults: core features default = ["vendored", "grib", "bufr", "mars2grib", "metkit-config"] # Build strategy (mutually exclusive) vendored = ["eckit-sys/vendored", "eccodes-sys/vendored", "eccodes-sys/eccodes-threads"] system = ["eckit-sys/system", "eccodes-sys/system"] # Format support (CMake default: ON) grib = ["eccodes-sys/product-grib"] # GRIB format support (requires eccodes) bufr = ["eccodes-sys/product-bufr"] # BUFR format support (requires eccodes) netcdf = [] # NetCDF data support (requires NetCDF library) odb = [] # ODB data support (requires odc) # Encoding (CMake default: ON) mars2grib = ["eckit-sys/geo-codec-grids"] # MARS2GRIB encoder (requires eckit geo codec grids for ORCA/FESOM/ICON) # Configuration (CMake default: ON) metkit-config = [] # Install metkit configuration files # Other (CMake default: OFF) experimental = [] # Experimental features fail-on-ccsds = [] # Fail on CCSDS [dependencies] cxx.workspace = true eckit-sys = { workspace = true, default-features = false } eccodes-sys = { workspace = true, default-features = false } bindman.workspace = true [build-dependencies] cxx-build.workspace = true bindman-utils.workspace = true bindman-build.workspace = true [package.metadata.docs.rs] metkit-1.19.2/rust/Cargo.toml0000664000175000017500000000164415232567717016236 0ustar alastairalastair[workspace] resolver = "2" members = ["crates/metkit-sys"] [workspace.package] edition = "2024" license = "Apache-2.0" repository = "https://github.com/ecmwf/metkit" rust-version = "1.90" readme = "README.md" keywords = ["ecmwf", "weather", "meteorology", "grib"] categories = ["science"] [workspace.dependencies] # Internal metkit-sys = { path = "crates/metkit-sys" } # Foundation crates eckit-sys = { git = "ssh://git@github.com/ecmwf/eckit.git", branch = "rust-bindings", default-features = false } eccodes-sys = { git = "ssh://git@github.com/ecmwf/rust-wrappers-playground.git", default-features = false } # Build tools bindman = { git = "ssh://git@github.com/ecmwf/bindman.git", rev = "47edf68" } bindman-build = { git = "ssh://git@github.com/ecmwf/bindman.git", rev = "47edf68" } bindman-utils = { git = "ssh://git@github.com/ecmwf/bindman.git", rev = "47edf68" } # External cxx = "1.0" cxx-build = "1.0" thiserror = "2" metkit-1.19.2/metkit.code-workspace0000664000175000017500000000005315232567717017427 0ustar alastairalastair{ "folders": [ { "path": "." } ] }metkit-1.19.2/pyproject.toml0000664000175000017500000000165015232567717016222 0ustar alastairalastair# pytest [tool.pytest.ini_options] minversion = "6.0" addopts = "-vv -s" testpaths = [ "pymetkit/tests" ] # pyproject.toml [build-system] requires = ["setuptools", "wheel", "cffi"] build-backend = "setuptools.build_meta" [project] name = "pymetkit" description = "Python interface for metkit" dynamic = ["version"] authors = [ { name = "European Centre for Medium-Range Weather Forecasts (ECMWF)", email = "software.support@ecmwf.int" }, ] license = { text = "Apache License Version 2.0" } requires-python = ">=3.10" dependencies = [ "cffi", "metkitlib", "findlibs" ] [tool.setuptools.dynamic] version = { file = ["VERSION"] } [tool.setuptools] packages = ["pymetkit"] package-dir = { "pymetkit" = "./python/pymetkit/src/pymetkit" } include-package-data = true zip-safe = false [tool.setuptools.package-data] "pymetkit" = [ "VERSION", "metkit_c.h" ] [project.optional-dependencies] tests = ["pytest"] metkit-1.19.2/src/0000775000175000017500000000000015232567717014073 5ustar alastairalastairmetkit-1.19.2/src/experimental/0000775000175000017500000000000015232567717016570 5ustar alastairalastairmetkit-1.19.2/src/experimental/netcdf4-example.cc0000664000175000017500000001112015232567717022052 0ustar alastairalastair#include #include #include "netcdf.h" /* This is the name of the data file we will create. */ #define FILE_NAME "sfc_pres_temp.nc" /* We are writing 2D data, a 6 x 12 lat-lon grid. We will need two * netCDF dimensions. */ #define NDIMS 2 #define NLAT 6 #define NLON 12 #define LAT_NAME "latitude" #define LON_NAME "longitude" /* Names of things. */ #define PRES_NAME "pressure" #define TEMP_NAME "temperature" #define UNITS "units" #define DEGREES_EAST "degrees_east" #define DEGREES_NORTH "degrees_north" /* These are used to construct some example data. */ #define SAMPLE_PRESSURE 900 #define SAMPLE_TEMP 9.0 #define START_LAT 25.0 #define START_LON -125.0 /* Handle errors by printing an error message and exiting with a * non-zero status. */ #define ERR(e) \ { \ printf("Error: %s\n", nc_strerror(e)); \ return 2; \ } int main() { int ncid, lon_dimid, lat_dimid, pres_varid, temp_varid; /* In addition to the latitude and longitude dimensions, we will also create latitude and longitude netCDF variables which will hold the actual latitudes and longitudes. Since they hold data about the coordinate system, the netCDF term for these is: "coordinate variables." */ int lat_varid, lon_varid; int dimids[NDIMS]; /* We will write surface temperature and pressure fields. */ float pres_out[NLAT][NLON]; float temp_out[NLAT][NLON]; float lats[NLAT], lons[NLON]; /* It's good practice for each netCDF variable to carry a "units" attribute. */ char pres_units[] = "hPa"; char temp_units[] = "celsius"; /* Error handling. */ int retval; for (int lat = 0; lat < NLAT; lat++) lats[lat] = START_LAT + 5. * lat; for (int lon = 0; lon < NLON; lon++) lons[lon] = START_LON + 5. * lon; for (int lat = 0; lat < NLAT; lat++) { for (int lon = 0; lon < NLON; lon++) { pres_out[lat][lon] = SAMPLE_PRESSURE + (lon * NLAT + lat); temp_out[lat][lon] = SAMPLE_TEMP + .25 * (lon * NLAT + lat); } } /* Create the file. */ if ((retval = nc_create(FILE_NAME, NC_CLOBBER, &ncid))) ERR(retval); /* Define the dimensions. */ if ((retval = nc_def_dim(ncid, LAT_NAME, NLAT, &lat_dimid))) ERR(retval); if ((retval = nc_def_dim(ncid, LON_NAME, NLON, &lon_dimid))) ERR(retval); // define latitudes if ((retval = nc_def_var(ncid, "lat", NC_FLOAT, 1, &lat_dimid, &lat_varid))) ERR(retval); if ((retval = nc_put_att_text(ncid, lat_varid, "standard_name", strlen(LAT_NAME), LAT_NAME))) ERR(retval); if ((retval = nc_put_att_text(ncid, lat_varid, UNITS, strlen(DEGREES_NORTH), DEGREES_NORTH))) ERR(retval); // define longitudes if ((retval = nc_def_var(ncid, "lon", NC_FLOAT, 1, &lon_dimid, &lon_varid))) ERR(retval); if ((retval = nc_put_att_text(ncid, lon_varid, "standard_name", strlen(LON_NAME), LON_NAME))) ERR(retval); if ((retval = nc_put_att_text(ncid, lon_varid, UNITS, strlen(DEGREES_EAST), DEGREES_EAST))) ERR(retval); /* Define the netCDF variables. The dimids array is used to pass the dimids of the dimensions of the variables.*/ dimids[0] = lat_dimid; dimids[1] = lon_dimid; /// pressure if ((retval = nc_def_var(ncid, "press", NC_FLOAT, NDIMS, dimids, &pres_varid))) ERR(retval); if ((retval = nc_put_att_text(ncid, pres_varid, "standard_name", strlen(PRES_NAME), PRES_NAME))) ERR(retval); if ((retval = nc_put_att_text(ncid, pres_varid, UNITS, strlen(pres_units), pres_units))) ERR(retval); /// temperature if ((retval = nc_def_var(ncid, "temp", NC_FLOAT, NDIMS, dimids, &temp_varid))) ERR(retval); if ((retval = nc_put_att_text(ncid, temp_varid, "standard_name", strlen(TEMP_NAME), TEMP_NAME))) ERR(retval); if ((retval = nc_put_att_text(ncid, temp_varid, UNITS, strlen(temp_units), temp_units))) ERR(retval); /* End define mode. */ if ((retval = nc_enddef(ncid))) ERR(retval); // write data if ((retval = nc_put_var_float(ncid, lat_varid, &lats[0]))) ERR(retval); if ((retval = nc_put_var_float(ncid, lon_varid, &lons[0]))) ERR(retval); if ((retval = nc_put_var_float(ncid, pres_varid, &pres_out[0][0]))) ERR(retval); if ((retval = nc_put_var_float(ncid, temp_varid, &temp_out[0][0]))) ERR(retval); // Close the file if ((retval = nc_close(ncid))) ERR(retval); return 0; } metkit-1.19.2/src/experimental/CMakeLists.txt0000664000175000017500000000026215232567717021330 0ustar alastairalastairecbuild_add_executable( TARGET netcdf4-example CONDITION HAVE_NETCDF SOURCES netcdf4-example.cc INCLUDES ${ECKIT_INCLUDE_DIRS} LIBS eckit NetCDF::NetCDF_C ) metkit-1.19.2/src/tools/0000775000175000017500000000000015232567717015233 5ustar alastairalastairmetkit-1.19.2/src/tools/grib1-to-grib2.cc0000664000175000017500000001371115232567717020174 0ustar alastairalastair/* * (C) Copyright 2026- 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 grib1-to-grib2.cc /// @brief CLI tool for converting grib1 to grib2 files. /// #include #include #include #include #include #include #include #include "eckit/config/LocalConfiguration.h" #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "eckit/io/DataHandle.h" #include "eckit/io/FileHandle.h" #include "eckit/io/MemoryHandle.h" #include "eckit/log/Log.h" #include "eckit/message/Message.h" #include "eckit/message/Reader.h" #include "eckit/option/CmdArgs.h" #include "eckit/option/EckitTool.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/api/Grib2Mars.h" #include "metkit/mars2grib/api/Mars2Grib.h" #include "metkit/mars2mars/api/Mars2Mars.h" using namespace eckit; using namespace eckit::option; //---------------------------------------------------------------------------------------------------------------------- class Grib1ToGrib2Tool final : public eckit::EckitTool { public: Grib1ToGrib2Tool(int argc, char** argv); ~Grib1ToGrib2Tool() override = default; private: int minimumPositionalArguments() const override { return 2; } void init(const CmdArgs& args) override; void execute(const CmdArgs& args) override; void usage(const std::string& tool) const override; }; //---------------------------------------------------------------------------------------------------------------------- Grib1ToGrib2Tool::Grib1ToGrib2Tool(int argc, char** argv) : eckit::EckitTool(argc, argv) {} void Grib1ToGrib2Tool::init(const CmdArgs& args) {} void Grib1ToGrib2Tool::usage(const std::string& tool) const { Log::info() << "Usage: " << tool << " [options] input output" << std::endl << std::endl << "Convert GRIB1 to GRIB2" << std::endl << std::endl; } //---------------------------------------------------------------------------------------------------------------------- std::unique_ptr readCodesHandle(eckit::message::Message& msg) { std::unique_ptr dataHandle{msg.readHandle()}; eckit::MemoryHandle* memoryHandle = reinterpret_cast(dataHandle.get()); if (memoryHandle == nullptr) { throw eckit::UserError("Could not read eckit::Message", Here()); } const auto* data = reinterpret_cast(memoryHandle->data()); const auto size = static_cast(memoryHandle->size()); return metkit::codes::codesHandleFromMessageCopy(metkit::codes::Span(data, size)); } eckit::LocalConfiguration mergeLocalConfigs(const eckit::LocalConfiguration& base, const eckit::LocalConfiguration& overwrite) { eckit::LocalConfiguration result{base}; for (const auto& key : overwrite.keys()) { if (overwrite.isString(key)) { result.set(key, overwrite.getString(key)); } else if (overwrite.isIntegral(key)) { result.set(key, overwrite.getLong(key)); } else if (overwrite.isFloatingPoint(key)) { result.set(key, overwrite.getDouble(key)); } else if (overwrite.isFloatingPointList(key)) { result.set(key, overwrite.getDoubleVector(key)); } else { throw eckit::NotImplemented("Unexpected type for '" + key + "'", Here()); } } return result; } //---------------------------------------------------------------------------------------------------------------------- void Grib1ToGrib2Tool::execute(const CmdArgs& args) { // Handles to conversion libraries metkit::grib2mars::Grib2Mars grib2mars; metkit::mars2mars::Mars2Mars mars2mars; metkit::mars2grib::Mars2Grib mars2grib; auto inputPath = eckit::PathName(args(0)); auto outputPath = eckit::PathName(args(1)); if (!inputPath.exists()) { throw eckit::UserError("Input file path '" + inputPath.path() + "' does not exist!", Here()); } // Remove the output file if it already exists, otherwise we are not able to write to it. if (outputPath.exists()) { remove(outputPath.asString().c_str()); } auto outputFile = eckit::FileHandle(outputPath, true); outputFile.openForWrite(0); eckit::message::Reader msgReader{args(0)}; eckit::message::Message msg; while ((msg = msgReader.next())) { auto codesHandle = readCodesHandle(msg); // Read the MARS/Misc dictionary from the input GRIB sample const auto originalMarsMisc = grib2mars.convert(*codesHandle); // Read the values from the input GRIB sample const std::vector values = codesHandle->getDoubleArray("values"); // Apply mappings to convert pre-MTG2 MARS/Misc to post-MTG2 MARS/Misc const auto mappedMarsMisc = mars2mars.convert(originalMarsMisc.mars); auto mars = mappedMarsMisc.mars; auto misc = mergeLocalConfigs(mappedMarsMisc.misc, originalMarsMisc.misc); // Encode into GRIB2 using mars2grib encoder const auto newSample = mars2grib.encode(values, mars, misc); // Write newly encoded GRIB2 sample to the output file outputFile.write(newSample->messageData().data(), newSample->messageData().size()); } outputFile.close(); } //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { Grib1ToGrib2Tool tool(argc, argv); return tool.start(); } metkit-1.19.2/src/tools/mars-archive-script.cc0000664000175000017500000002163515232567717021434 0ustar alastairalastair #include "eckit/io/DataHandle.h" #include "eckit/io/FileHandle.h" #include "eckit/option/CmdArgs.h" #include "eckit/option/SimpleOption.h" #include "metkit/mars/MarsParser.h" #include "metkit/tool/MetkitTool.h" #include using namespace metkit; using namespace metkit::mars; using namespace eckit; using namespace eckit::option; //---------------------------------------------------------------------------------------------------------------------- class MarsArchiveScript : public MetkitTool { using OverridesDict = std::map>>; public: // methods MarsArchiveScript(int argc, char** argv); ~MarsArchiveScript() override = default; private: // methods void init(const CmdArgs& args) override; void execute(const CmdArgs& args) override; void usage(const std::string& tool) const override; OverridesDict extractOverrides(MarsRequest& request); template void setOverrides(MarsRequest& rq, const OverridesDict& overrides, const char* overrideName, Args... more); void setOverrides(MarsRequest& rq, const OverridesDict& overrides); std::string srcfile(int cnt) const; std::string arcfile(int cnt) const; std::string cmpfile(int cnt) const; private: // members std::string infile_; std::string outfile_; std::string tempPrefix_; bool obs_; bool compare_; // Output environment variables std::string preObs_; std::string retrieveMars_; std::string archiveMars_; std::string compareMars_; std::string unblockCommand_; std::string retrieveComplete_; std::string archiveComplete_; std::string compareComplete_; std::string compareCommand_; }; //---------------------------------------------------------------------------------------------------------------------- MarsArchiveScript::MarsArchiveScript(int argc, char** argv) : MetkitTool(argc, argv), tempPrefix_("mars"), obs_(false), compare_(false), preObs_("PREOBS"), retrieveMars_("RETRIEVE_MARS"), archiveMars_("ARCHIVE_MARS"), compareMars_("COMPARE_MARS"), unblockCommand_("UNBLOCK"), retrieveComplete_("RETRIEVE_COMPLETE"), archiveComplete_("ARCHIVE_COMPLETE"), compareComplete_("COMPARE_COMPLETE"), compareCommand_("COMPARE") { options_.push_back(new SimpleOption("out", "Output filename (defaults output to stdout)")); options_.push_back(new SimpleOption("in", "Input filename (defaults input to stdin)")); options_.push_back(new SimpleOption("obs", "Handle observations rather than output GRIBs")); options_.push_back(new SimpleOption("compare", "Re-retrieve and compare results")); options_.push_back( new SimpleOption("legacy", "Use legacy environment variable names for old suite compatability")); options_.push_back(new SimpleOption("prefix", "Prefix for the temporary files. Typically \"mars\"")); } void MarsArchiveScript::usage(const std::string& tool) const { Log::info() << "Usage: " << tool << " [options]" << eckit::newl << eckit::newl << "Note: The output of this tool assumes that we are running in an ecflow suite with" << eckit::newl << " failure trapping enabled, as well as the following environment variables:" << eckit::newl << " RETRIEVE_MARS, ARCHIVE_MARS, COMPARE_MARS, UNBLOCK, RETRIEVE_COMPLETE" << eckit::newl << " ARCHIVE_COMPLETE, COMPARE_COMPLETE" << eckit::newl << eckit::newl << "Examples:" << eckit::newl << "=========" << eckit::newl << std::endl; } void MarsArchiveScript::init(const CmdArgs& args) { infile_ = args.getString("in", infile_); outfile_ = args.getString("out", outfile_); compare_ = args.getBool("compare", compare_); tempPrefix_ = args.getString("prefix", tempPrefix_); obs_ = args.getBool("obs", obs_); if (args.getBool("legacy", false)) { retrieveMars_ = "MARS_FROM_FDB"; archiveMars_ = "MARS_TO_IBM"; compareMars_ = "MARS_FROM_IBM"; unblockCommand_ = "UNBLOCK"; retrieveComplete_ = "FDB_COMPLETE"; archiveComplete_ = "ARC_COMPLETE"; compareComplete_ = "CMP_COMPLETE"; } if (obs_) compareCommand_ = "COMPOBS"; } std::string MarsArchiveScript::srcfile(int cnt) const { return tempPrefix_ + ".source." + translate(cnt); } std::string MarsArchiveScript::arcfile(int cnt) const { return tempPrefix_ + ".archive." + translate(cnt); } std::string MarsArchiveScript::cmpfile(int cnt) const { return tempPrefix_ + ".compare." + translate(cnt); } MarsArchiveScript::OverridesDict MarsArchiveScript::extractOverrides(MarsRequest& request) { OverridesDict ret; std::vector keys; request.getParams(keys); for (const auto& k : keys) { int pos = k.find('@'); if (pos != std::string::npos) { std::vector vals; request.getValues(k, vals); ret[k.substr(0, pos)][k.substr(pos + 1)] = vals; request.erase(k); } } return ret; } template void MarsArchiveScript::setOverrides(MarsRequest& rq, const OverridesDict& overrides, const char* overrideName, Args... more) { auto namedset = overrides.find(overrideName); if (namedset != overrides.end()) { for (const auto& kv : namedset->second) { rq.setValue(kv.first, kv.second); } } setOverrides(rq, overrides, more...); } void MarsArchiveScript::setOverrides(MarsRequest& rq, const OverridesDict& overrides) {} void MarsArchiveScript::execute(const CmdArgs& args) { // Input from file, or from stdin? std::unique_ptr in_file; if (!infile_.empty()) { in_file = std::make_unique(infile_.c_str(), std::ios::in | std::ios::binary); in_file->exceptions(std::ios::badbit); } std::istream& in(in_file ? *in_file : std::cin); // Output to file, or to stdout? std::unique_ptr out_file; if (!outfile_.empty()) { out_file = std::make_unique(outfile_.c_str(), std::ios::out | std::ios::binary); out_file->exceptions(std::ios::badbit); } std::ostream& out(out_file ? *out_file : std::cout); // Parse the input request MarsParser parser(in); auto requests = parser.parse(); std::vector overrides; for (auto& rq : requests) { overrides.emplace_back(extractOverrides(rq)); } // 1. Retrieve the source data (typically from the FDB) // n.b. loop over non-const, non-reference requests --> mutable copy in the loops int cnt = 0; if (obs_) { for (cnt = 0; cnt < requests.size(); ++cnt) { out << "$" << preObs_ << " $header." << translate(cnt + 1) << " $data." << translate(cnt + 1) << " " << arcfile(cnt) << "\n\n"; } } else { out << "$" << retrieveMars_ << " << @\n\n"; cnt = 0; for (auto rq : requests) { rq.verb("retrieve"); rq.setValue("target", srcfile(cnt)); setOverrides(rq, overrides[cnt++], "default", "retrieve", "fdb_retrieve"); rq.dump(out); } out << "@\n\n$" << retrieveComplete_ << "\n\n"; // 1.a) Any intermediate step required. Typically ln -s (historically handle "blocked" data output by older // Fortran // based systems) for (cnt = 0; cnt < requests.size(); ++cnt) { out << "$" << unblockCommand_ << " " << srcfile(cnt) << " " << arcfile(cnt) << "\n\n"; } } // 2. Archive the data out << "$" << archiveMars_ << " << @\n\n"; cnt = 0; for (auto rq : requests) { rq.verb("archive"); rq.setValue("source", arcfile(cnt)); setOverrides(rq, overrides[cnt++], "default", "archive"); rq.dump(out); } out << "@\n\n$" << archiveComplete_; // 3. Comparison of re-retrieved data if (compare_) { out << "\n\n$" << compareMars_ << " << @\n\n"; cnt = 0; for (MarsParsedRequest rq : requests) { rq.verb("retrieve"); rq.setValue("target", cmpfile(cnt)); setOverrides(rq, overrides[cnt++], "default", "compare", "ibm_retrieve"); rq.dump(out); } out << "@\n\n"; for (cnt = 0; cnt < requests.size(); ++cnt) { out << "$" << compareCommand_ << " " << arcfile(cnt) << " " << cmpfile(cnt) << "\n\n"; } out << "$" << compareComplete_; } out << std::endl; } //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { MarsArchiveScript tool(argc, argv); return tool.start(); }metkit-1.19.2/src/tools/message-to-mars.cc0000664000175000017500000000356315232567717020555 0ustar alastairalastair/* * (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 "eckit/config/Resource.h" #include "eckit/io/Buffer.h" #include "eckit/io/Offset.h" #include "eckit/runtime/Tool.h" #include "metkit/codes/GribToRequest.h" #include "metkit/mars/MarsRequest.h" using namespace metkit; using metkit::grib::MetFile; //---------------------------------------------------------------------------------------------------------------------- class Grib2Request : public eckit::Tool { public: Grib2Request(int argc, char** argv) : Tool(argc, argv) { path_ = eckit::Resource("-in", "input.grib"); ///< @todo Move to use Option } virtual ~Grib2Request() {} virtual void run(); private: // members eckit::PathName path_; }; void Grib2Request::run() { eckit::Log::debug() << "Opening GRIB file : " << path_ << std::endl; eckit::Buffer buffer(MetFile::gribBufferSize()); long len = 0; grib::MetFile file(path_); metkit::mars::MarsRequest onereq("GRIB"); size_t nMsg = 0; while ((len = file.readSome(buffer)) != 0) { metkit::mars::MarsRequest req("GRIB"); grib::GribToRequest::gribToRequest(buffer, len, req); // eckit::Log::info() << req << std::endl; ++nMsg; onereq.merge(req); } eckit::Log::info() << onereq << std::endl; } //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { Grib2Request tool(argc, argv); return tool.start(); } metkit-1.19.2/src/tools/check-mars2conf.cc0000664000175000017500000001421715232567717020514 0ustar alastairalastair #include #include "eckit/config/LocalConfiguration.h" #include "eckit/config/YAMLConfiguration.h" #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "eckit/log/JSON.h" #include "eckit/log/Log.h" #include "eckit/option/CmdArgs.h" #include "eckit/runtime/Tool.h" // This include is required to enable dictionary access traits from eckit::LocalConfiguration #include "metkit/mars2grib/frontend/normalization/normalizeMarsDict.h" #include "metkit/mars2grib/utils/dictionary_traits/dictaccess_eckit_configuration.h" #include "metkit/mars2grib/CoreOperations.h" eckit::LocalConfiguration mars2conf(const eckit::LocalConfiguration& mars) { eckit::LocalConfiguration opts; const std::string confJson = metkit::mars2grib::CoreOperations::dumpHeaderTest(mars, opts); return eckit::LocalConfiguration{eckit::YAMLConfiguration{confJson}}; } class CheckMars2conf : public eckit::Tool { public: CheckMars2conf(int argc, char** argv) : Tool{argc, argv} {} static void usage(const std::string& tool) { eckit::Log::info() << "\nUsage: " << tool << " inputFile" << std::endl; } void run() override { eckit::JSON json{eckit::Log::warning(), eckit::JSON::Formatting::indent(2)}; eckit::option::CmdArgs args{usage, 1, -1}; eckit::Log::info() << "Running " << args(0) << std::endl; const eckit::LocalConfiguration testCases{eckit::YAMLConfiguration{eckit::PathName(args(0))}}; eckit::Log::info() << "Loaded " << testCases.getSubConfigurations().size() << " test cases!" << std::endl; size_t count = 0; size_t failed = 0; for (const auto& testCase : testCases.getSubConfigurations()) { count++; if (!testCase.has("encoderConfiguration")) { continue; } auto mars = testCase.getSubConfiguration("mars"); const auto& expectedEncoder = testCase.getSubConfiguration("encoderConfiguration"); if (metkit::mars2grib::frontend::normalization::hack::fixMarsGrid(mars)) { eckit::Log::info() << "Fixed MARS grid" << std::endl; } eckit::LocalConfiguration actualEncoder; try { actualEncoder = mars2conf(mars).getSubConfiguration("GribHeaderLayoutData"); } catch (const eckit::Exception& e) { eckit::Log::warning() << "Encountered an exception!" << std::endl; { eckit::JSON json{eckit::Log::warning()}; json << testCase; eckit::Log::warning() << std::endl; } failed++; continue; } bool currentFailed = false; for (size_t si = 0; si <= 5; ++si) { const auto expectedSection = expectedEncoder.getSubConfigurations("sections")[si]; const auto actualSection = actualEncoder.getSubConfigurations("sections")[si].getSubConfiguration("SectionLayoutData"); const auto& expectedTemplate = expectedSection.getLong("templateNumber"); const auto& actualTemplate = actualSection.getLong("templateNumber"); if (expectedTemplate != actualTemplate) { eckit::Log::warning() << "Template number for section " << si << " does not match! : " << actualTemplate << " != " << expectedTemplate << std::endl; if (actualTemplate == 192'001'024'036) { eckit::Log::warning() << "Skipping..." << std::endl; continue; } currentFailed = true; } const auto& expectedConcepts = expectedSection.getSubConfigurations("concepts"); const auto& actualConcepts = actualSection.getStringVector("variantNames"); if (expectedConcepts.size() != actualConcepts.size()) { eckit::Log::warning() << "Number of concepts for section " << si << " does not match! : " << actualConcepts.size() << " != " << expectedConcepts.size() << std::endl; currentFailed = true; continue; } for (size_t ci = 0; ci < actualConcepts.size(); ++ci) { const auto expectedConcept = expectedConcepts[ci].getString("name"); const auto expectedVariant = expectedConcepts[ci].getString("type"); size_t pos = actualConcepts[ci].find("::"); const auto actualConcept = actualConcepts[ci].substr(0, pos); const auto actualVariant = actualConcepts[ci].substr(pos + 2); if (expectedConcept != actualConcept) { eckit::Log::warning() << "A concept for section " << si << " does not match! : " << actualConcept << " != " << expectedConcept << std::endl; currentFailed = true; } else if (expectedVariant != actualVariant) { eckit::Log::warning() << "A variant for section " << si << " does not match! : " << actualConcept << "::" << actualVariant << " != " << expectedConcept << "::" << expectedVariant << std::endl; currentFailed = true; } } } if (currentFailed) { failed++; } } std::ostringstream oss; oss << "Failed " << failed << " cases failed out of " << count << std::endl; eckit::Log::error() << oss.str(); if (failed != 0) { throw eckit::Exception(oss.str(), Here()); } } }; //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { CheckMars2conf tool(argc, argv); return tool.start(); } metkit-1.19.2/src/tools/ncmerge.cc0000664000175000017500000000203115232567717017156 0ustar alastairalastair/* * (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. */ // Baudouin Raoult - ECMWF Jan 2015 #include "metkit/netcdf/InputDataset.h" #include "metkit/netcdf/NCFileCache.h" #include "metkit/netcdf/OutputDataset.h" #include using namespace metkit::netcdf; int main(int argc, char** argv) { try { NCFileCache cache; OutputDataset out("out.nc", cache); for (int i = 1; i < argc; i++) { InputDataset f(argv[i], cache); std::cout << "@@@@@@@@@@@ " << argv[i] << std::endl; out.merge(f); } out.save(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; return 1; } return 0; } metkit-1.19.2/src/tools/grib-to-request.cc0000664000175000017500000001756715232567717020613 0ustar alastairalastair/* * (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 #include #include #include #include #include #include "eckit/filesystem/PathName.h" #include "eckit/io/FileHandle.h" #include "eckit/log/JSON.h" #include "eckit/log/Log.h" #include "eckit/message/Message.h" #include "eckit/message/Reader.h" #include "eckit/option/CmdArgs.h" #include "eckit/option/SimpleOption.h" #include "eckit/utils/StringTools.h" #include "metkit/hypercube/HyperCube.h" #include "metkit/mars/MarsRequest.h" #include "metkit/tool/MetkitTool.h" using namespace metkit; using namespace metkit::mars; using namespace eckit; using namespace eckit::option; //---------------------------------------------------------------------------------------------------------------------- class MarsRequestSetter : public eckit::message::MetadataGatherer { public: // methods MarsRequestSetter(MarsRequest& request) : request_(request) {} void setValue(const std::string& key, const std::string& value) override { request_.setValue(key, value); } void setValue(const std::string& key, long value) override { request_.setValue(key, value); } void setValue(const std::string& key, double value) override { request_.setValue(key, value); } private: // members MarsRequest& request_; }; //---------------------------------------------------------------------------------------------------------------------- class GribToRequestTool : public MetkitTool { public: GribToRequestTool(int argc, char** argv) : MetkitTool(argc, argv) { options_.push_back(new SimpleOption("verb", "Verb in the request, default = retrieve")); options_.push_back( new SimpleOption("database", "add database keyword to requests, default = none")); options_.push_back(new SimpleOption("source", "add source keyword to requests, default = none")); options_.push_back(new SimpleOption("target", "add target keyword to requests, default = none")); options_.push_back(new SimpleOption("one", "Merge into one request, potentially describing more data " "fields than the ones in the input file, default = false")); options_.push_back(new SimpleOption("compact", "Merge into a small number of requests, default = false")); options_.push_back(new SimpleOption("json", "Format request in json, default = false")); } private: // methods int minimumPositionalArguments() const override { return 1; } void execute(const eckit::option::CmdArgs& args) override; void init(const CmdArgs& args) override; void usage(const std::string& tool) const override; private: // members std::vector paths_; std::string verb_ = "retrieve"; std::string database_ = ""; std::string source_ = ""; std::string target_ = ""; bool one_ = false; bool compact_ = false; bool json_ = false; }; //---------------------------------------------------------------------------------------------------------------------- void GribToRequestTool::init(const CmdArgs& args) { args.get("one", one_); args.get("compact", compact_); args.get("verb", verb_); args.get("database", database_); args.get("source", source_); args.get("target", target_); args.get("json", json_); if (json_) { porcelain_ = true; } if (one_ and compact_) { Log::error() << "Options --one and --compact are mutually exclusive" << std::endl; std::exit(1); } } void GribToRequestTool::usage(const std::string& tool) const { Log::info() << "Usage: " << tool << " [options] [request1] [request2] ..." << std::endl << std::endl; Log::info() << "Examples:" << std::endl << "=========" << std::endl << std::endl << tool << " --one --verb=retrieve data.grib" << std::endl << tool << " --compact --verb=retrieve data.grib" << std::endl << std::endl; } namespace { static void toJSON(const std::vector& requests) { JSON j(Log::info()); for (const auto& r : requests) { r.json(j); } Log::info() << std::endl; } static void toStdOut(const std::vector& requests) { for (const auto& r : requests) { eckit::Log::info() << r << std::endl; } } static void addKeyValue(std::vector& requests, const std::string& key, const std::string& value) { std::transform(requests.begin(), requests.end(), requests.begin(), [key, value](MarsRequest& r) -> MarsRequest { r.setValue(key, value); return r; }); } } // namespace void GribToRequestTool::execute(const eckit::option::CmdArgs& args) { PathName inFile(args(0)); FileHandle dh(inFile); dh.openForRead(); eckit::message::Reader reader(dh, false); eckit::message::Message msg; std::vector requests; while ((msg = reader.next())) { MarsRequest r(verb_); MarsRequestSetter setter(r); msg.getMetadata(setter); if (one_ and requests.size()) { requests.back().merge(r); } else { requests.push_back(r); } } if (compact_ && requests.size() > 1) { std::map, std::vector> coherentRequests; // split the requests into groups of requests with the same set of metadata (but potentially different values) for (const auto& r : requests) { std::set keys; for (const auto& p : r.parameters()) { keys.insert(p.name()); } coherentRequests[keys].push_back(r); } requests.clear(); // compact each group of requests into a single request (if possible) for (const auto& [keys, reqs] : coherentRequests) { if (reqs.size() == 1) { // it is a single field - return its request as is requests.push_back(reqs.front()); continue; } MarsRequest merged{reqs.front()}; for (size_t i = 1; i < reqs.size(); ++i) { merged.merge(reqs[i]); } if (merged.count() == reqs.size()) { // the set of fields forms a full hypercube - return corresponding merged request requests.push_back(std::move(merged)); continue; } // sparse hypercube - we have to compute a set of compact requests describing the input fields metkit::hypercube::HyperCube h{merged}; for (const auto& r : reqs) { h.clear(r); } for (const auto& r : h.requests()) { requests.push_back(r); } } } if (not database_.empty()) { addKeyValue(requests, "database", database_); } if (StringTools::lower(verb_) == "archive") { addKeyValue(requests, "source", inFile); } if (not source_.empty()) { addKeyValue(requests, "source", source_); } if (not target_.empty()) { addKeyValue(requests, "target", target_); } if (json_) { toJSON(requests); } else { toStdOut(requests); } } //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { GribToRequestTool tool(argc, argv); return tool.start(); } metkit-1.19.2/src/tools/parse-mars-request.cc0000664000175000017500000000100415232567717021275 0ustar alastairalastair/* * (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 "ParseRequest.h" int main(int argc, char** argv) { ParseRequest tool(argc, argv); return tool.start(); } metkit-1.19.2/src/tools/grib-blob.cc0000664000175000017500000000442715232567717017410 0ustar alastairalastair/* * (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 "eckit/config/Resource.h" #include "eckit/io/Buffer.h" #include "eckit/option/CmdArgs.h" #include "eckit/option/Option.h" #include "eckit/runtime/Tool.h" #include "metkit/codes/GribMetaData.h" using namespace metkit; //---------------------------------------------------------------------------------------------------------------------- class GribBlob; static GribBlob* instance_ = 0; class GribBlob : public eckit::Tool { public: virtual void usage(const std::string& tool) const { eckit::Log::info() << std::endl << "Usage: " << tool << " [path2] ..." << std::endl; } virtual int numberOfPositionalArguments() const { return -1; } virtual int minimumPositionalArguments() const { return 1; } virtual void run(); GribBlob(int argc, char** argv) : eckit::Tool(argc, argv) { ASSERT(instance_ == 0); instance_ = this; } protected: // members std::vector options_; }; static void usage(const std::string& tool) { ASSERT(instance_); instance_->usage(tool); } void GribBlob::run() { eckit::option::CmdArgs args(&::usage, options_, numberOfPositionalArguments(), minimumPositionalArguments()); eckit::Buffer buffer(grib::MetFile::gribBufferSize()); long len = 0; for (size_t i = 0; i < args.count(); i++) { eckit::PathName path(args(i)); std::cout << "Processing " << path << std::endl; grib::MetFile file(path); size_t nMsg = 0; while ((len = file.readSome(buffer)) != 0) { metkit::grib::GribMetaData grib(buffer, len); ++nMsg; eckit::Log::info() << nMsg << " " << grib << std::endl; } } } //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { GribBlob tool(argc, argv); return tool.start(); } metkit-1.19.2/src/tools/ParseRequest.cc0000664000175000017500000001533315232567717020172 0ustar alastairalastair/* * (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 "ParseRequest.h" #include #include #include #include #include #include #include #include "eckit/log/JSON.h" #include "metkit/metkit_config.h" #include "metkit/hypercube/HyperCube.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsParser.h" #include "metkit/mars/MarsRequest.h" using namespace eckit; using namespace metkit::mars; #ifdef metkit_HAVE_MARS2MARS #include "metkit/mars2mars/api/Mars2Mars.h" #include "metkit/mars2mars/utils/dictionary_traits/dictaccess_mars_request.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" using namespace metkit::mars2mars; namespace { class ConvertedRequests : public FlattenCallback { public: ConvertedRequests(std::vector& flattenedRequests) : flattenedRequests_(flattenedRequests) {} void operator()(const MarsRequest& req) override { flattenedRequests_.push_back(converter_.convert(req).mars); } std::vector& flattenedRequests_; Mars2Mars converter_; }; } // namespace #endif //---------------------------------------------------------------------------------------------------------------------- void ParseRequest::execute(const eckit::option::CmdArgs& args) { for (size_t i = 0; i < args.count(); i++) { process(args(i)); } } void ParseRequest::init(const CmdArgs& args) { args.get("json", json_); args.get("compact", compact_); args.get("porcelain", porcelain_); if (porcelain_) { compact_ = true; } } void ParseRequest::usage(const std::string& tool) const { Log::info() << "Usage: " << tool << " [options] [request1] [request2] ..." << std::endl; if (grib2_) { Log::info() << " Converts requests to full GRIB2 metadata." << std::endl; } Log::info() << std::endl << "Examples:" << std::endl << "=========" << std::endl << std::endl << tool << " --json mars1.req mars2.req" << std::endl << tool << " --porcelain folderOfRequests" << std::endl << std::endl; } void ParseRequest::process(const eckit::PathName& path) { if (path.isDir()) { std::vector files; std::vector directories; path.children(files, directories); std::sort(files.begin(), files.end()); std::sort(directories.begin(), directories.end()); for (std::vector::const_iterator j = files.begin(); j != files.end(); ++j) { process(*j); } for (std::vector::const_iterator j = directories.begin(); j != directories.end(); ++j) { process(*j); } return; } if (!porcelain_) { std::cout << "==========> Parsing : " << path << std::endl; } std::ifstream in(path.asString().c_str()); MarsParser parser(in); bool inherit = true; MarsExpansion expand(inherit); auto p = parser.parse(); if (!porcelain_) { for (auto j = p.begin(); j != p.end(); ++j) { if (compact_) { j->dump(std::cout, "", ""); std::cout << std::endl; } else { j->dump(std::cout); } } std::cout << "----------> Expanding ... " << std::endl; } std::vector v = expand.expand(p); #ifdef metkit_HAVE_MARS2MARS if (grib2_) { std::vector out; for (const auto& r : v) { std::vector converted; ConvertedRequests cb(converted); expand.flatten(r, cb); if (converted.size() > 1) { std::map, std::vector> coherentRequests; // split the requests into groups of requests with the same set of metadata (but potentially different // values) for (const auto& r : converted) { std::set keys; for (const auto& p : r.parameters()) { keys.insert(p.name()); } coherentRequests[keys].push_back(r); } converted.clear(); // compact each group of requests into a single request (if possible) for (const auto& [keys, reqs] : coherentRequests) { if (reqs.size() == 1) { // it is a single field - return its request as is converted.push_back(reqs.front()); continue; } MarsRequest merged{reqs.front()}; for (size_t i = 1; i < reqs.size(); ++i) { merged.merge(reqs[i]); } if (merged.count() == reqs.size()) { // the set of fields forms a full hypercube - return // corresponding merged request converted.push_back(std::move(merged)); continue; } // sparse hypercube - we have to compute a set of compact requests describing the input fields metkit::hypercube::HyperCube h{merged}; for (const auto& r : reqs) { h.clear(r); } for (const auto& r : h.requests()) { converted.push_back(r); } } } out.insert(out.end(), converted.begin(), converted.end()); } std::swap(v, out); } #endif // HAVE_MARS2MARS for (std::vector::const_iterator j = v.begin(); j != v.end(); ++j) { if (json_) { if (compact_) { eckit::JSON jsonOut(std::cout); j->json(jsonOut); } else { eckit::JSON jsonOut(std::cout, eckit::JSON::Formatting(eckit::JSON::Formatting::BitFlags::INDENT_DICT)); j->json(jsonOut); } std::cout << std::endl; } else { if (compact_) { j->dump(std::cout, "", ""); std::cout << std::endl; } else { j->dump(std::cout); } } } } metkit-1.19.2/src/tools/odb-to-request.cc0000664000175000017500000001150015232567717020411 0ustar alastairalastair/* * (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 #include #include #include "eckit/filesystem/PathName.h" #include "eckit/io/FileHandle.h" #include "eckit/log/JSON.h" #include "eckit/log/Log.h" #include "eckit/option/CmdArgs.h" #include "eckit/option/Option.h" #include "eckit/option/SimpleOption.h" #include "eckit/runtime/Tool.h" #include "eckit/utils/StringTools.h" #include "metkit/odb/OdbToRequest.h" #include "metkit/tool/MetkitTool.h" using namespace metkit; using namespace eckit; using namespace eckit::option; //---------------------------------------------------------------------------------------------------------------------- class OdbToRequestTool : public MetkitTool { public: OdbToRequestTool(int argc, char** argv) : MetkitTool(argc, argv) { options_.push_back(new SimpleOption("verb", "Verb in the request, default = retrieve")); options_.push_back( new SimpleOption("database", "add database keyword to requests, default = none")); options_.push_back(new SimpleOption("source", "add source keyword to requests, default = none")); options_.push_back(new SimpleOption("target", "add target keyword to requests, default = none")); options_.push_back(new SimpleOption("one", "Merge into only one request, default = false")); options_.push_back(new SimpleOption("constant", "Only constant columns, default = true")); options_.push_back(new SimpleOption("json", "Format request in json, default = false")); } virtual ~OdbToRequestTool() {} private: // methods int minimumPositionalArguments() const { return 1; } virtual void execute(const eckit::option::CmdArgs& args); virtual void init(const CmdArgs& args); virtual void usage(const std::string& tool) const; private: // members std::vector paths_; std::string verb_ = "retrieve"; std::string database_ = ""; std::string source_ = ""; std::string target_ = ""; bool one_ = false; bool constant_ = true; bool json_ = false; }; //---------------------------------------------------------------------------------------------------------------------- void OdbToRequestTool::init(const CmdArgs& args) { args.get("one", one_); args.get("constant", constant_); args.get("verb", verb_); args.get("database", database_); args.get("source", source_); args.get("target", target_); args.get("json", json_); if (json_) { porcelain_ = true; } } void OdbToRequestTool::usage(const std::string& tool) const { Log::info() << "Usage: " << tool << " [options] [request1] [request2] ..." << std::endl << std::endl; Log::info() << "Examples:" << std::endl << "=========" << std::endl << std::endl << tool << " --one --verb=retrieve data.odb" << std::endl << std::endl; } static void toJSON(const std::vector& requests) { JSON j(Log::info()); for (auto& r : requests) { r.json(j); } Log::info() << std::endl; } static void toStdOut(const std::vector& requests) { for (auto& r : requests) { eckit::Log::info() << r << std::endl; } } static void addKeyValue(std::vector& requests, const std::string& key, const std::string& value) { std::transform(requests.begin(), requests.end(), requests.begin(), [key, value](MarsRequest& r) -> MarsRequest { r.setValue(key, value); return r; }); } void OdbToRequestTool::execute(const eckit::option::CmdArgs& args) { PathName inFile(args(0)); FileHandle dh(inFile); dh.openForRead(); std::vector requests = odb::OdbToRequest(verb_, one_, constant_).odbToRequest(dh); if (not database_.empty()) addKeyValue(requests, "database", database_); if (StringTools::lower(verb_) == "archive") { addKeyValue(requests, "source", inFile); } if (not source_.empty()) addKeyValue(requests, "source", source_); if (not target_.empty()) addKeyValue(requests, "target", target_); if (json_) { toJSON(requests); } else { toStdOut(requests); } } //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { OdbToRequestTool tool(argc, argv); return tool.start(); } metkit-1.19.2/src/tools/ParseRequest.h0000664000175000017500000000312515232567717020030 0ustar alastairalastair/* * (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. */ #pragma once #include #include "eckit/filesystem/PathName.h" #include "eckit/option/CmdArgs.h" #include "eckit/option/SimpleOption.h" #include "metkit/tool/MetkitTool.h" using eckit::PathName; using eckit::option::CmdArgs; using eckit::option::SimpleOption; using metkit::MetkitTool; //---------------------------------------------------------------------------------------------------------------------- class ParseRequest : public MetkitTool { public: ParseRequest(int argc, char** argv, bool convertToGrib2 = false) : MetkitTool(argc, argv), grib2_(convertToGrib2) { options_.push_back(new SimpleOption("json", "Format request in json, default = false")); options_.push_back(new SimpleOption("compact", "Compact output, default = false")); } virtual ~ParseRequest() {} private: // methods int minimumPositionalArguments() const { return 1; } void process(const eckit::PathName& path); virtual void execute(const eckit::option::CmdArgs& args); virtual void init(const CmdArgs& args); virtual void usage(const std::string& tool) const; private: // members bool json_ = false; bool compact_ = false; bool grib2_ = false; }; metkit-1.19.2/src/tools/convert-mars-request.cc0000664000175000017500000000101215232567717021642 0ustar alastairalastair/* * (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 "ParseRequest.h" int main(int argc, char** argv) { ParseRequest tool(argc, argv, true); return tool.start(); } metkit-1.19.2/src/tools/compare-mars-requests.py0000775000175000017500000002176115232567717022056 0ustar alastairalastair#!/usr/bin/env python3 # (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. # Emanuele Danovaro - ECMWF May 2021 import sys import os.path import json import datetime from dateutil.parser import parse def check_one(name, old, new): assert len(old) == 1 and len(new) == 1, f"{name.upper()}: too many values {len(old)} and {len(new)}" return old[0], new[0] def check_atMostOne(name, old, new): assert len(old) < 2 and len(new) < 2, f"{name.upper()}: too many values {len(old)} and {len(new)}" return old[0] if 0 < len(old) else None, new[0] if 0 < len(new) else None def compare_int(name, old, new): for o, n in zip(old, new): assert str(o).isnumeric() and str(n).isnumeric() and int(o) == int(n), f"{name.upper()}: value mismatch '{old}' and '{new}'" def compare_float(name, old, new): for o, n in zip(old, new): try: o = float(o) except ValueError: o = o.lower() try: n = float(n) except ValueError: n = n.lower() assert o == n, f"{name.upper()}: value mismatch '{old}' and '{new}'" def compare_intAndKeywords(name, old, new, keywords): o, n = check_atMostOne(name, old, new) if o in keywords or str(o).lower() in keywords: o = str(o).lower() if isinstance(o, str) else None if n in keywords or str(n).lower() in keywords: n = str(n).lower() if isinstance(n, str) else None assert o == n or (str(o).isnumeric() and str(n).isnumeric() and int(o) == int(n)), f"{name.upper()}: value mismatch '{old}' and '{new}'" def compare_grid(name, old, new): for o, n in zip(old, new): try: o = float(o) except ValueError: o = o.lower() try: n = float(n) except ValueError: n = n.lower() assert o == n, f"{name.upper()}: value mismatch '{old}' and '{new}'" def compare_expect(name, old, new): # 'any' or a positive integer value compare_intAndKeywords(name, old, new, ["any"]) def compare_accuracy(name, old, new): # 'off', 'av' or a positive integer value compare_intAndKeywords(name, old, new, [None, "off", "av", "n"]) def compare_frame(name, old, new): # 'off' or a positive integer value compare_intAndKeywords(name, old, new, ["off"]) def compare_truncation(name, old, new): # 'none', 'auto', 'off' or a positive integer value compare_intAndKeywords(name, old, new, ["off", "none", "auto"]) def compare_step(name, old, new): for o, n in zip(old, new): oParts = str(o).split('-') nParts = str(n).split('-') assert len(oParts) == len(nParts) and all(float(oi) == float(ni) for oi, ni in zip(oParts, nParts)), f"{name.upper()}: value mismatch '{old}' and '{new}'" def compare_str(name, old, new): o, n = check_one(name, old, new) assert o.replace('"', '').lower() == n.replace('"', '').lower(), f"{name.upper()}: string mismatch '{old[0]}' and '{new[0]}'" def compare_strList(name, old, new): for o, n in zip(old, new): compare_str(name, [o], [n]) def compare_strCaseSensitive(name, old, new): for o, n in zip(old, new): assert o.replace('"', '') == n.replace('"', ''), f"{name.upper()}: string mismatch '{old}' and '{new}'" def compare_time(name, old, new): o, n = check_one(name, old, new) if isinstance(o, str): o = int(o.replace(':', '')) if o>9999: o = o/100 if isinstance(n, str): n = int(n.replace(':', '')) if n > 9999: n = n / 100 assert o == n, f"{name.upper()}: time mismatch '{old[0]}' and '{new[0]}'" def compare_date(name, old, new): o, n = check_one(name, old, new) oStart = parse(str(o), default=datetime.datetime(1901, 1, 1)) oEnd = parse(str(o), default=datetime.datetime(2001, 12, 31)) nStart = parse(str(n), default=datetime.datetime(1901, 1, 1)) nEnd = parse(str(n), default=datetime.datetime(2001, 12, 31)) assert oStart.date() == nStart.date() and oEnd.date() == nEnd.date(), f"{name.upper()}: date mismatch '{old[0]}' and '{new[0]}'" def compare_dates(name, old, new): for o, n in zip(old, new): oStart = parse(str(o), default=datetime.datetime(1901, 1, 1)) oEnd = parse(str(o), default=datetime.datetime(2001, 12, 31)) nStart = parse(str(n), default=datetime.datetime(1901, 1, 1)) nEnd = parse(str(n), default=datetime.datetime(2001, 12, 31)) assert oStart.date() == nStart.date() and oEnd.date() == nEnd.date(), f"{name.upper()}: date mismatch '{o}' and '{n}'" def compare_expver(name, old, new): o, n = check_one(name, old, new) if isinstance(o, str): try: o = int(o) except ValueError: o = o.lower() if isinstance(n, str): try: n = int(n) except ValueError: n = n.lower() assert o == n, f"{name.upper()}: mismatch '{old[0]}' and '{new[0]}'" def ignore(name, old, new): pass def ignore_fixYaml(name, old, new): pass comparators = { "accuracy": ignore_fixYaml, # ????? # "accuracy": compare_accuracy, "anoffset": compare_int, # ????? "area": ignore_fixYaml, "bitmap": compare_strCaseSensitive, # block # channel "class": compare_str, "database": compare_str, # ????? "date": compare_date, "diagnostic": compare_int, # ?????? "direction": compare_int, # should we handle case "all" "domain": compare_str, "duplicates": compare_str, # can we ignore ? "expect": compare_expect, "expver": compare_expver, "fcmonth": compare_int, "fcperiod": compare_step, # ?????? "fieldset": compare_strCaseSensitive, "filter": compare_str, "format": compare_str, # ????? "frame": compare_frame, "frequency": compare_int, # ?????? "grid": compare_grid, "hdate": compare_dates, "ident": compare_int, "interpolation": compare_str, # "intgrid": "iteration": compare_int, # ?????? "latitude": compare_float, "levelist": compare_float, "levtype": compare_str, "longitude": compare_float, "lsm": compare_str, # ????? "method": compare_int, "number": compare_int, "obsgroup": compare_str, # ????? "obstype": compare_int, "origin": compare_expver, # ????? "packing": compare_str, "padding": compare_int, # ?????? "param": compare_float, # <-- to check that short names have been replaced, otherwise we can use compare_strList "priority": compare_int, "product": compare_str, # ????? "range": compare_int, "refdate": compare_date, "reference": compare_int, "reportype": compare_int, "repres": ignore, # resol "rotation": compare_grid, "section": compare_str, "source": compare_strCaseSensitive, "step": compare_step, "stream": compare_str, "system": compare_int, "target": compare_strCaseSensitive, "time": compare_time, "truncation": compare_truncation, "type": compare_str, "use": compare_str } def compare(name, old, new): if not isinstance(old, list): old = [old] if not isinstance(new, list): new = [new] assert len(old) == len(new), f"{name}: list mismatch {len(old)} and {len(new)}" assert name in comparators.keys(), f"{name}: not supported" comparators[name](name, old, new) # compare_date('date', '20210501', '2021-05-01') # compare_date('date', '20210501', '2021-May-01') # compare_date('date', '20210501', '2021-may-01') # compare_date('date', 'May-1', 'may-01') # compare_date('date', 'May', 'may') # compare_date('date', 'May', '20210521') # check command line parameters if len(sys.argv) != 3 or not os.path.isfile(sys.argv[1]) or not os.path.isfile(sys.argv[2]): print('Please specify the json files to be compared.') print('Usage: ',sys.argv[0], ' ') if len(sys.argv)>1 and not os.path.isfile(sys.argv[1]): print(sys.argv[1], 'is not a file') if len(sys.argv)>2 and not os.path.isfile(sys.argv[2]): print(sys.argv[2], 'is not a file') exit(1) #open json files and build case insensitive dictionaries json_lines_1 = [] with open(sys.argv[1]) as json_file: for line in json_file: j = {} for key, value in json.loads(line).items(): j[key.lower()] = value json_lines_1.append(j) json_lines_2 = [] with open(sys.argv[2]) as json_file: for line in json_file: j = {} for key, value in json.loads(line).items(): j[key.lower()] = value json_lines_2.append(j) assert len(json_lines_1) == len(json_lines_2), f"Json mismatch: {sys.argv[1]} contains {len(json_lines_1)} while {sys.argv[2]} contains {len(json_lines_2)}" for j1, j2 in zip(json_lines_1, json_lines_2): for key in set(j1.keys()) | set(j2.keys()): compare(key, j1.get(key), j2.get(key)) metkit-1.19.2/src/tools/bufr-sanity-check.cc0000664000175000017500000003672215232567717021072 0ustar alastairalastair/* * (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 "eccodes.h" #include "eckit/config/Resource.h" #include "eckit/exception/Exceptions.h" #include "eckit/io/FileHandle.h" #include "eckit/option/CmdArgs.h" #include "eckit/option/SimpleOption.h" #include "eckit/types/Date.h" #include "eckit/message/Message.h" #include "eckit/message/Reader.h" #include "metkit/codes/BUFRDecoder.h" #include "metkit/codes/BufrContent.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/tool/MetkitTool.h" #define WRONG_KEY_LENGTH 65535 using namespace metkit; using namespace eckit; using namespace eckit::option; //---------------------------------------------------------------------------------------------------------------------- enum Status { OK, FIXED, CORRUPTED }; class BufrCheck : public MetkitTool { public: BufrCheck(int argc, char** argv) : MetkitTool(argc, argv) { options_.push_back( new SimpleOption("abort-on-error", "Abort in case of corrupted message, default = true")); options_.push_back( new SimpleOption("patch-on-error", "Try to patch corrupted messages, default = false")); options_.push_back(new SimpleOption("skip-on-error", "Skip corrupted messages, default = false")); options_.push_back(new SimpleOption( "dont-patch-length", "Disable patching of message length in corrupted messages, default = false")); options_.push_back(new SimpleOption( "dont-patch-date", "Disable patching of date/time in corrupted messages, default = false")); options_.push_back(new SimpleOption( "ignore-century", "Disable patching of century in corrupted messages, default = false")); options_.push_back(new SimpleOption("ignore-type", "Ignore inconsistent type/subtype, default = false")); options_.push_back(new SimpleOption("acceptable-time-discrepancy", "Acceptable time discrepancy in seconds, default = 300")); options_.push_back( new SimpleOption("verbose", "Print details of all corrupted messages, default = false")); } virtual ~BufrCheck() {} private: // methods int minimumPositionalArguments() const { return 2; } Status checkMessageLength(const message::Message& msg, int numMessage, eckit::OrderedStringDict& transformation); Status checkSubType(const message::Message& msg, int numMessage); Status checkDate(const message::Message& msg, int numMessage, eckit::OrderedStringDict& transformation); void process(const PathName& input, const PathName& output); virtual void execute(const CmdArgs& args); virtual void init(const CmdArgs& args); virtual void usage(const std::string& tool) const; private: // members bool verbose_; bool abort_; bool patch_; bool skip_; bool ignoreLength_; bool ignoreDate_; bool ignoreCentury_; bool ignoreType_; long timeThreshold_; }; //---------------------------------------------------------------------------------------------------------------------- void BufrCheck::execute(const CmdArgs& args) { process(args(0), args(1)); } void BufrCheck::init(const CmdArgs& args) { patch_ = args.getBool("patch-on-error", false); skip_ = args.getBool("skip-on-error", false); abort_ = args.getBool("abort-on-error", !(patch_ || skip_)); if (abort_ + patch_ + skip_ > 1) { throw UserError( "Inconsistent configuration. You can only specify one of [--abort-on-error, --patch-on-error, " "--skip-on-error]"); } verbose_ = args.getBool("verbose", false); ignoreLength_ = args.getBool("dont-patch-length", false); ignoreDate_ = args.getBool("dont-patch-date", false); ignoreCentury_ = args.getBool("ignore-century", false); ignoreType_ = args.getBool("ignore-type", false); timeThreshold_ = args.getLong("acceptable-time-discrepancy", 300); } void BufrCheck::usage(const std::string& tool) const { Log::info() << "Usage: " << tool << " [options] [input] [output]" << std::endl << std::endl; Log::info() << "Examples:" << std::endl << "=========" << std::endl << std::endl << tool << " input.bufr output.bufr" << std::endl << std::endl << tool << " --skip-on-error --verbose input.bufr output.bufr" << std::endl << std::endl << tool << " --patch-on-error --ignore-century --acceptable-time-discrepancy=600 input.bufr output.bufr" << std::endl << std::endl << tool << " --patch-on-error --dont-patch-date input.bufr output.bufr" << std::endl << std::endl; } Status BufrCheck::checkMessageLength(const message::Message& msg, int numMessage, eckit::OrderedStringDict& transformation) { long totalLength = msg.getLong("totalLength"); long messageLength = msg.getLong("messageLength"); if (totalLength != messageLength && totalLength < WRONG_KEY_LENGTH) { if (verbose_) { Log::error() << "message " << numMessage << ", wrong key length in bufr message " << messageLength << " instead of " << totalLength << std::endl; } if (!patch_ || ignoreLength_) { return Status::CORRUPTED; } else { transformation.emplace_back("messageLength", std::to_string(totalLength)); return Status::FIXED; } } return Status::OK; } Status BufrCheck::checkSubType(const message::Message& msg, int numMessage) { long type = msg.getLong("rdbType"); long subtype = msg.getLong("oldSubtype"); long expectedType; if (codes::BUFRDecoder::typeBySubtype(subtype, expectedType)) { if (type == expectedType) { return Status::OK; } else { if (verbose_ || !ignoreType_) { Log::error() << "message " << numMessage << ", type " << type << " and expected type " << expectedType << " don't match for subtype " << subtype << std::endl; } return Status::CORRUPTED; } } else { if (verbose_ || !ignoreType_) { Log::error() << "message " << numMessage << ", unknown subtype " << subtype << std::endl; } return Status::CORRUPTED; } } Status BufrCheck::checkDate(const message::Message& msg, int numMessage, eckit::OrderedStringDict& transformation) { bool toFix = false; long localYear = msg.getLong("localYear"); long typicalYear = msg.getLong("typicalYear"); if (ignoreCentury_) { typicalYear = (localYear / 100) * 100 + typicalYear % 100; } long typicalMonth = msg.getLong("typicalMonth"); long typicalDay = msg.getLong("typicalDay"); long typicalJulian = 0; try { Date date(typicalYear, typicalMonth, typicalDay); typicalJulian = date.julian(); } catch (...) { if (verbose_) { Log::error() << "message " << numMessage << ", date is weird " << typicalYear << "/" << typicalMonth << "/" << typicalDay << std::endl; } if (!patch_) { return Status::CORRUPTED; } toFix = !ignoreDate_; } long typicalHour = msg.getLong("typicalHour"); long typicalMinute = msg.getLong("typicalMinute"); long typicalSecond = msg.getLong("typicalSecond"); if (typicalHour > 23 || typicalHour < 0 || typicalMinute > 59 || typicalMinute < 0 || typicalSecond > 59 || typicalSecond < 0) { if (verbose_) { Log::error() << "message " << numMessage << ", typical time is weird " << typicalHour << ":" << typicalMinute << ":" << typicalSecond << std::endl; } if (!patch_) { return Status::CORRUPTED; } toFix = !ignoreDate_; } long typicalTime = typicalHour * 3600 + typicalMinute * 60 + typicalSecond; long localMonth = msg.getLong("localMonth"); long localDay = msg.getLong("localDay"); long localJulian = 0; try { Date date(localYear, localMonth, localDay); localJulian = date.julian(); } catch (...) { if (verbose_) { Log::error() << "message " << numMessage << ", date is weird " << localYear << "/" << localMonth << "/" << localDay << std::endl; } return Status::CORRUPTED; } long localHour = msg.getLong("localHour"); long localMinute = msg.getLong("localMinute"); long localSecond = msg.getLong("localSecond"); // we accept localSecond==60 for backward compatibility (filterbufr behaviour) if (localHour > 23 || localHour < 0 || localMinute > 59 || localMinute < 0 || localSecond > 60 || localSecond < 0) { if (verbose_) { Log::error() << "message " << numMessage << ", local time is weird " << localHour << ":" << localMinute << ":" << localSecond << std::endl; } return Status::CORRUPTED; } long localTime = localHour * 3600 + localMinute * 60 + localSecond; if (abs((typicalJulian - localJulian) * 86400 + typicalTime - localTime) > timeThreshold_) { if (verbose_) { Log::error() << "message " << numMessage << ", date-time (" << typicalYear << "/" << typicalMonth << "/" << typicalDay << " " << typicalHour << ":" << typicalMinute << ":" << typicalSecond << ") and local date-time (" << localYear << "/" << localMonth << "/" << localDay << " " << localHour << ":" << localMinute << ":" << localSecond << ") differs" << std::endl; } toFix = !ignoreDate_; } if (toFix) { if (msg.getLong("edition") == 3) { transformation.emplace_back("typicalYearOfCentury", std::to_string(localYear - 2000)); } else { transformation.emplace_back("typicalYear", std::to_string(localYear)); transformation.emplace_back("typicalSecond", std::to_string(localSecond)); } transformation.emplace_back("typicalMonth", std::to_string(localMonth)); transformation.emplace_back("typicalDay", std::to_string(localDay)); transformation.emplace_back("typicalHour", std::to_string(localHour)); transformation.emplace_back("typicalMinute", std::to_string(localMinute)); return Status::FIXED; } return Status::OK; } void BufrCheck::process(const PathName& input, const PathName& output) { message::Reader reader(input); FileHandle out(output.path()); Offset pos; out.openForWrite(0); AutoClose closer(out); int err = 0; void* buffer = NULL; size_t size = 0; bool ok; unsigned int numMessage = 0; unsigned int missingKey = 0; unsigned int messageLength = 0; unsigned int inconsistentSubType = 0; unsigned int inconsistentDate = 0; message::Message rawMsg; do { try { pos = reader.position(); if ((rawMsg = reader.next())) { auto h = codes::codesHandleFromMessage({reinterpret_cast(rawMsg.data()), rawMsg.length()}); if (!h) { throw FailedLibraryCall("eccodes", "codes_handle_new_from_message", "failed to create handle", Here()); } codes::BufrContent* c = new codes::BufrContent(std::move(h)); message::Message msg(c); // verify the presence of section 2 (to store the MARS key) long localSectionPresent = msg.getLong("localSectionPresent"); ok = localSectionPresent != 0; if (!ok) { missingKey++; } else { eckit::OrderedStringDict transformation; switch (checkMessageLength(msg, numMessage, transformation)) { case Status::CORRUPTED: ok = false; messageLength++; break; case Status::FIXED: messageLength++; break; case Status::OK: break; }; switch (checkSubType(msg, numMessage)) { case Status::CORRUPTED: if (!ignoreType_) { ok = false; } inconsistentSubType++; break; case Status::FIXED: inconsistentSubType++; break; case Status::OK: break; }; switch (checkDate(msg, numMessage, transformation)) { case Status::CORRUPTED: ok = false; inconsistentDate++; break; case Status::FIXED: inconsistentDate++; break; case Status::OK: break; }; if (ok) { if (transformation.size() != 0) { msg.transform(transformation); } msg.write(out); } } if (!ok && abort_) { Log::error() << "message " << numMessage << " not compliant" << std::endl; exit(1); } numMessage++; } } catch (eckit::Exception& e) { Log::warning() << " Error parsing message " << numMessage << " - offset " << pos << std::endl; if (verbose_) { e.dumpStackTrace(Log::warning()); } } } while (pos != reader.position()); if (missingKey) Log::warning() << missingKey << " message" << (missingKey > 1 ? "s miss " : " misses ") << " the MARS key" << std::endl; if (messageLength) Log::warning() << messageLength << " message" << (messageLength > 1 ? "s " : " ") << " with incoherent message length in the MARS key" << std::endl; if (inconsistentSubType) Log::warning() << inconsistentSubType << " message" << (inconsistentSubType > 1 ? "s " : " ") << " with unknown or inconsistent subtype" << std::endl; if (inconsistentDate) Log::warning() << inconsistentDate << " message" << (inconsistentDate > 1 ? "s " : " ") << " with inconsistent date" << std::endl; } //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { BufrCheck tool(argc, argv); return tool.start(); } metkit-1.19.2/src/tools/nccompare.py0000775000175000017500000000747315232567717017572 0ustar alastairalastair#!/usr/bin/env python # (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. # Baudouin Raoult - ECMWF Jan 2015 # See http://unidata.github.io/netcdf4-python/netCDF4.Dataset-class.html from netCDF4 import Dataset import sys import numpy ERRORS = 0 def error(text): global ERRORS ERRORS += 1 print "+ ERROR:", text def mismatch(title, v1, v2): global ERRORS ERRORS += 1 print "+ MISMATCH:", title print " FILE:", sys.argv[1] print " VALUE:", v1 print " FILE:", sys.argv[2] print " VALUE:", v2 def compare_lists(what, l1, l2): s1 = set(l1) s2 = set(l2) if len(s1 - s2): error("%s [%s] only in %s" % (what, ",".join([str(x) for x in list(s1-s2)]), sys.argv[1])) return False if len(s2 - s1): error("%s [%s] only in %s" % (what, ",".join([str(x) for x in list(s2-s1)]), sys.argv[2])) return False return True def compare_attributes(ns, name, a1, a2): if type(a1) != type(a2): mismatch("Type of attributes '%s:%s'" % (ns, name,), type(a1), type(a2)) if a1 != a2: a1 = str(a1) a2 = str(a2) if len(a1) > 80: a1 = a1[:79] + '...' if len(a2) > 80: a2 = a2[:79] + '...' mismatch("Value of attributes '%s:%s'" % (ns, name,), a1, a2) def compare_variables(name, v1, v2): compare_lists("Dimensions for '%s'" % (name,), v1.dimensions, v2.dimensions) if v1.datatype != v2.datatype: mismatch("Type of variable '%s'" % (name,), v1.datatype, v2.datatype) if compare_lists("Variable '%s' attributes" % (name,), v1.ncattrs(), v2.ncattrs()): for a in v1.ncattrs(): compare_attributes(name, a, v1.getncattr(a), v2.getncattr(a)) vals1 = v1[:] vals2 = v2[:] if vals1.size != vals2.size: mismatch("Size of variable '%s'" % (name,), vals1.size, vals2.size) if not (vals1 == vals2).all(): vals1 = vals1.flatten() vals2 = vals2.flatten() idx = numpy.nonzero(vals1 - vals2)[0][0] if idx < len(vals1) and idx < len(vals2): mismatch("Value for variable '%s', first difference at index %s" % (name, idx), vals1[idx], vals2[idx]) def compare_dimensions(name, d1, d2): if len(d1) != len(d2): mismatch("Length of dimension '%s'" % (name,), len(d1), len(d2)) def check_file(nc, path): if len(nc.groups): error("Groups not supported (file %s)" % (path,)) if len(nc.cmptypes): error("Compound types not supported (file %s)" % (path,)) if len(nc.vltypes): error("Variable-length types not supported (file %s)" % (path,)) def compare_files(nc1, nc2): check_file(nc1, sys.argv[1]) check_file(nc2, sys.argv[2]) dims1 = nc1.dimensions dims2 = nc2.dimensions if compare_lists("Dimensions", dims1.keys(), dims2.keys()): for k in dims2.keys(): compare_dimensions(k, dims1[k], dims2[k]) vars1 = nc1.variables vars2 = nc2.variables if compare_lists("Variables", vars1.keys(), vars2.keys()): for k in vars1.keys(): compare_variables(k, vars1[k], vars2[k]) if compare_lists("Global attributes", nc1.ncattrs(), nc2.ncattrs()): for a in nc1.ncattrs(): compare_attributes("", a, nc1.getncattr(a), nc2.getncattr(a)) # Other things to checks # print nc1.data_model, nc1.disk_format # print nc1.parent, nc1.vltypes nc1 = Dataset(sys.argv[1], 'r') nc2 = Dataset(sys.argv[2], 'r') compare_files(nc1, nc2) if ERRORS: sys.exit(1) metkit-1.19.2/src/tools/CMakeLists.txt0000664000175000017500000000570415232567717020001 0ustar alastairalastair### provides MARS requests from GRIB files # ecbuild_add_executable( TARGET grib-blob # CONDITION HAVE_GRIB # SOURCES grib-blob.cc # INCLUDES ${ECKIT_INCLUDE_DIRS} # LIBS metkit eckit_option eckit # ) ecbuild_add_executable( TARGET metkit-grib1-to-grib2 SOURCES grib1-to-grib2.cc CONDITION HAVE_MARS2GRIB AND HAVE_MARS2MARS AND HAVE_BUILD_GRIB2_TOOLS INCLUDES ${ECKIT_INCLUDE_DIRS} NO_AS_NEEDED LIBS metkit eckit_option eckit ) ecbuild_add_executable( TARGET convert-mars-request CONDITION HAVE_MARS2MARS AND HAVE_BUILD_GRIB2_TOOLS SOURCES convert-mars-request.cc ParseRequest.cc ParseRequest.h INCLUDES ${ECKIT_INCLUDE_DIRS} NO_AS_NEEDED LIBS metkit eckit_option eckit ) ecbuild_add_executable( TARGET parse-mars-request CONDITION HAVE_BUILD_TOOLS SOURCES parse-mars-request.cc ParseRequest.cc ParseRequest.h INCLUDES ${ECKIT_INCLUDE_DIRS} NO_AS_NEEDED LIBS metkit eckit_option eckit ) ecbuild_add_executable( TARGET odb-to-request SOURCES odb-to-request.cc CONDITION HAVE_ODB AND HAVE_BUILD_TOOLS INCLUDES ${ECKIT_INCLUDE_DIRS} NO_AS_NEEDED LIBS metkit eckit_option ) ecbuild_add_executable( TARGET grib-to-request SOURCES grib-to-request.cc CONDITION HAVE_GRIB AND HAVE_BUILD_TOOLS INCLUDES ${ECKIT_INCLUDE_DIRS} NO_AS_NEEDED LIBS metkit eckit_option ) ecbuild_add_executable( TARGET bufr-sanity-check SOURCES bufr-sanity-check.cc CONDITION HAVE_BUFR AND HAVE_BUILD_TOOLS INCLUDES ${ECKIT_INCLUDE_DIRS} NO_AS_NEEDED LIBS metkit eckit_option ) ecbuild_add_executable( TARGET mars-archive-script SOURCES mars-archive-script.cc CONDITION HAVE_BUILD_TOOLS INCLUDES ${ECKIT_INCLUDE_DIRS} NO_AS_NEEDED LIBS metkit ) # ecbuild_add_executable( # TARGET check-mars2conf # CONDITION HAVE_MARS2GRIB # SOURCES check-mars2conf.cc # INCLUDES ${ECKIT_INCLUDE_DIRS} # NO_AS_NEEDED # LIBS metkit # ) # ecbuild_add_executable( # TARGET ncmerge # SOURCES ncmerge.cc # CONDITION HAVE_NETCDF # LIBS metkit NetCDF::NetCDF_C ) if(HAVE_BUILD_TOOLS) list( APPEND tools nccompare.py compare-mars-requests.py ) foreach( _tool ${tools} ) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/${_tool} DESTINATION ${CMAKE_BINARY_DIR}/bin FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endforeach() install( FILES ${CMAKE_BINARY_DIR}/bin/compare-mars-requests.py DESTINATION "bin" PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ ) endif() metkit-1.19.2/src/metkit/0000775000175000017500000000000015232567717015370 5ustar alastairalastairmetkit-1.19.2/src/metkit/fields/0000775000175000017500000000000015232567717016636 5ustar alastairalastairmetkit-1.19.2/src/metkit/fields/FieldIndexList.cc0000664000175000017500000000315615232567717022021 0ustar alastairalastair/* * (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 "eckit/exception/Exceptions.h" #include "metkit/fields/FieldIndexList.h" #include "metkit/fields/SimpleFieldIndex.h" namespace metkit { namespace fields { FieldIndexList::~FieldIndexList() { for (size_t i = 0; i < fields_.size(); ++i) { FieldIndex* h = fields_[i]; delete h; } } void FieldIndexList::readFrom(eckit::Stream& s) { ASSERT(length_.size() == 0); ASSERT(offset_.size() == 0); ASSERT(fields_.size() == 0); unsigned long count; s >> count; length_.resize(count); offset_.resize(count); fields_.resize(count); for (size_t i = 0; i < count; ++i) { unsigned long long x; s >> x; offset_[i] = x; s >> x; length_[i] = x; fields_[i] = new SimpleFieldIndex(s); } } void FieldIndexList::sendTo(eckit::Stream& s) const { ASSERT(length_.size() == offset_.size()); ASSERT(offset_.size() == fields_.size()); unsigned long count = length_.size(); s << count; for (size_t i = 0; i < count; ++i) { unsigned long long o = offset_[i]; unsigned long long l = length_[i]; s << o; s << l; fields_[i]->encode(s); } } } // namespace fields } // namespace metkit metkit-1.19.2/src/metkit/fields/SimpleFieldIndex.cc0000664000175000017500000000117715232567717022340 0ustar alastairalastair/* * (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 "metkit/fields/SimpleFieldIndex.h" namespace metkit { namespace fields { SimpleFieldIndex::SimpleFieldIndex(eckit::Stream& s) : FieldIndex(s) {} SimpleFieldIndex::~SimpleFieldIndex() {} } // namespace fields } // namespace metkit metkit-1.19.2/src/metkit/fields/FieldIndex.h0000664000175000017500000000277215232567717021032 0ustar alastairalastair/* * (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. */ #ifndef metkit_FieldIndex_H #define metkit_FieldIndex_H #include "eckit/memory/NonCopyable.h" #include "eckit/serialisation/Stream.h" namespace eckit { namespace message { class Message; } } // namespace eckit namespace metkit { namespace fields { class FieldIndex : private eckit::NonCopyable { public: FieldIndex(); FieldIndex(const eckit::message::Message&); FieldIndex(eckit::Stream&); virtual ~FieldIndex(); void getValue(const std::string& name, double& value); void getValue(const std::string& name, long& value); void getValue(const std::string& name, std::string& value); std::string substitute(const std::string& pattern) const; void encode(eckit::Stream&) const; void setValue(const std::string& name, double value); void setValue(const std::string& name, long value); void setValue(const std::string& name, const std::string& value); protected: // members std::map stringValues_; std::map longValues_; std::map doubleValues_; }; } // namespace fields } // namespace metkit #endif metkit-1.19.2/src/metkit/fields/FieldIndex.cc0000664000175000017500000000741215232567717021164 0ustar alastairalastair/* * (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 #include "eckit/exception/Exceptions.h" #include "eckit/message/Message.h" #include "eckit/utils/StringTools.h" #include "metkit/fields/FieldIndex.h" namespace metkit { namespace fields { FieldIndex::FieldIndex() {} FieldIndex::FieldIndex(eckit::Stream& s) { bool b, more; std::string key; std::string str; long ival; double dval; s >> more; while (more) { s >> key; s >> b; if (b) { s >> str; stringValues_[key] = str; } s >> b; if (b) { s >> ival; longValues_[key] = ival; } s >> b; if (b) { s >> dval; doubleValues_[key] = dval; } s >> more; } } FieldIndex::FieldIndex(const eckit::message::Message& message) { eckit::message::TypedSetter gather(*this); message.getMetadata(gather); } void FieldIndex::encode(eckit::Stream& s) const { std::set keys; for (auto j = stringValues_.begin(); j != stringValues_.end(); ++j) { keys.insert((*j).first); } for (auto j = longValues_.begin(); j != longValues_.end(); ++j) { keys.insert((*j).first); } for (auto j = doubleValues_.begin(); j != doubleValues_.end(); ++j) { keys.insert((*j).first); } for (auto j = keys.begin(); j != keys.end(); ++j) { const std::string& key = (*j); s << bool(true); s << key; auto js = stringValues_.find(key); s << bool(js != stringValues_.end()); if (js != stringValues_.end()) { s << (*js).second; } auto ls = longValues_.find(key); s << bool(ls != longValues_.end()); if (ls != longValues_.end()) { s << (*ls).second; } auto ds = doubleValues_.find(key); s << bool(ds != doubleValues_.end()); if (ds != doubleValues_.end()) { s << (*ds).second; } } s << bool(false); } FieldIndex::~FieldIndex() {} std::string FieldIndex::substitute(const std::string& pattern) const { return eckit::StringTools::substitute(pattern, stringValues_); } void FieldIndex::getValue(const std::string& key, double& value) { std::map::iterator j = doubleValues_.find(key); if (j == doubleValues_.end()) throw eckit::UserError(std::string("FieldIndex::getDouble failed for [") + key + "]"); value = (*j).second; } void FieldIndex::getValue(const std::string& key, long& value) { std::map::iterator j = longValues_.find(key); if (j == longValues_.end()) throw eckit::UserError(std::string("FieldIndex::getLong failed for [") + key + "]"); value = (*j).second; } void FieldIndex::getValue(const std::string& key, std::string& value) { std::map::iterator j = stringValues_.find(key); if (j == stringValues_.end()) throw eckit::UserError(std::string("FieldIndex::getString failed for [") + key + "]"); value = (*j).second; } void FieldIndex::setValue(const std::string& name, double value) { doubleValues_[name] = value; } void FieldIndex::setValue(const std::string& name, long value) { longValues_[name] = value; } void FieldIndex::setValue(const std::string& name, const std::string& value) { stringValues_[name] = value; } } // namespace fields } // namespace metkit metkit-1.19.2/src/metkit/fields/SimpleFieldIndex.h0000664000175000017500000000167515232567717022205 0ustar alastairalastair/* * (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. */ #ifndef metkit_SimpleFieldIndex_H #define metkit_SimpleFieldIndex_H #include "metkit/fields/FieldIndex.h" namespace metkit { namespace fields { // struct grib_handle; class SimpleFieldIndex : public FieldIndex { public: // -- Contructors SimpleFieldIndex(eckit::Stream&); // -- Destructor virtual ~SimpleFieldIndex() override; // -- Methods private: // members // friend std::ostream& operator<<(std::ostream& s,const SimpleFieldIndex& p) // { p.print(s); return s; } }; } // namespace fields } // namespace metkit #endif metkit-1.19.2/src/metkit/fields/FieldIndexList.h0000664000175000017500000000161715232567717021663 0ustar alastairalastair/* * (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. */ #ifndef metkit_FieldIndexList_H #define metkit_FieldIndexList_H #include "eckit/io/Offset.h" #include "eckit/serialisation/Stream.h" #include "eckit/types/Types.h" namespace metkit { namespace fields { class FieldIndex; class FieldIndexList { public: ~FieldIndexList(); void readFrom(eckit::Stream& s); void sendTo(eckit::Stream& s) const; eckit::OffsetList offset_; eckit::LengthList length_; std::vector fields_; }; } // namespace fields } // namespace metkit #endif metkit-1.19.2/src/metkit/mars2grib/0000775000175000017500000000000015232567717017260 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/0000775000175000017500000000000015232567717021077 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/make_HeaderLayout.h0000664000175000017500000001347415232567717024644 0ustar alastairalastair/* * (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 make_HeaderLayout.h /// @brief Top-level factory for GRIB header structural resolution. /// /// This header defines the entry point for the resolution subsystem. It /// coordinates the semantic inference of concepts and the subsequent /// mapping of those concepts to a physical GRIB section layout. /// /// @ingroup mars2grib_frontend_resolution /// #pragma once // System includes #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/sections/resolver/ActiveConceptsData.h" #include "metkit/mars2grib/backend/sections/resolver/SectionLayoutData.h" #include "metkit/mars2grib/frontend/GribHeaderLayoutData.h" #include "metkit/mars2grib/frontend/resolution/resolveActiveConcepts.h" #include "metkit/mars2grib/frontend/resolution/resolveSectionsLayout.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::frontend { /// /// @brief Orchestrates the complete resolution of a GRIB message blueprint. /// /// This factory function executes the two-stage resolution pipeline: /// * 1. **Semantic Resolution**: Infers which concepts and variants are /// active based on the input MARS and Options dictionaries. /// 2. **Structural Resolution**: Maps those active concepts to specific /// GRIB sections and selects the appropriate GRIB templates. /// /// The resulting @ref GribHeaderLayoutData is a POD-like structure suitable /// for move-construction into the @ref SpecializedEncoder. /// /// ------------------------------------------------------------------------ /// * @section resolution_pipeline Data Flow /// /// 1. `MarsDict_t` / `OptDict_t` -> @ref resolve_ActiveConcepts_or_throw /// 2. `ActiveConceptsData` -> @ref resolve_SectionsLayout_or_throw /// 3. `SectionLayoutData[]` -> @ref GribHeaderLayoutData /// /// ------------------------------------------------------------------------ /// /// @tparam MarsDict_t Type of the MARS dictionary. /// @tparam OptDict_t Type of the encoding options dictionary. /// * @param[in] marsDict Input MARS request. /// @param[in] optDict Encoder configuration and options. /// * @return A fully resolved @ref GribHeaderLayoutData. /// @throws Mars2GribHeaderLayoutException if any phase of the resolution fails. /// template GribHeaderLayoutData make_HeaderLayout_or_throw(const MarsDict_t& marsDict, const OptDict_t& optDict) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; using metkit::mars2grib::backend::sections::resolver::ActiveConceptsData; using metkit::mars2grib::frontend::GribHeaderLayoutData; using metkit::mars2grib::frontend::resolution::resolve_ActiveConcepts_or_throw; using metkit::mars2grib::frontend::resolution::resolve_SectionsLayout_or_throw; using metkit::mars2grib::frontend::resolution::SectionLayoutData; using metkit::mars2grib::utils::dict_traits::dict_to_json; using metkit::mars2grib::utils::exceptions::Mars2GribHeaderLayoutException; try { // Step 1: Semantic Analysis (What concepts are we encoding?) ActiveConceptsData activeConcepts = resolve_ActiveConcepts_or_throw(marsDict, optDict); // Step 2: Structural Mapping (Where do these concepts live in the GRIB sections?) std::array sectionsLayout = resolve_SectionsLayout_or_throw(activeConcepts); // Step 3: Blueprint Aggregation // We move the resolved array into the layout carrier to ensure zero-copy transfer. return GribHeaderLayoutData{{std::move(sectionsLayout)}}; } catch (...) { std::throw_with_nested(Mars2GribHeaderLayoutException("Critical failure: Unable to resolve GRIB HeaderLayout", dict_to_json(marsDict), Here())); } return {}; } namespace tests { /// /// @brief Generates a JSON diagnostic capture of the resolution pipeline. /// * This creates an object containing: /// 1. The original MARS request. /// 2. The resolved ActiveConcepts (Semantic layer). /// 3. The resolved GribHeaderLayout (Structural layer). /// * @return std::string A single JSON object string. /// template std::string capture_resolution_state_json(const MarsDict_t& mars, const OptDict_t& opt) { using metkit::mars2grib::backend::sections::resolver::debug::debug_convert_ActiveConceptsData_to_json; using metkit::mars2grib::frontend::debug::debug_convert_GribHeaderLayoutData_to_json; using metkit::mars2grib::utils::dict_traits::dict_to_json; // 1. Resolve states // Note: In a real test, you'd likely catch exceptions here to log failures auto activeConcepts = resolution::resolve_ActiveConcepts_or_throw(mars, opt); auto headerLayout = make_HeaderLayout_or_throw(mars, opt); // 2. Build the aggregate JSON string // We leverage the debug helpers we built in previous steps std::ostringstream oss; oss << "{ " << "\"mars\": " << dict_to_json(mars) << ", " << "\"activeConcepts\": " << debug_convert_ActiveConceptsData_to_json(activeConcepts) << ", " << "\"headerLayout\": " << debug_convert_GribHeaderLayoutData_to_json(headerLayout) << " }"; return oss.str(); } } // namespace tests } // namespace metkit::mars2grib::frontendmetkit-1.19.2/src/metkit/mars2grib/frontend/GribHeaderLayoutData.h0000664000175000017500000001117515232567717025241 0ustar alastairalastair/* * (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 GribHeaderLayoutData.h /// @brief Structural blueprint of a resolved GRIB message. /// /// This header defines the data structures used to represent the resolved /// structural state of a GRIB message. It acts as a bridge between the /// frontend (Resolution) and the backend (Encoding). /// #pragma once // System includes #include #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/sections/resolver/SectionLayoutData.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend { /// /// @brief Aggregated layout metadata for a complete GRIB message. /// /// `GribHeaderLayoutData` serves as a **blueprint** or **manifest**. It contains /// the resolved templates and specific variants for every GRIB section (0-7). /// /// This structure is strictly "data-only" and is intended to be passed to /// specialized encoders which use these indices to perform lookups in the /// `GeneralRegistry`. /// struct GribHeaderLayoutData { using GeneralRegistry = metkit::mars2grib::backend::concepts_::GeneralRegistry; using SectionLayoutData = metkit::mars2grib::backend::sections::resolver::SectionLayoutData; /// @brief Number of sections defined by the GRIB registry (typically 8) /// we use 5 because section from 6 to 8 are pure data /// static constexpr std::size_t nSections = GeneralRegistry::NSections; /// @brief Array of layout definitions, indexed by GRIB section number. std::array sectionLayouts{}; }; /// /// @namespace metkit::mars2grib::frontend::debug /// @brief Diagnostic and serialization utilities for header layout data. /// namespace debug { /// /// @brief Serialize the header layout to a JSON-like diagnostic string. /// /// Produces a machine-readable representation of the resolved layout. This is /// primarily used for regression testing (dumping "GRIB blueprints") to /// ensure that changes in metadata resolution do not unexpectedly alter /// the resulting GRIB structure. /// /// @param data The layout data to serialize. /// @return A JSON formatted string containing the section-variant map. /// inline std::string debug_convert_GribHeaderLayoutData_to_json(const GribHeaderLayoutData& data) { using metkit::mars2grib::backend::sections::resolver::debug::debug_convert_SectionLayoutData_to_json; std::ostringstream oss; oss << "{ \"GribHeaderLayoutData\": { \"sections\": [ "; for (std::size_t sid = 0; sid < GribHeaderLayoutData::nSections; ++sid) { // Delegate to the resolution::debug helper to get the "Concept::Variant" names oss << debug_convert_SectionLayoutData_to_json(data.sectionLayouts[sid]); if (sid + 1 < GribHeaderLayoutData::nSections) { oss << ", "; } } oss << " ] } }"; return oss.str(); } /// /// @brief Detailed print to ostream for human-readable logging. /// /// Formats the layout into a hierarchical tree view, showing which GRIB /// Template is used for each section and listing the specific Concept/Variant /// pairs that will be encoded. /// /// @param data The layout data to print. /// @param prefix Leading string for each line (used for indentation/log headers). /// @param os The output stream. /// inline void debug_print_GribHeaderLayoutData(const GribHeaderLayoutData& data, std::string_view prefix, std::ostream& os) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; os << prefix << " :: GribHeaderLayoutData Summary\n"; for (std::size_t sid = 0; sid < GribHeaderLayoutData::nSections; ++sid) { const auto& section = data.sectionLayouts[sid]; os << prefix << " :: Section[" << sid << "] Template: " << section.templateNumber << "\n"; for (std::size_t i = 0; i < section.count; ++i) { std::size_t id = section.variantIndices[i]; os << prefix << " :: - " << GeneralRegistry::conceptNameArr[id] << "::" << GeneralRegistry::variantNameArr[id] << "\n"; } } } } // namespace debug } // namespace metkit::mars2grib::frontend metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/0000775000175000017500000000000015232567717023765 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/normalization/normalization.h0000664000175000017500000000024115232567717027021 0ustar alastairalastair// Project includes #include "metkit/mars2grib/frontend/normalization/normalizeMarsDict.h" #include "metkit/mars2grib/frontend/normalization/normalizeMiscDict.h"metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/normalizeMiscDict.h0000664000175000017500000000502015232567717027553 0ustar alastairalastair/* * (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 normalization.h /// @brief Utilities for input dictionary sanitization and normalization. /// /// This header provides "just-in-time" sanitization routines used to ensure /// input dictionaries conform to backend expectations before the resolution /// phase begins. /// /// @ingroup mars2grib_frontend_normalization /// #pragma once // Project includes #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization { /// /// @brief Conditionally sanitizes a dictionary based on runtime options. /// /// This function implements a **pass-through or transform** pattern designed /// to minimize unnecessary copies. If sanitization is required by the provided /// options, the transformed data is stored in a caller-provided scratch /// buffer, and a reference to that buffer is returned. Otherwise, the /// original dictionary is returned as-is. /// /// @tparam MiscDict_t Type of the dictionary to be sanitized /// @tparam OptDict_t Type of the options dictionary driving the logic /// /// @param[in] miscDict Original dictionary to evaluate /// @param[in] optDict Configuration/Options dict used to determine policy /// @param[out] scratch Buffer used to store the sanitized result if needed /// /// @return A const reference to either the original or the sanitized dictionary /// template const MiscDict_t& normalize_MiscDict_if_enabled(const MiscDict_t& miscDict, const OptDict_t& optDict, const eckit::Value& language, MiscDict_t& scratch) { using metkit::mars2grib::utils::normalizeMiscEnabled; // TODO: Implement sanitization trigger logic based on optDict settings if (normalizeMiscEnabled(optDict)) { // [Development Stub] // Example: logic to prune illegal keys or normalize units // scratch = perform_transform(miscDict); return scratch; } // Default path: zero-copy pass-through return miscDict; } } // namespace metkit::mars2grib::frontend::normalizationmetkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/0000775000175000017500000000000015232567717025423 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/0000775000175000017500000000000015232567717026365 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/model.h0000664000175000017500000000106115232567717027634 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: model. /// template void sanitise_model_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for model } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/hdate.h0000664000175000017500000000106115232567717027621 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: hdate. /// template void sanitise_hdate_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for hdate } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/All.h0000664000175000017500000000533515232567717027254 0ustar alastairalastair#include "metkit/mars2grib/frontend/normalization/per_key/mars/activity.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/anoffset.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/channel.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/chem.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/class.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/dataset.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/date.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/direction.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/domain.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/experiment.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/expver.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/frequency.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/generation.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/grid.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/hdate.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/htime.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/ident.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/instrument.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/levelist.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/levtype.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/method.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/model.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/number.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/origin.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/packing.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/param.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/realization.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/resolution.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/stattype.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/step.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/stream.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/system.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/time.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/timespan.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/truncation.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/type.h" #include "metkit/mars2grib/frontend/normalization/per_key/mars/wavelength.h" #include "metkit/mars2grib/utils/generalUtils.h" metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/stattype.h0000664000175000017500000000107215232567717030413 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: stattype. /// template void sanitise_stattype_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for stattype } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/expver.h0000664000175000017500000000106415232567717030050 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: expver. /// template void sanitise_expver_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for expver } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/origin.h0000664000175000017500000000106415232567717030026 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: origin. /// template void sanitise_origin_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for origin } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/grid.h0000664000175000017500000000105615232567717027465 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: grid. /// template void sanitise_grid_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for grid } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/system.h0000664000175000017500000000106415232567717030063 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: system. /// template void sanitise_system_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for system } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/anoffset.h0000664000175000017500000000107215232567717030343 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: anoffset. /// template void sanitise_anoffset_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for anoffset } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/activity.h0000664000175000017500000000107215232567717030372 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: activity. /// template void sanitise_activity_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for activity } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/htime.h0000664000175000017500000000106115232567717027642 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: htime. /// template void sanitise_htime_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for htime } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/channel.h0000664000175000017500000000106715232567717030152 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: channel. /// template void sanitise_channel_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for channel } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/ident.h0000664000175000017500000000106115232567717027637 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: ident. /// template void sanitise_ident_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for ident } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/timespan.h0000664000175000017500000000107215232567717030356 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: timespan. /// template void sanitise_timespan_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for timespan } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/level.h0000664000175000017500000000101315232567717027640 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: level. /// template void sanitise_level_or_throw(T& value, const eckit::Value& language) { // TODO: Implement specific validation logic for level } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/wavelength.h0000664000175000017500000000110015232567717030672 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: wavelength. /// template void sanitise_wavelength_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for wavelength } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/chem.h0000664000175000017500000000105615232567717027454 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: chem. /// template void sanitise_chem_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for chem } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/domain.h0000664000175000017500000000106415232567717030006 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: domain. /// template void sanitise_domain_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for domain } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/generation.h0000664000175000017500000000110015232567717030661 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: generation. /// template void sanitise_generation_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for generation } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/realization.h0000664000175000017500000000110315232567717031052 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: realization. /// template void sanitise_realization_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for realization } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/packing.h0000664000175000017500000000106715232567717030156 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: packing. /// template void sanitise_packing_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for packing } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/experiment.h0000664000175000017500000000110015232567717030706 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: experiment. /// template void sanitise_experiment_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for experiment } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/instrument.h0000664000175000017500000000110015232567717030736 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: instrument. /// template void sanitise_instrument_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for instrument } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/levtype.h0000664000175000017500000000106715232567717030232 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: levtype. /// template void sanitise_levtype_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for levtype } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/method.h0000664000175000017500000000106415232567717030017 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: method. /// template void sanitise_method_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for method } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/date.h0000664000175000017500000000105615232567717027455 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: date. /// template void sanitise_date_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for date } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/stream.h0000664000175000017500000000106415232567717030032 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: stream. /// template void sanitise_stream_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for stream } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/truncation.h0000664000175000017500000000110015232567717030714 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: truncation. /// template void sanitise_truncation_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for truncation } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/dataset.h0000664000175000017500000000106715232567717030167 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: dataset. /// template void sanitise_dataset_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for dataset } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/param.h0000664000175000017500000000106115232567717027634 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: param. /// template void sanitise_param_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for param } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/number.h0000664000175000017500000000106415232567717030027 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: number. /// template void sanitise_number_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for number } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/frequency.h0000664000175000017500000000107515232567717030542 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: frequency. /// template void sanitise_frequency_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for frequency } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/levelist.h0000664000175000017500000000107215232567717030365 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: levelist. /// template void sanitise_levelist_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for levelist } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/time.h0000664000175000017500000000105615232567717027476 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: time. /// template void sanitise_time_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for time } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/resolution.h0000664000175000017500000000110015232567717030731 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: resolution. /// template void sanitise_resolution_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for resolution } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/type.h0000664000175000017500000000105615232567717027521 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: type. /// template void sanitise_type_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for type } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/class.h0000664000175000017500000000106115232567717027641 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: class. /// template void sanitise_class_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for class } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/direction.h0000664000175000017500000000107515232567717030521 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: direction. /// template void sanitise_direction_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for direction } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/mars/step.h0000664000175000017500000000105615232567717027513 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF and individual contributors. */ #pragma once #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Individual sanitization check for the GRIB key: step. /// template void sanitise_step_or_throw(const MarsDict_t& in, MarsDict_t& out, const eckit::Value& language) { // TODO: Implement specific validation logic for step } } // namespace metkit::mars2grib::frontend::normalization::per_key metkit-1.19.2/src/metkit/mars2grib/frontend/normalization/per_key/SanitizationRegistry.h0000664000175000017500000000227615232567717032010 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF */ #pragma once #include #include #include #include "metkit/mars2grib/frontend/normalization/per_key/mars/All.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization::per_key { /// /// @brief Registry to provide atomic access to sanitizers for testing. /// template struct MarsSanitizerRegistry { using SanitizerFn = std::function; struct Entry { std::string key; SanitizerFn func; }; static std::vector get_all_tests() { return { {"origin", per_key::sanitise_origin_or_throw}, {"class", per_key::sanitise_class_or_throw}, {"stream", per_key::sanitise_stream_or_throw}, {"type", per_key::sanitise_type_or_throw}, {"expver", per_key::sanitise_expver_or_throw}, {"date", per_key::sanitise_date_or_throw}, {"truncation", per_key::sanitise_truncation_or_throw} // ... the script can be extended to populate this list }; } }; } // namespace metkit::mars2grib::frontend::normalization::per_keymetkit-1.19.2/src/metkit/mars2grib/frontend/normalization/normalizeMarsDict.h0000664000175000017500000000756715232567717027604 0ustar alastairalastair/* * (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 normalization.h /// @brief Orchestration of input dictionary sanitization. /// #pragma once // System includes #include #include // Project includes #include "eckit/value/Value.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::normalization { namespace hack { /// /// @brief Normalizes legacy MARS grid strings in-place. /// @return true if the dictionary was actually modified. /// template bool needFixMarsGrid(const MarsDict_t& mars) { using metkit::mars2grib::utils::dict_traits::get_opt; auto marsGrid = get_opt(mars, "grid"); if (!marsGrid) return false; static const std::regex pattern{R"(L(\d+)x(\d+))"}; return std::regex_match(*marsGrid, pattern); } template bool fixMarsGrid(MarsDict_t& mars) { using metkit::mars2grib::utils::dict_traits::get_opt; auto marsGrid = get_opt(mars, "grid"); if (!marsGrid) return false; // Matches legacy LxN format (e.g., L640x320) static const std::regex pattern{R"(L(\d+)x(\d+))"}; std::smatch match; if (std::regex_match(*marsGrid, match, pattern)) { const long ni = std::stol(match[1].str()); const long nj = std::stol(match[2].str()); const double deltaLon = 360.0 / static_cast(ni); const double deltaLat = 180.0 / static_cast(nj - 1); mars.set("grid", std::to_string(deltaLon) + "/" + std::to_string(deltaLat)); return true; } return false; } } // namespace hack /// /// @brief Conditionally sanitizes a dictionary by running a suite of atomic key checks. /// /// This function iterates through specific keys (truncation, grid, etc.) and /// applies the normalization rules defined in the @ref per_key directory. /// /// @tparam MiscDict_t Type of the dictionary to be sanitized /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars Original dictionary /// @param[in] opt Options driving the sanitization policy /// @param[out] scratch Buffer to store results if a transformation occurs /// /// @todo build structure around tests /// /// @return A const reference to the sanitized or original dictionary /// template const MarsDict_t& normalize_MarsDict_if_enabled(const MarsDict_t& mars, const OptDict_t& opt, const eckit::Value& language, MarsDict_t& scratch) { using metkit::mars2grib::utils::fixMarsGridEnabled; using metkit::mars2grib::utils::normalizeMarsEnabled; using metkit::mars2grib::utils::dict_traits::get_opt; // Track if we have moved data into scratch yet bool modified = false; bool needsFix = fixMarsGridEnabled(opt) && hack::needFixMarsGrid(mars); bool needsSanitize = normalizeMarsEnabled(opt); if (needsFix || needsSanitize) { // We pay the performance debt here for the user's convenience scratch = mars; if (needsSanitize) { // Future placeholder for language-based logic } if (needsFix) { hack::fixMarsGrid(scratch); } return scratch; } // No flags enabled: zero overhead, return original reference return mars; } } // namespace metkit::mars2grib::frontend::normalizationmetkit-1.19.2/src/metkit/mars2grib/frontend/resolution/0000775000175000017500000000000015232567717023302 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/0000775000175000017500000000000015232567717026376 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/SectionTemplateSelectors.h0000664000175000017500000000310415232567717033531 0ustar alastairalastair#pragma once #include #include "metkit/mars2grib/backend/sections/resolver/SectionTemplateSelector.h" #include "metkit/mars2grib/frontend/resolution/section-recipes/impl/section0Recipes.h" #include "metkit/mars2grib/frontend/resolution/section-recipes/impl/section1Recipes.h" #include "metkit/mars2grib/frontend/resolution/section-recipes/impl/section2Recipes.h" #include "metkit/mars2grib/frontend/resolution/section-recipes/impl/section3Recipes.h" #include "metkit/mars2grib/frontend/resolution/section-recipes/impl/section4Recipes.h" #include "metkit/mars2grib/frontend/resolution/section-recipes/impl/section5Recipes.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::resolution::recipes { struct SectionTemplateSelectors { using GeneralRegistry = metkit::mars2grib::backend::concepts_::GeneralRegistry; using SectionTemplateSelector = metkit::mars2grib::backend::sections::resolver::SectionTemplateSelector; static inline const std::array value = [] { return std::array{ SectionTemplateSelector::make(impl::Section0Recipes), SectionTemplateSelector::make(impl::Section1Recipes), SectionTemplateSelector::make(impl::Section2Recipes), SectionTemplateSelector::make(impl::Section3Recipes), SectionTemplateSelector::make(impl::Section4Recipes), SectionTemplateSelector::make(impl::Section5Recipes)}; }(); }; } // namespace metkit::mars2grib::frontend::resolution::recipesmetkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/0000775000175000017500000000000015232567717027337 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section0Recipes.h0000664000175000017500000000245215232567717032552 0ustar alastairalastair//------------------------------------------------------------------------------ // Section 0 Recipes (runtime, static inline) //------------------------------------------------------------------------------ #pragma once // All elements needed to used the "dsl" objects to define recipes #include "metkit/mars2grib/backend/sections/resolver/dsl.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::resolution::recipes::impl { using namespace metkit::mars2grib::backend::concepts_; using namespace metkit::mars2grib::backend::sections::resolver::dsl; // clang-format off //------------------------------------------------------------------------------ // Section 0 – Individual Recipes //------------------------------------------------------------------------------ // Indicator Section (sentinel-only) inline const Recipe S0_R0 = make_recipe<0, Select >(); //------------------------------------------------------------------------------ // Section 0 – Aggregated Recipes //------------------------------------------------------------------------------ inline const Recipes Section0Recipes{ 0, std::vector{ &S0_R0 } }; // clang-format on } // namespace metkit::mars2grib::frontend::resolution::recipes::impl metkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section4Recipes.h0000664000175000017500000002470315232567717032561 0ustar alastairalastair//------------------------------------------------------------------------------ // Section 4 Recipes (runtime, static inline) //------------------------------------------------------------------------------ // All elements needed to used the "dsl" objects to define recipes #include "metkit/mars2grib/backend/sections/resolver/dsl.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::resolution::recipes::impl { using namespace metkit::mars2grib::backend::concepts_; using namespace metkit::mars2grib::backend::sections::resolver::dsl; // clang-format off //------------------------------------------------------------------------------ // Section 4 – Individual Recipes //------------------------------------------------------------------------------ inline const Recipe S4_R0 = make_recipe<0, Select, Select, Select, Select >(); inline const Recipe S4_R1 = make_recipe<1, Select, Select, Select, Select, Select >(); inline const Recipe S4_R2 = make_recipe<2, Select, Select, Select, Select, Select >(); inline const Recipe S4_R8 = make_recipe<8, Select, Select, Select, Select >(); inline const Recipe S4_R11 = make_recipe<11, Select, Select, Select, Select, Select >(); inline const Recipe S4_R12 = make_recipe<12, Select, Select, Select, Select, Select >(); inline const Recipe S4_R32 = make_recipe<32, Select, Select, Select, Select >(); inline const Recipe S4_R33 = make_recipe<33, Select, Select, Select, Select, Select >(); inline const Recipe S4_R40 = make_recipe<40, Select, Select, Select, Select, Select >(); inline const Recipe S4_R41 = make_recipe<41, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R42 = make_recipe<42, Select, Select, Select, Select, Select >(); inline const Recipe S4_R43 = make_recipe<43, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R50 = make_recipe<50, Select, Select, Select, Select, Select >(); inline const Recipe S4_R45 = make_recipe<45, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R46 = make_recipe<46, Select, Select, Select, Select, Select >(); inline const Recipe S4_R85 = make_recipe<85, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R48 = make_recipe<48, Select, Select, Select, Select, Select >(); inline const Recipe S4_R49 = make_recipe<49, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R60 = make_recipe<60, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R61 = make_recipe<61, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R76 = make_recipe<76, Select, Select, Select, Select, Select >(); inline const Recipe S4_R77 = make_recipe<77, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R78 = make_recipe<78, Select, Select, Select, Select, Select >(); inline const Recipe S4_R80 = make_recipe<79, Select, Select, Select, Select, Select >(); inline const Recipe S4_R79 = make_recipe<79, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R99 = make_recipe<99, Select, Select, Select, Select >(); inline const Recipe S4_R100 = make_recipe<100, Select, Select, Select, Select, Select >(); inline const Recipe S4_R103 = make_recipe<103, Select, Select, Select, Select >(); inline const Recipe S4_R104 = make_recipe<104, Select, Select, Select, Select, Select >(); inline const Recipe S4_R107 = make_recipe<107, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R121 = make_recipe<121, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R122 = make_recipe<122, Select, Select, Select, Select, Select, Select >(); inline const Recipe S4_R142 = make_recipe<142, Select, Select, Select, Select >(); inline const Recipe S4_R143 = make_recipe<143, Select, Select, Select, Select >(); //------------------------------------------------------------------------------ // Section 4 – Aggregated Recipes //------------------------------------------------------------------------------ inline const Recipes Section4Recipes{ 4, std::vector{ &S4_R0, &S4_R1, &S4_R2, &S4_R8, &S4_R11, &S4_R12, &S4_R32, &S4_R33, &S4_R40, &S4_R41, &S4_R42, &S4_R43, &S4_R50, &S4_R45, &S4_R46, &S4_R85, &S4_R48, &S4_R49, &S4_R60, &S4_R61, &S4_R76, &S4_R77, &S4_R78, &S4_R79, &S4_R80, &S4_R99, &S4_R100, &S4_R103, &S4_R104, &S4_R107, &S4_R121, &S4_R122, &S4_R142, &S4_R143 } }; // clang-format on } // namespace metkit::mars2grib::frontend::resolution::recipes::impl metkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section3Recipes.h0000664000175000017500000000550515232567717032557 0ustar alastairalastair//------------------------------------------------------------------------------ // Section 3 Recipes (runtime, static inline) //------------------------------------------------------------------------------ #pragma once // All elements needed to used the "dsl" objects to define recipes #include "metkit/mars2grib/backend/sections/resolver/dsl.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::resolution::recipes::impl { using namespace metkit::mars2grib::backend::concepts_; using namespace metkit::mars2grib::backend::sections::resolver::dsl; // clang-format off //------------------------------------------------------------------------------ // Section 3 – Individual Recipes //------------------------------------------------------------------------------ // Latitude / Longitude grid inline const Recipe S3_R0 = make_recipe<0, Select, Select >(); // Reduced or Gaussian grid inline const Recipe S3_R40 = make_recipe<40, Select, Select >(); // Spectral representation inline const Recipe S3_R50 = make_recipe<50, Select >(); // General unstructured grid inline const Recipe S3_R101 = make_recipe<101, Select, Select >(); inline const Recipe S3_R101_ORCA = make_recipe<101, Select, Select >(); // HEALPix grid inline const Recipe S3_R150 = make_recipe<150, Select, Select >(); // This is just meant to skip the section3 resolution in order to allow gridspec do be used after the encoder inline const Recipe S3_R1000 = make_recipe<1000, Select, Select >(); //------------------------------------------------------------------------------ // Section 3 – Aggregated Recipes //------------------------------------------------------------------------------ inline const Recipes Section3Recipes{ 3, std::vector{ &S3_R0, &S3_R40, &S3_R50, &S3_R101, &S3_R101_ORCA, &S3_R150, &S3_R1000 } }; // clang-format on } // namespace metkit::mars2grib::frontend::resolution::recipes::impl metkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section2Recipes.h0000664000175000017500000000744315232567717032561 0ustar alastairalastair//------------------------------------------------------------------------------ // Section 2 Recipes (runtime, static inline) //------------------------------------------------------------------------------ #pragma once // All elements needed to used the "dsl" objects to define recipes #include "metkit/mars2grib/backend/sections/resolver/dsl.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::resolution::recipes::impl { using namespace metkit::mars2grib::backend::concepts_; using namespace metkit::mars2grib::backend::sections::resolver::dsl; // clang-format off //------------------------------------------------------------------------------ // Section 2 – Individual Recipes //------------------------------------------------------------------------------ // Standard local definition inline const Recipe S2_R1 = make_recipe<1, Select >(); // Long-range products inline const Recipe S2_R15 = make_recipe<15, Select, 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, Select, Select >(); // Model-error products inline const Recipe S2_R25 = make_recipe<25, Select, Select >(); // Analysis-related products inline const Recipe S2_R36 = make_recipe<36, Select, Select >(); // Brightness temperature satellite products inline const Recipe S2_R37A = make_recipe<37, Select, Select, Select >(); inline const Recipe S2_R37B = make_recipe<37, Select, Select, Select, Select >(); // 4i Analysis-related products inline const Recipe S2_R38 = make_recipe<38, Select, Select, Select >(); // Analysis model-error products inline const Recipe S2_R39 = make_recipe<39, Select, Select, Select >(); //------------------------------------------------------------------------------ // Virtual (encoder-specific) templates //------------------------------------------------------------------------------ // DestinE Climate DT products inline const Recipe S2_R1001 = make_recipe<1001, Select, Select >(); // DestinE Extremes DT products inline const Recipe S2_R1002 = make_recipe<1002, Select, Select >(); // DestinE On-demand Extremes DT products //inline const Recipe S2_R1004 = // make_recipe<1004, // Select, // Select // >(); //------------------------------------------------------------------------------ // Section 2 – Aggregated Recipes //------------------------------------------------------------------------------ inline const Recipes Section2Recipes{ 2, std::vector{ &S2_R1, &S2_R15, &S2_R20, &S2_R24, &S2_R25, &S2_R36, &S2_R37A, &S2_R37B, &S2_R38, &S2_R39, &S2_R1001, &S2_R1002 } }; // clang-format on } // namespace metkit::mars2grib::frontend::resolution::recipes::impl metkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section5Recipes.h0000664000175000017500000000315115232567717032554 0ustar alastairalastair//------------------------------------------------------------------------------ // Section 5 Recipes (runtime, static inline) //------------------------------------------------------------------------------ #pragma once // All elements needed to used the "dsl" objects to define recipes #include "metkit/mars2grib/backend/sections/resolver/dsl.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::resolution::recipes::impl { using namespace metkit::mars2grib::backend::concepts_; using namespace metkit::mars2grib::backend::sections::resolver::dsl; // clang-format off //------------------------------------------------------------------------------ // Section 5 – Individual Recipes //------------------------------------------------------------------------------ // Simple packing inline const Recipe S5_R0 = make_recipe<0, Select >(); // CCSDS compression inline const Recipe S5_R42 = make_recipe<42, Select >(); // Spectral complex packing inline const Recipe S5_R51 = make_recipe<51, Select >(); //------------------------------------------------------------------------------ // Section 5 – Aggregated Recipes //------------------------------------------------------------------------------ inline const Recipes Section5Recipes{ 5, std::vector{ &S5_R0, &S5_R42, &S5_R51 } }; // clang-format on } // namespace metkit::mars2grib::frontend::resolution::recipes::impl metkit-1.19.2/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section1Recipes.h0000664000175000017500000000261015232567717032547 0ustar alastairalastair//------------------------------------------------------------------------------ // Section 1 Recipes (runtime, static inline) //------------------------------------------------------------------------------ #pragma once // All elements needed to used the "dsl" objects to define recipes #include "metkit/mars2grib/backend/sections/resolver/dsl.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::frontend::resolution::recipes::impl { using namespace metkit::mars2grib::backend::concepts_; using namespace metkit::mars2grib::backend::sections::resolver::dsl; // clang-format off //------------------------------------------------------------------------------ // Section 1 – Individual Recipes //------------------------------------------------------------------------------ // Identification Section inline const Recipe S1_R0 = make_recipe<0, Select, Select, Select, Select >(); //------------------------------------------------------------------------------ // Section 1 – Aggregated Recipes //------------------------------------------------------------------------------ inline const Recipes Section1Recipes{ 1, std::vector{ &S1_R0 } }; // clang-format on } // namespace metkit::mars2grib::frontend::resolution::recipes::impl metkit-1.19.2/src/metkit/mars2grib/frontend/resolution/resolveSectionsLayout.h0000664000175000017500000001313315232567717030041 0ustar alastairalastair/* * (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 GribHeaderLayoutResolver.h /// @brief Logic for mapping active semantic concepts to structural GRIB sections. /// /// This header defines the structural resolution logic that transforms /// normalized MARS request data (ActiveConceptsData) into a concrete /// GRIB message blueprint (GribHeaderLayoutData). /// /// @ingroup mars2grib_frontend_resolution /// #pragma once // System includes #include #include #include // Project includes #include "metkit/mars2grib/backend/sections/resolver/ActiveConceptsData.h" #include "metkit/mars2grib/backend/sections/resolver/SectionLayoutData.h" #include "metkit/mars2grib/frontend/GribHeaderLayoutData.h" #include "metkit/mars2grib/frontend/resolution/section-recipes/SectionTemplateSelectors.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::frontend::resolution { using SectionLayoutData = metkit::mars2grib::backend::sections::resolver::SectionLayoutData; using ActiveConceptsData = metkit::mars2grib::backend::sections::resolver::ActiveConceptsData; /// /// @brief Resolves the structural layout of all GRIB sections. /// /// This function orchestrates the "Recipe Selection" phase of the frontend. /// It iterates through every canonical GRIB section and utilizes static /// selectors to determine: /// 1. Which GRIB template should represent the section. /// 2. Which concept variants should be mapped to that section's fields. /// /// ------------------------------------------------------------------------ /// /// @section resolution_logic Resolution Logic /// /// The resolution is deterministic and based on the @ref ActiveConceptsData. /// For each section $S \in [0, N_{sections})$, the function: /// - Invokes the corresponding @ref SectionTemplateSelector. /// - Validates that the selector returned a valid payload for the correct section. /// /// ------------------------------------------------------------------------ /// /// @param[in] activeConcepts The semantic interpretation of the MARS request. /// @return A dense array of resolved @ref SectionLayoutData. /// @throws Mars2GribGenericException if a section returns invalid data. /// @throws Mars2GribSectionResolutionException if any section fails to resolve. /// inline std::array resolve_SectionsLayout_or_throw( const ActiveConceptsData& activeConcepts) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; using metkit::mars2grib::backend::sections::resolver::debug::debug_convert_ActiveConceptsData_to_json; using metkit::mars2grib::frontend::resolution::recipes::SectionTemplateSelectors; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; using metkit::mars2grib::utils::exceptions::Mars2GribSectionResolutionException; std::size_t section = 0; try { // Recover the static structural recipes (Stage 1 resolution) const auto& selectors = SectionTemplateSelectors::value; // Pre-allocate the layout container on the stack std::array sectionsLayout; // Iterate through all canonical GRIB sections (0 through 8) for (section = 0; section < GeneralRegistry::NSections; ++section) { // Apply the recipe for this specific section index SectionLayoutData sectionData = selectors[section].select_or_throw(activeConcepts); // Validation: Ensure the recipe actually targetted the expected section index if (sectionData.sectionNumber != section) { throw Mars2GribGenericException("SectionTemplateSelector mismatch: expected section " + std::to_string(section) + " but recipe returned " + std::to_string(sectionData.sectionNumber), Here()); } sectionsLayout[section] = std::move(sectionData); } return sectionsLayout; } catch (...) { std::throw_with_nested( Mars2GribSectionResolutionException("Critical failure: Unable to resolve GRIB HeaderLayout", section, debug_convert_ActiveConceptsData_to_json(activeConcepts), Here())); } mars2gribUnreachable(); } /// /// @namespace metkit::mars2grib::frontend::resolution::debug /// @brief Diagnostic tools for the resolution process. /// namespace debug { /// /// @brief Logs the resolution results for debugging purposes. /// * @param[in] layout Resolved section layouts /// @param[out] os Target output stream /// inline void debug_print_resolved_layout( const std::array& layout, std::ostream& os) { os << "--- GRIB Layout Resolution Debug ---" << std::endl; for (const auto& s : layout) { os << "Section " << s.sectionNumber << " -> Template " << s.templateNumber << " (Concepts: " << s.count << ")" << std::endl; } } } // namespace debug } // namespace metkit::mars2grib::frontend::resolutionmetkit-1.19.2/src/metkit/mars2grib/frontend/resolution/resolveActiveConcepts.h0000664000175000017500000001061515232567717027770 0ustar alastairalastair/* * (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 resolve_ActiveConcepts.h /// @brief Logic for mapping MARS metadata to active GRIB concept variants. /// /// This header defines the core resolution engine that identifies which /// GRIB entities (Concepts and Variants) are triggered by a specific /// combination of MARS and auxiliary metadata. /// #pragma once // project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/concepts/MatchingCallbacksRegistry.h" #include "metkit/mars2grib/backend/sections/resolver/ActiveConceptsData.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::frontend::resolution { using ActiveConceptsData = metkit::mars2grib::backend::sections::resolver::ActiveConceptsData; /// /// @brief Orchestrates the resolution of MARS metadata into GRIB concepts. /// /// This function is the primary entry point for the **Resolution Phase**. It iterates /// through the global registry of GRIB concepts and executes specialized "matching /// callbacks" against the input dictionaries. /// /// Each callback determines which specific variant of a GRIB concept (e.g., which /// type of Level or Step) is triggered by the current MARS request. /// /// ### Error Handling /// - Throws `Mars2GribGenericException` if the registry is inconsistent. /// - Uses `std::throw_with_nested` to preserve the trace of matching failures /// within specific callbacks. /// /// @tparam MarsDict_t Type of the MARS metadata dictionary. /// @tparam OptDict_t Type of the options/configuration dictionary. /// /// @param[in] marsDict The sanitized MARS request. /// @param[in] optDict The auxiliary metadata and encoding options. /// /// @return An `ActiveConceptsData` object containing the indices of all triggered variants. /// template ActiveConceptsData resolve_ActiveConcepts_or_throw(const MarsDict_t& marsDict, const OptDict_t& optDict) { // bring in GeneralRegistry and MatchersRegistry using metkit::mars2grib::backend::concepts_::GeneralRegistry; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; using Registry = metkit::mars2grib::backend::concepts_::MatchingCallbacksRegistry; using metkit::mars2grib::backend::sections::resolver::debug::debug_convert_ActiveConceptsData_to_json; try { // Lookup of the static registry const auto& callbacks = Registry::matchingCallbacks; // Error handling if (callbacks.size() != GeneralRegistry::NConcepts) { throw Mars2GribGenericException("Wrong size of Matchers", Here()); } /// /// @note The Matchers are typically ordered by Section (0 to 5) to ensure /// dependency resolution flows correctly. /// ActiveConceptsData activeConceptsData{}; activeConceptsData.count = 0; for (std::size_t i = 0; i < GeneralRegistry::NConcepts; ++i) { std::size_t localVariantId = callbacks[i](marsDict, optDict); std::size_t globalVariantId = GeneralRegistry::conceptOffsets[i] + localVariantId; if (localVariantId != GeneralRegistry::missing) { activeConceptsData.activeVariantIndices[i] = globalVariantId; activeConceptsData.activeConceptsIndices[activeConceptsData.count++] = i; } else { activeConceptsData.activeVariantIndices[i] = GeneralRegistry::missing; } } eckit::Log::debug() << "Resolved ActiveConceptsData: " << debug_convert_ActiveConceptsData_to_json(activeConceptsData) << std::endl; // Return the active concepts return activeConceptsData; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Unable to match ActiveConcepts", Here())); } mars2gribUnreachable(); }; } // namespace metkit::mars2grib::frontend::resolutionmetkit-1.19.2/src/metkit/mars2grib/frontend/header/0000775000175000017500000000000015232567717022327 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/frontend/header/SpecializedEncoder.h0000664000175000017500000004545115232567717026245 0ustar alastairalastair/* * (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 SpecializedEncoder.h /// @brief Fully specialized, hot-path GRIB header encoder. /// /// This file defines the `SpecializedEncoder` class template, which represents /// the **final, performance-critical encoding stage** of the mars2grib frontend. /// /// The encoder is responsible for: /// /// - owning a fully resolved GRIB header layout /// - generating an optimized, immutable execution plan at construction time /// - executing the plan in a dense, allocation-minimized hot path /// /// Unlike generic or incremental encoders, this class **internalizes all /// resolution work** and is designed to be invoked repeatedly with minimal /// overhead once constructed. /// /// ----------------------------------------------------------------------------- /// Architectural position /// ----------------------------------------------------------------------------- /// /// The `SpecializedEncoder` sits at the boundary between: /// /// - **frontend resolution** (concept selection, section layout, planning) /// - **encoding execution** (dictionary mutation via callbacks) /// /// All expensive or branching logic is assumed to have already happened before /// this class is instantiated. /// /// In particular: /// /// - Section templates are already resolved /// - Concept variants are already fixed /// - The header layout is complete and immutable /// /// As a result, `encode()` executes a **pre-compiled sequence of callbacks** /// without any conditional logic beyond null-checks. /// /// ----------------------------------------------------------------------------- /// Design goals /// ----------------------------------------------------------------------------- /// /// This class is explicitly designed with the following goals: /// /// 1. **Hot-path execution** /// - No dynamic resolution /// - No registry lookups /// - No allocation except controlled cloning /// /// 2. **Immutability** /// - The layout and plan are `const` /// - The encoder is thread-safe after construction /// /// 3. **Move-only ownership** /// - Layout data is moved into the encoder /// - Copies are explicitly disabled /// /// 4. **Failure transparency** /// - Any failure is wrapped in a domain-specific exception /// - Full diagnostic context is preserved via nested exceptions /// /// ----------------------------------------------------------------------------- /// Typical lifecycle /// ----------------------------------------------------------------------------- /// /// The intended usage pattern is: /// /// @code /// // One-time resolution phase /// GribHeaderLayoutData layout = resolve_layout_or_throw(...); /// /// // One-time specialization /// SpecializedEncoder encoder( /// std::move(layout) /// ); /// /// // Hot-path execution (possibly millions of times) /// auto grib = encoder.encode(mars, par, opt); /// @endcode /// /// ----------------------------------------------------------------------------- /// Transitional staged-encoding support /// ----------------------------------------------------------------------------- /// /// This class also exposes a temporary staged interface via `prepare()` and /// `finaliseEncoding()`. /// /// These functions are introduced in preparation for a cache mechanism that /// will soon be implemented in the higher-level encoding pipeline. /// /// Their purpose is to split the hot-path header construction into: /// /// - an immutable reusable preparation phase /// - a lightweight finalisation phase operating from a prepared sample /// /// This staged interface is intended to support incremental integration and /// performance comparison while the future cache design is being introduced. /// /// ----------------------------------------------------------------------------- /// Template parameters /// ----------------------------------------------------------------------------- /// /// @tparam MarsDict_t Dictionary type containing MARS metadata /// @tparam ParDict_t Dictionary type containing parameter metadata /// @tparam OptDict_t Dictionary type containing encoding options /// @tparam OutDict_t Dictionary type representing the GRIB output /// /// All dictionary types are expected to satisfy the `dict_traits` interface /// used throughout mars2grib. /// #pragma once // System includes #include #include // Project includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/frontend/GribHeaderLayoutData.h" #include "metkit/mars2grib/frontend/header/EncodingPlan.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::frontend::header { /// /// @brief Fully specialized GRIB encoder. /// /// This class represents a **fully materialized encoder instance**, where: /// /// - the GRIB header layout is already resolved /// - the execution plan is generated eagerly at construction /// /// Once constructed, the encoder performs **no further planning or resolution**. /// The `encode()` method executes a dense, pre-computed plan consisting of /// concept setter callbacks organized by: /// /// - encoding stage /// - GRIB section /// - concept callback /// /// The encoder is **logically immutable** and safe to reuse across multiple /// encoding calls with different input dictionaries. /// template class SpecializedEncoder { public: /// /// @brief Alias for the execution plan. /// /// The execution plan is a statically typed, nested container describing /// the exact sequence of operations required to populate the GRIB header. /// /// Its structure reflects the conceptual hierarchy: /// /// - stages /// - sections /// - concept callbacks /// /// The plan is generated once at construction time and never modified. /// using Plan_t = metkit::mars2grib::frontend::header::detail::EncodingPlan; /// /// @brief Alias for resolved header layout data. /// /// This object describes the finalized layout of the GRIB header, /// including: /// /// - which sections are present /// - which templates are used /// - which concept variants are active /// /// It is assumed to be complete and internally consistent. /// using HeaderLayout_t = metkit::mars2grib::frontend::GribHeaderLayoutData; /// /// @brief Construct the encoder by internalizing the header layout. /// /// This constructor performs two tightly coupled operations: /// /// 1. Moves the resolved header layout into the encoder's internal state /// 2. Builds the optimized execution plan from the internalized layout /// /// The order of operations is intentional: /// /// - The layout is stored first /// - The plan factory reads directly from the stored layout /// /// @param[in] headerLayout /// Fully resolved header layout to be moved into the encoder. /// /// @throws Mars2GribException /// If plan construction fails due to inconsistent layout data. /// /// @note /// After construction, both the layout and the plan are immutable. /// explicit SpecializedEncoder(HeaderLayout_t&& headerLayout) : layout_{std::move(headerLayout)}, plan_{detail::make_EncodingPlan_or_throw(layout_)} {} /// /// @name Special member functions /// @{ /// /// /// @brief Copy construction is disabled. /// /// Copying the encoder would imply copying the layout and plan, /// which is both expensive and semantically undesirable. /// SpecializedEncoder(const SpecializedEncoder&) = delete; /// /// @brief Copy assignment is disabled. /// SpecializedEncoder& operator=(const SpecializedEncoder&) = delete; /// /// @brief Move construction is allowed. /// /// Enables efficient transfer of ownership when caching or /// storing encoders in containers. /// SpecializedEncoder(SpecializedEncoder&&) = default; /// /// @brief Move assignment is allowed. /// SpecializedEncoder& operator=(SpecializedEncoder&&) = default; /// /// @brief Destructor. /// /// Trivial destructor; all owned state is RAII-managed. /// ~SpecializedEncoder() = default; /// @} /// /// @brief Execute the encoding plan (hot path). /// /// This method performs the actual GRIB header encoding. /// /// Characteristics: /// /// - No layout resolution /// - No plan modification /// - No dynamic dispatch /// /// The algorithm is: /// /// 1. Create an initial GRIB sample dictionary /// 2. Iterate through the execution plan: /// - stages /// - sections /// - concept callbacks /// 3. Apply each non-null callback to the current dictionary /// 4. Clone the dictionary between stages as required /// /// @param[in] mars /// MARS metadata dictionary /// /// @param[in] par /// Parameter metadata dictionary /// /// @param[in] opt /// Encoding options dictionary /// /// @return /// A newly allocated dictionary containing the encoded GRIB header /// /// @throws Mars2GribEncoderException /// On any failure during encoding. The exception includes: /// - serialized input dictionaries /// - serialized header layout /// - full nested exception chain /// std::unique_ptr encode(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) const { using metkit::mars2grib::frontend::debug::debug_convert_GribHeaderLayoutData_to_json; using metkit::mars2grib::utils::dict_traits::clone_or_throw; using metkit::mars2grib::utils::dict_traits::dict_to_json; using metkit::mars2grib::utils::dict_traits::make_from_sample_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribEncoderException; try { auto samplePtr = make_from_sample_or_throw("GRIB2"); // Encoding loop as a dense set of optimized operations for (const auto& stage : plan_) { for (const auto& section : stage) { for (const auto& conceptCallback : section) { if (conceptCallback) { conceptCallback(mars, par, opt, *samplePtr); } } } samplePtr = clone_or_throw(*samplePtr); } return samplePtr; } catch (...) { std::throw_with_nested(Mars2GribEncoderException( "Critical failure in SpecializedEncoder execution", dict_to_json(mars), dict_to_json(par), dict_to_json(opt), debug_convert_GribHeaderLayoutData_to_json(layout_), Here())); } } /// /// @brief Execute the preparation phase of the staged encoder. /// /// This method evaluates only the prefix of the execution plan required /// to build an immutable reusable sample for later completion. /// /// In particular, it executes all stages up to and including the /// override stage, then returns the resulting output object as a /// read-only prepared sample. /// /// This method exists to support a temporary staged-encoding workflow /// introduced in preparation for a cache mechanism that will soon be /// implemented in the surrounding encoding pipeline. /// /// Characteristics: /// /// - No layout resolution /// - No plan modification /// - No dynamic dispatch /// - Execution limited to the preparation prefix of the plan /// /// The algorithm is: /// /// 1. Create an initial GRIB sample dictionary /// 2. Execute all sections for stages from the beginning of the plan /// through `StageOverride` /// 3. Clone between stages as required /// 4. Return the resulting object as an immutable prepared sample /// /// @param[in] mars /// MARS metadata dictionary /// /// @param[in] par /// Parameter metadata dictionary /// /// @param[in] opt /// Encoding options dictionary /// /// @return /// A newly allocated immutable dictionary containing the prepared /// reusable header state. /// /// @throws Mars2GribEncoderException /// On any failure during preparation. The exception includes: /// - serialized input dictionaries /// - serialized header layout /// - full nested exception chain /// /// @note /// This function is part of a temporary staged interface added in /// preparation for a cache that will soon be implemented. /// std::unique_ptr prepare(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) const { using metkit::mars2grib::backend::compile_time_registry_engine::StageOverride; using metkit::mars2grib::frontend::debug::debug_convert_GribHeaderLayoutData_to_json; using metkit::mars2grib::utils::dict_traits::clone_or_throw; using metkit::mars2grib::utils::dict_traits::dict_to_json; using metkit::mars2grib::utils::dict_traits::make_from_sample_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribEncoderException; try { auto samplePtr = make_from_sample_or_throw("GRIB2"); // Initialization of the sample for (const auto& section : plan_[0]) { for (const auto& conceptCallback : section) { if (conceptCallback) { conceptCallback(mars, par, opt, *samplePtr); } } } samplePtr = clone_or_throw(*samplePtr); // Encoding loop as a dense set of optimized operations for (std::size_t s = 0; s <= StageOverride; ++s) { for (const auto& section : plan_[s + 1]) { for (const auto& conceptCallback : section) { if (conceptCallback) { conceptCallback(mars, par, opt, *samplePtr); } } } samplePtr = clone_or_throw(*samplePtr); } return samplePtr; } catch (...) { std::throw_with_nested(Mars2GribEncoderException( "Critical failure in SpecializedEncoder execution", dict_to_json(mars), dict_to_json(par), dict_to_json(opt), debug_convert_GribHeaderLayoutData_to_json(layout_), Here())); } } /// /// @brief Execute the finalisation phase of the staged encoder. /// /// This method completes the encoding from a previously prepared sample. /// /// It clones the provided prepared sample, then executes only the runtime /// stage of the execution plan in order to produce a fresh finalized /// output object. /// /// This method exists to support a temporary staged-encoding workflow /// introduced in preparation for a cache mechanism that will soon be /// implemented in the surrounding encoding pipeline. /// /// Characteristics: /// /// - No layout resolution /// - No plan modification /// - No dynamic dispatch /// - Execution limited to the runtime stage of the plan /// /// The algorithm is: /// /// 1. Clone the provided prepared sample /// 2. Execute all sections in `StageRuntime` /// 3. Return the finalized output object /// /// @param[in] sample /// Immutable prepared sample previously produced by `prepare()` /// /// @param[in] mars /// MARS metadata dictionary /// /// @param[in] par /// Parameter metadata dictionary /// /// @param[in] opt /// Encoding options dictionary /// /// @return /// A newly allocated dictionary containing the finalized GRIB header /// /// @throws Mars2GribEncoderException /// On any failure during finalisation. The exception includes: /// - serialized input dictionaries /// - serialized header layout /// - full nested exception chain /// /// @note /// This function is part of a temporary staged interface added in /// preparation for a cache that will soon be implemented. /// std::unique_ptr finaliseEncoding(const OutDict_t& sample, const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) const { using metkit::mars2grib::backend::compile_time_registry_engine::StageRuntime; using metkit::mars2grib::frontend::debug::debug_convert_GribHeaderLayoutData_to_json; using metkit::mars2grib::utils::dict_traits::clone_or_throw; using metkit::mars2grib::utils::dict_traits::dict_to_json; using metkit::mars2grib::utils::exceptions::Mars2GribEncoderException; try { auto samplePtr = clone_or_throw(sample); // Encoding loop as a dense set of optimized operations for (const auto& section : plan_[StageRuntime + 1]) { for (const auto& conceptCallback : section) { if (conceptCallback) { conceptCallback(mars, par, opt, *samplePtr); } } } // @todo eventually need to return another clone to commit modifications return samplePtr; } catch (...) { std::throw_with_nested(Mars2GribEncoderException( "Critical failure in SpecializedEncoder execution", dict_to_json(mars), dict_to_json(par), dict_to_json(opt), debug_convert_GribHeaderLayoutData_to_json(layout_), Here())); } } private: /// /// @brief Internalized header layout. /// /// Stored as `const` to enforce immutability after construction. /// /// @note /// This member must be declared before `plan_` so that it is /// fully initialized before being used by the plan factory. /// const HeaderLayout_t layout_; /// /// @brief Optimized execution plan. /// /// Generated eagerly from `layout_` during construction. /// Never modified thereafter. /// const Plan_t plan_; }; } // namespace metkit::mars2grib::frontend::headermetkit-1.19.2/src/metkit/mars2grib/frontend/header/EncodingPlan.h0000664000175000017500000002170115232567717025042 0ustar alastairalastair/* * (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 EncodingPlan.h /// @brief Construction of the header encoding execution plan. /// /// This header defines the data structures and factory function used to /// build an **encoding plan** for GRIB header generation. /// /// An encoding plan is a fully resolved, runtime-ready representation of /// *what encoding callbacks must be executed*, organized by: /// /// - Encoding stage /// - GRIB section /// /// The plan is derived from a resolved header layout and from the /// compile-time encoding callback registry. /// /// @ingroup mars2grib_frontend_header /// #pragma once // System includes #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/EncodingCallbacksRegistry.h" #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/sections/initializers/sectionRegistry.h" #include "metkit/mars2grib/frontend/GribHeaderLayoutData.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::frontend::header::detail { /// /// @brief Fixed-capacity vector used to strictly avoid dynamic allocation. /// /// `FixedVector` provides a minimal vector-like interface backed by a /// statically allocated `std::array`. /// /// The primary motivation for this type is to **completely avoid dynamic /// allocation** in hot-path code that may be executed billions of times. /// In this context: /// /// - The maximum number of elements is small and known at compile time /// (typically <= 22 elements) /// - Allocating tiny dynamic vectors would result in unacceptable /// allocation overhead and memory fragmentation /// /// This abstraction also allows future replacement with a small-buffer /// optimized container (e.g. `boost::container::small_vector`) without /// changing the surrounding code. /// /// @tparam T Element type /// @tparam Capacity Maximum number of elements /// template struct FixedVector { /// Underlying fixed storage std::array data; /// Number of valid elements in @ref data std::size_t current_size = 0; /// /// @brief Append an element to the vector. /// /// @throws Mars2GribGenericException /// If the fixed capacity is exceeded /// void push_back(const T& value) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; if (current_size >= Capacity) { throw Mars2GribGenericException("FixedVector capacity exceeded", Here()); } data[current_size++] = value; } /// Remove all elements void clear() { current_size = 0; } /// Return the number of stored elements std::size_t size() const { return current_size; } /// Begin iterator (non-const) T* begin() { return data.data(); } /// End iterator (non-const) T* end() { return data.data() + current_size; } /// Begin iterator (const) const T* begin() const { return data.data(); } /// End iterator (const) const T* end() const { return data.data() + current_size; } /// Element access (non-const) T& operator[](std::size_t i) { return data[i]; } /// Element access (const) const T& operator[](std::size_t i) const { return data[i]; } }; /// /// @brief Global compile-time registry of indexing and layout metadata. /// /// `GeneralRegistry` provides **compile-time constants and indexing /// infrastructure** shared across the entire mars2grib codebase. /// /// It defines: /// - The total number of concepts, variants, sections, and stages /// - Stable indices used to access registry-backed tables /// /// Callback registries are intentionally separated from `GeneralRegistry`: /// - They are heavily templated on dictionary types /// - In many contexts only index metadata is required /// - Templating on unused dictionary parameters would be unnecessary /// and harmful to compile-time and readability /// /// This separation keeps indexing lightweight while allowing callback /// registries to remain fully type-safe where required. /// using GeneralRegistry = metkit::mars2grib::backend::concepts_::GeneralRegistry; /// /// @brief Encoding callbacks registry alias. /// /// This alias refers to the compile-time registry that maps /// `(variant, section, stage)` to concrete encoding callbacks. /// /// The underscore namespace is used to avoid clashes with the C++20 /// `concepts` keyword. /// template using EncodingRegistry = metkit::mars2grib::backend::concepts_::EncodingCallbacksRegistry; /// /// @brief Alias for the encoding callback function type. /// /// Extracted from the encoding callbacks registry. /// template using Fn_t = typename EncodingRegistry::Fn_t; /// /// @brief Encoding execution plan. /// /// An `EncodingPlan` is a two-dimensional grid indexed by: /// /// - Encoding stage /// - GRIB section /// /// Each cell contains a fixed-capacity list of encoding callbacks /// that must be executed for that `(stage, section)` pair. /// /// Layout: /// /// @code /// EncodingPlan[Stage][Section] -> list of callbacks /// @endcode /// /// Stage `0` is reserved for **section initializers**. /// Stages `1..N` contain concept encoding callbacks. /// template using EncodingPlan = std::array, GeneralRegistry::NConcepts>, GeneralRegistry::NSections>, GeneralRegistry::NStages + 1>; /// /// @brief Build an encoding plan from resolved header layout data. /// /// This factory function constructs a complete `EncodingPlan` by: /// /// 1. Selecting section initializer callbacks (stage 0) /// 2. Selecting concept encoding callbacks for each section and stage /// /// The plan is fully determined by: /// - The resolved header layout (template numbers and variants) /// - The compile-time encoding callbacks registry /// /// @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 /// /// @param[in] headerLayout Resolved header layout data /// /// @return Fully populated encoding plan /// /// @throws Mars2GribFrontendException /// If plan construction fails /// template EncodingPlan make_EncodingPlan_or_throw( const GribHeaderLayoutData& headerLayout) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { // Access the static callback registry const auto& callbacks = EncodingRegistry::encodingCallbacks; EncodingPlan table; // Stage 0: Populate section initializers (always one initializer per section) for (std::size_t sid = 0; sid < GeneralRegistry::NSections; ++sid) { std::size_t templ = headerLayout.sectionLayouts[sid].templateNumber; table[0][sid].push_back( metkit::mars2grib::backend::sections::initializers::sectionRegistry(sid, templ)); } // Stages 1 to N: Populate encoding callbacks for (std::size_t pid = 0; pid < GeneralRegistry::NStages; ++pid) { for (std::size_t sid = 0; sid < GeneralRegistry::NSections; ++sid) { const auto& section = headerLayout.sectionLayouts[sid]; // Index pid+1 because stage 0 is reserved for initializers auto& cell = table[pid + 1][sid]; cell.clear(); for (std::size_t cid = 0; cid < section.count; ++cid) { std::size_t vid = section.variantIndices[cid]; const auto& f = callbacks[vid][pid][sid]; if (f) { cell.push_back(f); } } } } return table; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Unable to create encoding plan", Here())); } } } // namespace metkit::mars2grib::frontend::header::detailmetkit-1.19.2/src/metkit/mars2grib/backend/0000775000175000017500000000000015232567717020647 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/encodeValues.h0000664000175000017500000002310615232567717023437 0ustar alastairalastair/* * (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 encodeValues.h /// @brief Low-level utility for GRIB payload encoding via non-owning memory spans. /// /// This header defines `encodeValues`, a **terminal encoding operation** that /// bridges raw numeric data to the physical GRIB message representation. /// /// By utilizing `metkit::codes::Span`, this utility achieves **zero-copy /// data passing** from the caller to the encoding engine. A temporary copy /// is only performed if a type conversion (e.g., `float` to `double`) is /// strictly required by the underlying ecCodes API. /// /// The logic is designed to trigger the internal ecCodes encoding machinery, /// which performs: /// - Bitmap construction from missing values /// - Value packing and compression (e.g., CCSDS, Simple Packing) /// - Message size resolution /// /// @ingroup mars2grib_backend /// #pragma once // System includes #include #include #include #include #include // Project includes #include "metkit/codes/api/CodesTypes.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend { template using Span = metkit::codes::Span; /// /// @brief Inject numeric field values and resolve data-section bitmasking. /// /// A `Values` encoding operation represents the **physical realization of /// the GRIB Data Section**. /// /// Unlike metadata encoding, which is combinatorial, value encoding is /// **procedural and performance-critical**. The use of `Span` ensures /// that the payload is passed by reference, avoiding unnecessary allocations /// on the hot path. /// /// The role of this function is to: /// - Configure the GRIB handle's **Data Representation** state /// - Bind the numeric payload to the `values` key /// - Handle **Precision Casting** only when native support is unavailable /// /// ------------------------------------------------------------------------ /// /// @section internal_behavior Internal ecCodes Behavior /// /// Upon setting the `values` key, the underlying ecCodes engine performs: /// - **Missing Value Scanning**: If `bitmapPresent` is enabled, the input /// buffer is scanned, and a bitmask is constructed using the specified /// `missingValue`. /// - **Packing Execution**: The data is compressed according to the /// `packingType` resolved during the header encoding phase. /// /// ------------------------------------------------------------------------ /// /// @tparam Val_t Numeric precision of input (must be `float` or `double`) /// @tparam MiscDict_t Type of the dictionary containing auxiliary metadata /// @tparam OptDict_t Type of the dictionary containing auxiliary metadata /// /// @param[in] values Non-owning span of numeric values (Payload) /// @param[in] misc Dictionary containing bitmap and missing value keys /// @param[in] opt Dictionary containing options /// @param[in,out] handle The GRIB handle to be updated /// /// @return The updated GRIB handle containing the encoded payload /// template void encodeValues(Span values, const MiscDict_t& misc, const OptDict_t& opt, OutDict_t& handle) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { static_assert(std::is_floating_point_v, "encodeValues: Val_t must be float or double"); // 1. Configure Bitmap and Metadata State const bool bitmapPresent = get_opt(misc, "bitmapPresent").value_or(false); set_or_throw(handle, "bitmapPresent", bitmapPresent); if (bitmapPresent) { // Resolve missing value sentinel cast to double for ecCodes compatibility const double missingValue = get_opt(misc, "missingValue").value_or(static_cast(std::numeric_limits::max())); set_or_throw(handle, "missingValue", missingValue); } // 2. Physical Value Injection // Current ecCodes implementation requires double-precision for the 'values' key. // If input is float, we perform an explicit deep-copy cast to double. if constexpr (std::is_same_v) { set_or_throw(handle, "values", values); } else { // Deep copy-cast from float to double for legacy API support std::vector dValues; dValues.reserve(values.size()); const Val_t* raw = values.data(); for (std::size_t i = 0; i < values.size(); ++i) { dValues.push_back(static_cast(raw[i])); } set_or_throw(handle, "values", Span{dValues}); } } catch (...) { std::throw_with_nested(Mars2GribGenericException("Critical failure in SpecializedEncoder execution", Here())); } } /// /// @brief Configure section3, inject numeric field values and resolve data-section bitmasking. /// /// A `Values` encoding operation represents the **physical realization of /// the GRIB Data Section**. /// /// Unlike metadata encoding, which is combinatorial, value encoding is /// **procedural and performance-critical**. The use of `Span` ensures /// that the payload is passed by reference, avoiding unnecessary allocations /// on the hot path. /// /// The role of this function is to: /// - Configure the GRIB handle's **Data Representation** state /// - Bind the numeric payload to the `values` key /// - Handle **Precision Casting** only when native support is unavailable /// /// ------------------------------------------------------------------------ /// /// @section internal_behavior Internal ecCodes Behavior /// /// Upon setting the `values` key, the underlying ecCodes engine performs: /// - **Missing Value Scanning**: If `bitmapPresent` is enabled, the input /// buffer is scanned, and a bitmask is constructed using the specified /// `missingValue`. /// - **Packing Execution**: The data is compressed according to the /// `packingType` resolved during the header encoding phase. /// /// ------------------------------------------------------------------------ /// /// @tparam Val_t Numeric precision of input (must be `float` or `double`) /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam MiscDict_t Type of the dictionary containing auxiliary metadata /// @tparam OptDict_t Type of the dictionary containing auxiliary metadata /// /// @param[in] values Non-owning span of numeric values (Payload) /// @param[in] mars Read-only MARS dictionary /// @param[in] misc Dictionary containing bitmap and missing value keys /// @param[in] opt Dictionary containing options /// @param[in,out] handle The GRIB handle to be updated /// /// @return The updated GRIB handle containing the encoded payload /// template void encodeValuesGridSpec(Span values, const MarsDict_t& mars, const MiscDict_t& misc, const OptDict_t& opt, OutDict_t& handle) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { // Recover the value of the gridSpec from Mars dictionary auto GridSpec = get_or_throw(mars, "grid"); set_or_throw(handle, "gridSpec", GridSpec); static_assert(std::is_floating_point_v, "encodeValues: Val_t must be float or double"); // 1. Configure Bitmap and Metadata State const bool bitmapPresent = get_opt(misc, "bitmapPresent").value_or(false); set_or_throw(handle, "bitmapPresent", bitmapPresent); if (bitmapPresent) { // Resolve missing value sentinel cast to double for ecCodes compatibility const double missingValue = get_opt(misc, "missingValue").value_or(static_cast(std::numeric_limits::max())); set_or_throw(handle, "missingValue", missingValue); } // 2. Physical Value Injection // Current ecCodes implementation requires double-precision for the 'values' key. // If input is float, we perform an explicit deep-copy cast to double. if constexpr (std::is_same_v) { set_or_throw(handle, "values", values); } else { // Deep copy-cast from float to double for legacy API support std::vector dValues; dValues.reserve(values.size()); const Val_t* raw = values.data(); for (std::size_t i = 0; i < values.size(); ++i) { dValues.push_back(static_cast(raw[i])); } set_or_throw(handle, "values", Span{dValues}); } } catch (...) { std::throw_with_nested(Mars2GribGenericException("Critical failure in SpecializedEncoder execution", Here())); } } } // namespace metkit::mars2grib::backendmetkit-1.19.2/src/metkit/mars2grib/backend/checks/0000775000175000017500000000000015232567717022107 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/checks/checkDestinELocalSection.h0000664000175000017500000001051615232567717027114 0ustar alastairalastair/* * (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. */ #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Validate the DestinE Local Use Section against production status rules. /// /// This function verifies that, when a GRIB *Local Use Section* is present, /// its content is compatible with the expected DestinE conventions. /// /// The validation is performed **only if** the option `applyChecks` is present /// in the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function: /// - checks that the Local Use Section is present (`LocalUsePresent != 0`); /// - reads the key `productionStatusOfProcessedData` from the output dictionary; /// - throws an exception if the production status is different from the only /// allowed DestinE value (`12`); /// /// If the Local Use Section is expected but not present, an exception is raised. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary expected to contain the keys /// `LocalUsePresent` and `productionStatusOfProcessedData` /// when checks are enabled /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - checks are enabled and the Local Use Section is not present /// - the production status is not compatible with DestinE rules /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void check_DestinELocalSection_or_throw(const OptDict_t& opt, const OutDict_t& out) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { // If Local Use Section is present, validate production status if (long hasLocalUseSection = get_or_throw(out, "localUsePresent"); hasLocalUseSection != 0) { long actualProductionStatusOfProcessedData = get_or_throw(out, "productionStatusOfProcessedData"); // Throw if no match constexpr long kDestinEProductionStatus = 12; if (actualProductionStatusOfProcessedData != kDestinEProductionStatus) { std::string errMsg = "Invalid DestinE Local Use Section (wrong productionStatusOfProcessedData): "; errMsg += "actual=" + std::to_string(actualProductionStatusOfProcessedData); errMsg += ", expected=" + std::to_string(kDestinEProductionStatus); throw Mars2GribValidationException(errMsg, Here()); } } else { throw Mars2GribValidationException("DestinE Local Use Section not allocated in the sample", Here()); } // Useful for debugging MARS2GRIB_LOG_CHECK("Validated DestinE Local Use Section"); } // Exit on success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribValidationException("Unable to validate DestinE Local Use Section", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/checkLocalUseSection.h0000664000175000017500000000655115232567717026321 0ustar alastairalastair/* * (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. */ #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Check that the GRIB message contains a Local Use Section. /// /// This function verifies the presence of the GRIB *Local Use Section* based on /// the runtime configuration provided in the options dictionary. /// /// The check is performed **only if** the option `applyChecks` is present in /// the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function reads the key `LocalUsePresent` from the output /// dictionary (`out`): /// - a value of `0` indicates that the Local Use Section is missing and results /// in an exception; /// - a non-zero value indicates that the section is present and the check succeeds. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary; must contain the key `LocalUsePresent` /// when checks are enabled /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - `applyChecks` is `true` and `LocalUsePresent == 0` /// - required keys are missing /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void check_LocalUseSection_or_throw(const OptDict_t& opt, const OutDict_t& out) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { long localUsePresent = get_or_throw(out, "localUsePresent"); if (localUsePresent == 0) { throw Mars2GribValidationException("Local Use Section not present in the sample", Here()); } // Useful for debugging MARS2GRIB_LOG_CHECK("Local Use Section is present in the sample"); } // Exit point with success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribValidationException("Unable to validate presence of Local Use Section", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/matchProductDefinitionTemplateNumber.h0000664000175000017500000001107615232567717031600 0ustar alastairalastair/* * (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. */ #pragma once #include #include #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Validate the Product Definition Template Number against a set of expected values. /// /// This function verifies that the GRIB *Product Definition Template Number* /// matches one of the expected template numbers provided by the caller. /// /// The validation is performed **only if** the option `applyChecks` is present /// in the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function reads the key `productDefinitionTemplateNumber` /// from the output dictionary (`out`) and compares it against the list of /// expected values supplied in `expectedProductDefinitionTemplateNumber`. /// /// If the actual template number does not match any of the expected values, /// an exception is thrown. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary expected to contain the key /// `productDefinitionTemplateNumber` when checks are enabled /// @param[in] expectedProductDefinitionTemplateNumbers /// List of acceptable Product Definition Template Numbers /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - `applyChecks` is `true` and the template number does not match /// any of the expected values /// - required keys are missing /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void match_ProductDefinitionTemplateNumber_or_throw(const OptDict_t& opt, const OutDict_t& out, const std::vector& expectedProductDefinitionTemplateNumbers) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::joinNumbers; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { // Get the productDefinitionTemplateNumber long actualProductDefinitionTemplateNumber = get_or_throw(out, "productDefinitionTemplateNumber"); // Compare against expected values const bool match = std::find(expectedProductDefinitionTemplateNumbers.begin(), expectedProductDefinitionTemplateNumbers.end(), actualProductDefinitionTemplateNumber) != expectedProductDefinitionTemplateNumbers.end(); // Throw if no match if (!match) { std::string errMsg = "Product Definition Template Number does not match any of the expected values: "; errMsg += "actual=" + std::to_string(actualProductDefinitionTemplateNumber); errMsg += ", expected=" + joinNumbers(expectedProductDefinitionTemplateNumbers); throw Mars2GribValidationException(errMsg, Here()); } // Useful for debugging MARS2GRIB_LOG_MATCH("Product Definition Template Number matches expected values"); } // Exit on success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribValidationException("Unable to validate Product Definition Template Number", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/matchLocalDefinitionNumber.h0000664000175000017500000001147615232567717027522 0ustar alastairalastair/* * (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. */ #pragma once #include #include #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Validate the Local Definition Number in the Local Use Section. /// /// This function verifies that, when a GRIB *Local Use Section* is present, /// its `localDefinitionNumber` matches one of the expected values provided /// by the caller. /// /// The validation is performed **only if** the option `applyChecks` is present /// in the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function: /// - checks that the Local Use Section is present (`LocalUsePresent != 0`); /// - reads the key `localDefinitionNumber` from the output dictionary (`out`); /// - compares it against the list of expected local definition numbers. /// /// If the Local Use Section is missing, or if the local definition number does /// not match any of the expected values, an exception is thrown. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary expected to contain the keys /// `LocalUsePresent` and `localDefinitionNumber` /// when checks are enabled /// @param[in] expectedLocalDefinitionNumber /// List of acceptable Local Definition Numbers /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - checks are enabled and the Local Use Section is not present /// - the local definition number does not match any expected value /// - required keys are missing /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void match_LocalDefinitionNumber_or_throw(const OptDict_t& opt, const OutDict_t& out, const std::vector& expectedLocalDefinitionNumber) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::joinNumbers; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { // If Local Use Section is present, check definition number if (long hasLocalUseSection = get_or_throw(out, "localUsePresent"); hasLocalUseSection != 0) { long actualLocalDefinitionNumber = get_or_throw(out, "localDefinitionNumber"); // Compare against expected values bool match = std::find(expectedLocalDefinitionNumber.begin(), expectedLocalDefinitionNumber.end(), actualLocalDefinitionNumber) != expectedLocalDefinitionNumber.end(); // Throw if no match if (!match) { std::string errMsg = "Local Definition Number mismatch in Local Use Section: "; errMsg += "actual=" + std::to_string(actualLocalDefinitionNumber); errMsg += ", expected=" + joinNumbers(expectedLocalDefinitionNumber); throw Mars2GribValidationException(errMsg, Here()); } } else { throw Mars2GribValidationException("Local Use Section not present in the sample", Here()); } // Useful for debugging MARS2GRIB_LOG_MATCH("Local Definition Number matches expected values"); } // Exit on success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribValidationException("Unable to validate Local Definition Number in Local Use Section", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/checkDerivedProductDefinitionSection.h0000664000175000017500000000741015232567717031541 0ustar alastairalastair/* * (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. */ #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Check that the GRIB Product Definition Section corresponds to an Derived forecast, /// when validation is enabled. /// /// This function verifies that the GRIB *Product Definition Section* indicates an /// Derived forecast based on the runtime configuration provided in the options dictionary. /// /// The check is performed **only if** the option `applyChecks` is present in /// the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function checks for the presence of the following keys /// in the output dictionary (`out`): /// - `derivedForecast` /// - `numberOfForecastsInEnsemble` /// /// If any of these keys are missing, an exception is raised indicating that /// the Product Definition Section is not of Derived type. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary; must contain the keys required for Derived check /// when checks are enabled /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - `applyChecks` is `true` and any of the required keys are missing /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void check_DerivedProductDefinitionSection_or_throw(const OptDict_t& opt, const OutDict_t& out) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { bool hasDerivedForecast = has(out, "derivedForecast"); bool hasNumberOfForecastsInEnsemble = has(out, "numberOfForecastsInEnsemble"); // Derived forecast needs to have all 2 fields defined in the Product Definition Section if (!(hasDerivedForecast && hasNumberOfForecastsInEnsemble)) { throw Mars2GribValidationException("Product Definition Section does not represent a Derived forecast", Here()); } // Useful for debugging MARS2GRIB_LOG_CHECK("Validated Derived Product Definition Section"); } // Exit point with success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribValidationException("Unable to validate Derived Product Definition Section", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/checkStatisticsProductDefinitionSection.h0000664000175000017500000000736215232567717032317 0ustar alastairalastair/* * (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. */ #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Verify that the Product Definition Section corresponds to a statistics product. /// /// This function checks whether the GRIB Product Definition Section (PDS) /// represents a *statistics product*. /// /// The validation is performed **only if** the option `applyChecks` is present /// in the options dictionary (`opt`) and evaluates to `true`. /// If the option is not present, no validation is performed. /// /// When enabled, the function verifies that the key `numberOfTimeRanges` /// is present in the output dictionary (`out`). /// /// The absence of this field indicates that the Product Definition Section /// does not describe a statistics product and results in an exception. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary expected to contain the key `numberOfTimeRanges` /// when checks are enabled /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - checks are enabled and `numberOfTimeRanges` is missing /// - any error occurs while accessing the dictionaries /// /// @todo Extend the checks to other relevant keys, e.g. `typeOfStatisticsProcessing` /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void check_StatisticsProductDefinitionSection_or_throw(const OptDict_t& opt, const OutDict_t& out) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { bool hasNumberOfTimeRanges = has(out, "numberOfTimeRanges"); bool hasTypeOfStatisticalProcessing = has(out, "typeOfStatisticalProcessing"); // Statistics product needs to have numberOfTimeRanges defined in the Product Definition Section if (!hasNumberOfTimeRanges || !hasTypeOfStatisticalProcessing) { throw Mars2GribValidationException("Product Definition Section is not of Statistics type", Here()); } // Useful for debugging MARS2GRIB_LOG_CHECK("Product Definition Section is of Statistics type"); } // Exit point with success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribValidationException("Unable to validate Product Definition Section as Statistics type", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/matchDataset.h0000664000175000017500000001025215232567717024662 0ustar alastairalastair/* * (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. */ #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Validate that the dataset identifier matches an expected value. /// /// This function checks whether the GRIB output dictionary contains a `dataset` /// entry matching the expected dataset identifier provided by the caller. /// /// The validation is performed **only if** the option `applyChecks` is present /// in the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function reads the key `dataset` from the output dictionary /// (`out`) and compares it against `expectedDataset`. /// A mismatch results in an exception. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary expected to contain the key `dataset` /// when checks are enabled /// @param[in] expectedDatasetString Expected dataset identifier /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - `applyChecks` is `true` and the dataset does not match the expected value /// - required keys are missing /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void match_Dataset_or_throw(const OptDict_t& opt, const OutDict_t& out, const std::string& expectedDatasetString) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { // Convert the expected 'dataset' from string to integer long expectedDataset; if (expectedDatasetString == "climate-dt") { expectedDataset = 1; } else if (expectedDatasetString == "extremes-dt") { expectedDataset = 2; } else { throw Mars2GribValidationException( "Expected dataset '" + expectedDatasetString + "' cannot be mapped to integer representation", Here()); } // Get the `dataset` entry (expected in DestinE local use sections) const auto actualDataset = get_or_throw(out, "dataset"); // Compare against expected values if (actualDataset != expectedDataset) { throw Mars2GribValidationException( "Dataset does not match the expected value: actual=" + std::to_string(actualDataset) + ", expected=" + std::to_string(expectedDataset), Here()); } // Useful for debugging MARS2GRIB_LOG_MATCH("Dataset matches expected value"); } // Exit on success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribValidationException("Unable to validate dataset from the sample", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/matchDataRepresentationTemplateNumber.h0000664000175000017500000001103615232567717031737 0ustar alastairalastair/* * (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. */ #pragma once #include #include #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Validate the Data Representation Template Number against a set of expected values. /// /// This function verifies that the GRIB *Data Representation Template Number* /// matches one of the expected template numbers provided by the caller. /// /// The validation is performed **only if** the option `applyChecks` is present /// in the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function reads the key `dataRepresentationTemplateNumber` /// from the output dictionary (`out`) and compares it against the list of /// expected values supplied in `expectedDataRepresentationTemplateNumber`. /// /// If the actual template number does not match any of the expected values, /// an exception is thrown. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary expected to contain the key /// `dataRepresentationTemplateNumber` when checks are enabled /// @param[in] expectedDataRepresentationTemplateNumber /// List of acceptable Data Representation Template Numbers /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - `applyChecks` is `true` and the actual template number does not match /// any of the expected values /// - required keys are missing /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void match_DataRepresentationTemplateNumber_or_throw( const OptDict_t& opt, const OutDict_t& out, const std::vector& expectedDataRepresentationTemplateNumber) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::joinNumbers; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { // Get the dataRepresentationTemplateNumber long actualDataRepresentationTemplateNumber = get_or_throw(out, "dataRepresentationTemplateNumber"); // Compare against expected values bool match = std::find(expectedDataRepresentationTemplateNumber.begin(), expectedDataRepresentationTemplateNumber.end(), actualDataRepresentationTemplateNumber) != expectedDataRepresentationTemplateNumber.end(); // Throw if no match if (!match) { std::string errMsg = "Data Representation Template Number does not match any of the expected values: "; errMsg += "actual=" + std::to_string(actualDataRepresentationTemplateNumber); errMsg += ", expected=" + joinNumbers(expectedDataRepresentationTemplateNumber); throw Mars2GribValidationException(errMsg, Here()); } // Useful for debugging MARS2GRIB_LOG_MATCH("Data Representation Template Number matches expected values"); } // Exit on success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribValidationException("Unable to validate Data Representation Template Number", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/checkEnsembleProductDefinitionSection.h0000664000175000017500000000765515232567717031724 0ustar alastairalastair/* * (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. */ #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Check that the GRIB Product Definition Section corresponds to an Ensemble forecast, /// when validation is enabled. /// /// This function verifies that the GRIB *Product Definition Section* indicates an /// Ensemble forecast based on the runtime configuration provided in the options dictionary. /// /// The check is performed **only if** the option `applyChecks` is present in /// the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function checks for the presence of the following keys /// in the output dictionary (`out`): /// - `typeOfEnsembleForecast` /// - `perturbationNumber` /// - `numberOfForecastsInEnsemble` /// /// If any of these keys are missing, an exception is raised indicating that /// the Product Definition Section is not of Ensemble type. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary; must contain the keys required for Ensemble check /// when checks are enabled /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - `applyChecks` is `true` and any of the required keys are missing /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void check_EnsembleProductDefinitionSection_or_throw(const OptDict_t& opt, const OutDict_t& out) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { bool hasTypeOfEnsembleForecast = has(out, "typeOfEnsembleForecast"); bool hasPerturbationNumber = has(out, "perturbationNumber"); bool hasNumberOfForecastsInEnsemble = has(out, "numberOfForecastsInEnsemble"); // Ensemble forecast needs to have all 3 fields defined in the Product Definition Section if (!(hasTypeOfEnsembleForecast && hasPerturbationNumber && hasNumberOfForecastsInEnsemble)) { throw Mars2GribValidationException("Product Definition Section does not represent an Ensemble forecast", Here()); } // Useful for debugging MARS2GRIB_LOG_CHECK("Validated Ensemble Product Definition Section"); } // Exit point with success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribValidationException("Unable to validate Ensemble Product Definition Section", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/checks/matchGridDefinitionTemplateNumber.h0000664000175000017500000001073315232567717031044 0ustar alastairalastair/* * (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. */ #pragma once #include #include #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::validation { /// /// @brief Validate the Grid Definition Template Number against a set of expected values. /// /// This function verifies that the GRIB *Grid Definition Template Number* /// matches one of the expected template numbers provided by the caller. /// /// The validation is performed **only if** the option `applyChecks` is present /// in the options dictionary (`opt`) and evaluates to `true`. /// /// When enabled, the function reads the key `gridDefinitionTemplateNumber` /// from the output dictionary (`out`) and compares it against the list of /// expected values supplied in `expectedGridDefinitionTemplateNumber`. /// /// If the actual template number does not match any of the expected values, /// an exception is thrown. /// /// Any failure occurring during dictionary access or validation is caught and /// rethrown as a nested `Mars2GribValidationException` with additional context. /// /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// /// @param[in] opt Options dictionary; may contain the boolean key `applyChecks` /// @param[in] out Output dictionary expected to contain the key /// `gridDefinitionTemplateNumber` when checks are enabled /// @param[in] expectedGridDefinitionTemplateNumber /// List of acceptable Grid Definition Template Numbers /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribValidationException /// If: /// - `applyChecks` is `true` and the template number does not match /// any of the expected values /// - required keys are missing /// - any error occurs while accessing the dictionaries /// /// @note /// - If `applyChecks` is absent or evaluates to `false`, no validation is performed. /// - The function returns normally on success and does not produce any output. /// template void match_GridDefinitionTemplateNumber_or_throw(const OptDict_t& opt, const OutDict_t& out, const std::vector& expectedGridDefinitionTemplateNumber) { using metkit::mars2grib::utils::checksEnabled; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::joinNumbers; using metkit::mars2grib::utils::exceptions::Mars2GribValidationException; try { if (checksEnabled(opt)) { // Get the gridDefinitionTemplateNumber long actualGridDefinitionTemplateNumber = get_or_throw(out, "gridDefinitionTemplateNumber"); // Compare against expected values const bool match = std::find(expectedGridDefinitionTemplateNumber.begin(), expectedGridDefinitionTemplateNumber.end(), actualGridDefinitionTemplateNumber) != expectedGridDefinitionTemplateNumber.end(); // Throw if no match if (!match) { std::string errMsg = "Grid Definition Template Number does not match any of the expected values: "; errMsg += "actual=" + std::to_string(actualGridDefinitionTemplateNumber); errMsg += ", expected=" + joinNumbers(expectedGridDefinitionTemplateNumber); throw Mars2GribValidationException(errMsg, Here()); } // Useful for debugging MARS2GRIB_LOG_MATCH("Grid Definition Template Number matches expected values"); } // Exit on success return; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribValidationException("Unable to validate Grid Definition Template Number", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::validation metkit-1.19.2/src/metkit/mars2grib/backend/concepts/0000775000175000017500000000000015232567717022465 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/nil/0000775000175000017500000000000015232567717023247 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/nil/nilEncoding.h0000664000175000017500000001055515232567717025657 0ustar alastairalastair/* * (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 nilEncoding.h /// @brief Implementation of the GRIB `nil` concept operation. /// /// This header defines the **nil concept**, a sentinel / placeholder concept /// used within the mars2grib backend. /// /// The nil concept: /// - Has no semantic meaning at the GRIB level /// - Must never be applicable /// - Must never modify the output GRIB dictionary /// /// Its primary purposes are: /// - Acting as a compile-time placeholder in concept tables /// - Providing a well-defined failure mode if accidentally invoked /// - Making concept dispatch logic total (no missing concept slots) /// /// @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/nil/nilEnum.h" #include "metkit/mars2grib/utils/generalUtils.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 `nil` concept. /// /// The nil concept is **never applicable**. /// /// This predicate always evaluates to `false` and exists only to satisfy /// the uniform concept interface expected by the concept dispatcher. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Nil concept variant /// /// @return Always `false`. /// template constexpr bool nilApplicable() { return false; } /// /// @brief Execute the `nil` concept operation. /// /// This function must never perform any operation. /// /// If invoked when not applicable (which is always the case), /// a `Mars2GribConceptException` is thrown with full contextual /// information. /// /// The existence of this function ensures that: /// - concept dispatch tables are complete, /// - accidental invocation is detected early and explicitly, /// - no silent no-op behaviour is possible. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Nil 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 (unused) /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// @param[out] out Output GRIB dictionary (unused) /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribConceptException /// Always thrown if this function is invoked. /// /// @note /// This function intentionally does not provide a silent no-op. /// Any invocation is treated as a programming error. /// template void NilOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) noexcept(false) { using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (nilApplicable()) { // Debug output MARS2GRIB_LOG_CONCEPT(nil); // Successful no-op return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(nil, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/nil/nilMatcher.h0000664000175000017500000000430115232567717025504 0ustar alastairalastair/* * (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 nilMatcher.h /// @brief Entry-level matcher for the GRIB `nil` concept. /// /// This header defines the runtime matcher used by the concept registry to /// activate the default nil concept variant. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/nil/nilEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `nil` concept variant. /// /// The nil concept is always active and resolves to `NilType::Default`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `NilType::Default`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t nilMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { return static_cast(NilType::Default); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException("Unable to match `nil` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/nil/nilEnum.h0000664000175000017500000001026415232567717025032 0ustar alastairalastair/* * (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 nilEnum.h /// @brief Definition of the `nil` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `nil` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`nilName`) /// - the enumeration of supported nil variants (`NilType`) /// - a compile-time typelist of all variants (`NilList`) /// - 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 /// `nilEncoding.h` 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 `nil` 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 `nil` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view nilName{"nil"}; /// /// @brief Enumeration of all supported `nil` concept variants. /// /// Each enumerator represents a placeholder or empty semantic concept /// used to explicitly denote the absence of a concrete concept value. /// /// 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 and typically used as /// a sentinel or default concept in the encoder pipeline. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class NilType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `nil` 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 NilList = ValueList; /// /// @brief Compile-time mapping from `NilType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given nil variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Nil 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 nilTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view nilTypeName() { \ return NAME; \ } DEF(NilType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/nil/nilConceptDescriptor.h0000664000175000017500000001256015232567717027561 0ustar alastairalastair/* * (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 nilConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `nil` concept. /// /// This header defines `NilConcept`, the **compile-time descriptor** /// that registers the GRIB `nil` 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/nil/nilEncoding.h" #include "metkit/mars2grib/backend/concepts/nil/nilEnum.h" #include "metkit/mars2grib/backend/concepts/nil/nilMatcher.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 `nil` concept. /// /// `NilConcept` registers the GRIB `nil` 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 NilConcept : 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 nilName; } /// /// @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 nilTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `nil` 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 (nilApplicable()) { return &NilOp; } 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 &nilMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-period/0000775000175000017500000000000015232567717025703 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-period/referencePeriodEncoding.h0000664000175000017500000001147115232567717032630 0ustar alastairalastair/* * (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 referencePeriodEncoding.h /// @brief Implementation of the GRIB `referencePeriod` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **referencePeriod concept** within the mars2grib backend. /// /// The current implementation is an inactive skeleton that provides the /// standard compile-time and runtime hooks required by the concept registry, /// without enabling any encoding behavior yet. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `referencePeriodApplicable` /// - Runtime execution via `ReferencePeriodOp` /// - 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/reference-period/referencePeriodEnum.h" #include "metkit/mars2grib/utils/generalUtils.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 `referencePeriod` concept. /// /// This predicate determines whether the `referencePeriod` 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 Reference-period concept variant /// /// @return `true` if the concept is applicable for the given parameters, /// `false` otherwise. /// /// @note /// The current skeleton implementation always returns `false`. /// template constexpr bool referencePeriodApplicable() { return false; } /// /// @brief Execute the `referencePeriod` concept operation. /// /// This function provides the standard runtime hook for the /// `referencePeriod` concept. /// /// 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 Reference-period 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 concept is invoked when not applicable. /// /// @see referencePeriodApplicable /// template void ReferencePeriodOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { static_cast(mars); static_cast(par); static_cast(opt); static_cast(out); using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (referencePeriodApplicable()) { try { MARS2GRIB_LOG_CONCEPT(referencePeriod); } catch (...) { MARS2GRIB_CONCEPT_RETHROW(referencePeriod, "Unable to set `referencePeriod` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(referencePeriod, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-period/referencePeriodEnum.h0000664000175000017500000001076715232567717032015 0ustar alastairalastair/* * (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 referencePeriodEnum.h /// @brief Definition of the `referencePeriod` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `referencePeriod` /// concept used by the mars2grib backend. It contains: /// /// - the canonical concept name (`referencePeriodName`) /// - the enumeration of supported reference-period variants (`ReferencePeriodType`) /// - a compile-time typelist of all variants (`ReferencePeriodList`) /// - 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 /// `referencePeriodEncoding.h` 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 `referencePeriod` 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 `referencePeriod` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view referencePeriodName{"referencePeriod"}; /// /// @brief Enumeration of all supported `referencePeriod` concept variants. /// /// Each enumerator represents a specific reference-period 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 reference-period concept evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class ReferencePeriodType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `referencePeriod` 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 ReferencePeriodList = ValueList; /// /// @brief Compile-time mapping from `ReferencePeriodType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given reference-period variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Reference-period 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 referencePeriodTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view referencePeriodTypeName() { \ return NAME; \ } DEF(ReferencePeriodType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-period/referencePeriodConceptDescriptor.hmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-period/referencePeriodConceptDescripto0000664000175000017500000001323715232567717034126 0ustar alastairalastair/* * (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 referencePeriodConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `referencePeriod` concept. /// /// This header defines `ReferencePeriodConcept`, the **compile-time descriptor** /// that registers the GRIB `referencePeriod` 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/reference-period/referencePeriodEncoding.h" #include "metkit/mars2grib/backend/concepts/reference-period/referencePeriodEnum.h" #include "metkit/mars2grib/backend/concepts/reference-period/referencePeriodMatcher.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 `referencePeriod` concept. /// /// `ReferencePeriodConcept` registers the GRIB `referencePeriod` 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 ReferencePeriodConcept : 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 referencePeriodName; } /// /// @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 referencePeriodTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `referencePeriod` 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 (referencePeriodApplicable()) { return &ReferencePeriodOp; } 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 &referencePeriodMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-period/referencePeriodMatcher.h0000664000175000017500000000526515232567717032471 0ustar alastairalastair/* * (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 referencePeriodMatcher.h /// @brief Entry-level matcher for the GRIB `referencePeriod` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether the `referencePeriod` concept is active for a request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include #include // Utils #include "metkit/mars2grib/backend/concepts/reference-period/referencePeriodEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `referencePeriod` concept variant. /// /// The `referencePeriod` concept is currently inactive and therefore always /// resolves to `compile_time_registry_engine::MISSING`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return `compile_time_registry_engine::MISSING`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t referencePeriodMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::get_or_throw; const auto marsType = get_or_throw(mars, "type"); if (marsType == "efi" || marsType == "efic" || marsType == "sot") { return static_cast(ReferencePeriodType::Default); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `referencePeriod` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/AllConcepts.h0000664000175000017500000002044115232567717025046 0ustar alastairalastair/* * (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 AllConcepts.h /// @brief Canonical compile-time list of all mars2grib semantic concepts. /// /// This header defines the **complete, ordered universe of semantic concepts** /// known to the mars2grib backend. /// /// ----------------------------------------------------------------------------- /// Scope and responsibility /// ----------------------------------------------------------------------------- /// /// This file has a single, strictly limited responsibility: /// /// - to aggregate all concept descriptor types into one canonical /// compile-time `TypeList`. /// /// It does **not**: /// - define any concept logic, /// - implement any dispatch behavior, /// - perform any registry construction, /// - introduce any compile-time algorithms. /// /// All included headers are assumed to provide **concept descriptor types** /// conforming to the `RegisterEntryDescriptor` contract. /// /// ----------------------------------------------------------------------------- /// Architectural role /// ----------------------------------------------------------------------------- /// /// The `AllConcepts` typelist defined in this file is the **root input** to the /// entire compile-time registry engine. /// /// It is consumed by: /// /// - EntryVariantRegistry (index space definition), /// - EntryCallbacksRegistry (entry-level dispatch), /// - VariantCallbacksRegistry (variant-level dispatch), /// - PhaseCallbacksRegistry (phase-level dispatch). /// /// The **order** of types in this list is semantically significant and defines: /// /// - concept identifiers (conceptId), /// - the layout of flattened variant index spaces, /// - the ordering of all generated dispatch tables. /// /// Reordering entries in this list changes global indices and must therefore be /// treated as a **breaking structural change**. /// /// ----------------------------------------------------------------------------- /// Design constraints /// ----------------------------------------------------------------------------- /// /// - Header-only /// - Purely declarative /// - No runtime state /// - No constexpr logic beyond type aggregation /// /// This file must remain lightweight and stable, as it is included transitively /// by many translation units. /// /// ----------------------------------------------------------------------------- /// Extension policy /// ----------------------------------------------------------------------------- /// /// To introduce a new semantic concept into the system: /// /// 1. Implement a new concept descriptor conforming to /// `RegisterEntryDescriptor`. /// 2. Include its header in this file. /// 3. Append the descriptor type to the `AllConcepts` typelist. /// /// No other changes are required to integrate the new concept into the /// compile-time registry engine. /// /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include // Project includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/backend/concepts/analysis/analysisConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/composition/compositionConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/data-type/dataTypeConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/derived/derivedConceptDescriptor.h" #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" #include "metkit/mars2grib/backend/concepts/model-error/modelErrorConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/nil/nilConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/origin/originConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/packing/packingConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/param/paramConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/point-in-time/pointInTimeConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/reference-period/referencePeriodConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/reference-time/referenceTimeConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/representation/representationConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/satellite/satelliteConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/statistics/statisticsConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/tables/tablesConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/wave/waveConceptDescriptor.h" namespace metkit::mars2grib::backend::concepts_::detail { /// /// @brief Local alias for the canonical compile-time TypeList. /// /// This alias shortens references to the `TypeList` type provided by the /// compile-time registry engine and avoids repeating long qualified names /// within this namespace. /// /// @tparam Ts /// Pack of concept descriptor types. /// template using TypeList = metkit::mars2grib::backend::compile_time_registry_engine::TypeList; /// /// @brief Complete ordered list of all semantic concept descriptors. /// /// `AllConcepts` defines the **entire concept universe** known to the /// mars2grib backend at compile time. /// /// Each type in this list: /// - represents one semantic concept, /// - conforms to `RegisterEntryDescriptor`, /// - contributes its variants and dispatch interfaces to the registry engine. /// /// ----------------------------------------------------------------------------- /// Ordering guarantees /// ----------------------------------------------------------------------------- /// /// The order of types in this list: /// /// - defines the concept identifier (`conceptId`) assigned to each concept, /// - determines the layout of global variant indices, /// - is propagated unchanged into all generated dispatch tables. /// /// This order must therefore be: /// - stable, /// - intentional, /// - modified only with full awareness of downstream effects. /// /// ----------------------------------------------------------------------------- /// Visibility /// ----------------------------------------------------------------------------- /// /// This typelist is placed in the `detail` namespace to: /// - discourage casual use outside registry construction, /// - make its role as *structural metadata* explicit. /// /// Higher-level code should interact with concepts exclusively through /// registry APIs, not by iterating this list directly. /// using AllConcepts = TypeList; } // namespace metkit::mars2grib::backend::concepts_::detail metkit-1.19.2/src/metkit/mars2grib/backend/concepts/GeneralRegistry.h0000664000175000017500000001204315232567717025744 0ustar alastairalastair/* * (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 GeneralRegistry.h /// @brief Canonical compile-time registry instantiation for all mars2grib concepts. /// /// This header defines the **single, authoritative instantiation** of the /// compile-time registry engine over the complete set of semantic concepts /// known to the mars2grib backend. /// /// ----------------------------------------------------------------------------- /// Scope and responsibility /// ----------------------------------------------------------------------------- /// /// This file has one precise responsibility: /// /// - to bind the generic `EntryVariantRegistry` template to the concrete, /// ordered list of all concept descriptors (`AllConcepts`). /// /// It does **not**: /// - define any concept descriptors, /// - implement any dispatch logic, /// - perform any compile-time computation itself, /// - introduce any new abstractions. /// /// It is purely a **type aliasing and configuration point**. /// /// ----------------------------------------------------------------------------- /// Architectural role /// ----------------------------------------------------------------------------- /// /// `GeneralRegistry` is the **central structural metadata provider** for the /// entire mars2grib backend. /// /// It exposes, in a single type: /// /// - the complete concept universe, /// - the global variant index space, /// - all compile-time constants derived from concept structure /// (counts, offsets, identifiers), /// - constexpr lookup tables for concept and variant metadata. /// /// All higher-level systems depend on this registry, including: /// /// - header layout resolution, /// - encoding plan construction, /// - callback dispatch tables, /// - debugging and diagnostic utilities. /// /// ----------------------------------------------------------------------------- /// Ordering and stability guarantees /// ----------------------------------------------------------------------------- /// /// The structure of `GeneralRegistry` is entirely determined by /// `detail::AllConcepts`. /// /// Consequently: /// /// - the order of concepts in `AllConcepts` defines concept identifiers, /// - the order of variants in each concept defines global variant indices, /// - any reordering is a **breaking structural change**. /// /// Stability of this registry is therefore critical for: /// - reproducibility, /// - deterministic encoding, /// - consistency across translation units. /// /// ----------------------------------------------------------------------------- /// Namespace choice /// ----------------------------------------------------------------------------- /// /// This registry is placed in the `concepts_` namespace (with trailing /// underscore) to: /// /// - avoid collision with the C++20 `concepts` keyword, /// - clearly distinguish internal structural concepts from language features. /// /// ----------------------------------------------------------------------------- /// Design constraints /// ----------------------------------------------------------------------------- /// /// - Header-only /// - Purely compile-time /// - No runtime state /// - No dynamic allocation /// /// This header must remain lightweight and safe to include transitively. /// /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include // Project includes #include "metkit/mars2grib/backend/compile-time-registry-engine/EntryVariantRegistry.h" #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/AllConcepts.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Canonical compile-time registry over all mars2grib concepts. /// /// `GeneralRegistry` is a concrete specialization of /// `EntryVariantRegistry`, instantiated with the complete, ordered list /// of all concept descriptors. /// /// This alias provides a **single point of access** to: /// /// - concept counts and identifiers, /// - variant counts and global indices, /// - compile-time offset calculations, /// - constexpr metadata tables (names, IDs), /// - sentinel-aware index utilities. /// /// It is intended to be used as: /// /// - the structural backbone of the encoding pipeline, /// - a read-only, constexpr registry, /// - a shared dependency across frontend and backend layers. /// /// @note /// There must be exactly one such registry instantiation for the entire /// mars2grib backend. Introducing additional instantiations with different /// concept lists would create incompatible index spaces. /// using GeneralRegistry = EntryVariantRegistry; } // namespace metkit::mars2grib::backend::concepts_metkit-1.19.2/src/metkit/mars2grib/backend/concepts/analysis/0000775000175000017500000000000015232567717024310 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/analysis/analysisEnum.h0000664000175000017500000001044615232567717027136 0ustar alastairalastair/* * (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 analysisEnum.h /// @brief Definition of the `analysis` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `analysis` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`analysisName`) /// - the enumeration of supported analysis variants (`AnalysisType`) /// - a compile-time typelist of all variants (`AnalysisList`) /// - 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 /// `analysisEncoding.h` 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 `analysis` 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 `analysis` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view analysisName{"analysis"}; /// /// @brief Enumeration of all supported `analysis` concept variants. /// /// Each enumerator represents a specific analysis mode or semantic /// interpretation 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 analysis concept evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class AnalysisType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `analysis` 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 AnalysisList = ValueList; /// /// @brief Compile-time mapping from `AnalysisType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given analysis variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Analysis 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 analysisTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view analysisTypeName() { \ return NAME; \ } DEF(AnalysisType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/analysis/analysisEncoding.h0000664000175000017500000001535415232567717027763 0ustar alastairalastair/* * (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 analysisEncoding.h /// @brief Implementation of the GRIB `analysis` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **analysis concept** within the mars2grib backend. /// /// The concept is responsible for populating GRIB keys related to the /// *Local Use Section* analysis metadata, based on information extracted /// from MARS input dictionaries and validated against GRIB constraints. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `analysisApplicable` /// - Runtime validation and deduction /// - 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 // System includes #include #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/analysis/analysisEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/lengthOfTimeWindow.h" #include "metkit/mars2grib/backend/deductions/offsetToEndOf4DvarWindow.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_ { using namespace metkit::mars2grib::backend::compile_time_registry_engine; /// /// @brief Compile-time applicability predicate for the `analysis` concept. /// /// This function determines whether the `analysis` concept is applicable /// for a given combination of: /// - encoding stage /// - GRIB section /// - concept variant /// /// The applicability is evaluated entirely at compile time and is used /// by the concept dispatcher to ensure that only valid concept invocations /// are instantiated. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Analysis 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 == AnalysisType::Default` /// - `Stage == StagePreset` /// - `Section == SecLocalUseSection` /// /// Users may override or specialize this predicate to alter applicability. /// template constexpr bool analysisApplicable() { // Conditions to apply concept return ((Variant == AnalysisType::Default) && (Stage == StagePreset) && (Section == SecLocalUseSection)); } /// /// @brief Execute the `analysis` concept operation. /// /// This function implements the runtime logic of the GRIB `analysis` concept. /// When applicable, it: /// /// 1. Verifies GRIB preconditions for the Local Use Section. /// 2. Deduces required analysis-related values from MARS and parameter dictionaries. /// 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 Analysis 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 concept is called when not applicable /// - required GRIB preconditions are not satisfied /// - any deduction or encoding step fails /// /// @note /// - All runtime errors are wrapped with full concept context /// (concept name, variant, stage, section). /// - This function does not rely on any pre-existing GRIB header state. /// /// @see analysisApplicable /// template void AnalysisOp(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 (analysisApplicable()) { try { MARS2GRIB_LOG_CONCEPT(analysis); // Structural validation validation::match_LocalDefinitionNumber_or_throw(opt, out, {36L, 37L, 38L, 39L}); // Deductions long offsetToEndOf4DvarWindowVal = deductions::resolve_offsetToEndOf4DvarWindow_or_throw(mars, par, opt); std::optional lengthOfTimeWindowVal = deductions::resolve_LengthOfTimeWindowInSeconds_or_throw(mars, par, opt); // Encoding set_or_throw(out, "offsetToEndOf4DvarWindow", offsetToEndOf4DvarWindowVal); if (lengthOfTimeWindowVal.has_value()) { set_or_throw(out, "lengthOf4DvarWindow", lengthOfTimeWindowVal.value() / 3600); } else { long missingLengthOfTimeWindowInHours = 0xFFFF; // Missing sentinel in hours set_or_throw(out, "lengthOf4DvarWindow", missingLengthOfTimeWindowInHours); // Missing } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(analysis, "Unable to set `analysis` concept..."); } return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(analysis, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/analysis/analysisConceptDescriptor.h0000664000175000017500000001457215232567717031670 0ustar alastairalastair/* * (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 analysisConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `analysis` concept. /// /// This header defines `AnalysisConcept`, the **compile-time descriptor** /// that registers the GRIB `analysis` 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/analysis/analysisEncoding.h" #include "metkit/mars2grib/backend/concepts/analysis/analysisEnum.h" #include "metkit/mars2grib/backend/concepts/analysis/analysisMatcher.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 `analysis` concept. /// /// `AnalysisConcept` registers the GRIB `analysis` 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 AnalysisConcept : 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 analysisName; } /// /// @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 analysisTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `analysis` 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 (analysisApplicable()) { return &AnalysisOp; } else { return nullptr; } } else { return nullptr; } // Avoid compiler warnings mars2gribUnreachable(); } /// /// @brief Variant-specific callbacks (not used for this concept). /// /// This hook is provided for completeness of the registry interface. /// The `analysis` concept does not define variant-level callbacks, /// so this function always returns `nullptr`. /// /// @tparam Capability Encoding capability index /// @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 Always `nullptr` /// template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This callback is invoked to determine whether the `analysis` /// concept should be activated for a given encoding request. /// /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Matcher function pointer /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &analysisMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/analysis/analysisMatcher.h0000664000175000017500000000512415232567717027612 0ustar alastairalastair/* * (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 analysisMatcher.h /// @brief Entry-level matcher for the GRIB `analysis` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether the **analysis concept** is active for a MARS request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/analysis/analysisEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `analysis` concept variant. /// /// The concept is active when the MARS request contains `anoffset`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `AnalysisType::Default`, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t analysisMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::has; if (has(mars, "anoffset")) { return static_cast(AnalysisType::Default); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `analysis` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/statistics/0000775000175000017500000000000015232567717024657 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h0000664000175000017500000001764315232567717030541 0ustar alastairalastair/* * (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 statisticsMatcher.h /// @brief Entry-level matcher for the GRIB `statistics` concept. /// /// This header defines the runtime matcher used by the concept registry to /// identify statistical-processing semantics from the MARS parameter code. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include #include // Utils #include "metkit/mars2grib/backend/concepts/statistics/statisticsEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/paramMatcher.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `statistics` concept variant. /// /// The matcher maps known statistical MARS parameter codes onto the appropriate /// local `StatisticsType` variant. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local statistics variant index, or /// `compile_time_registry_engine::MISSING` when no statistical-processing /// concept is active. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If the required parameter metadata cannot be read or lower-level matcher /// evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t statisticsMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; const auto param = get_or_throw(mars, "param"); if (matchAny(param, 8, 9, 20, 44, 45, 47, 50, 57, 58, range(142, 147), 169, range(175, 182), 189, range(195, 197), 205, range(208, 213), 228, 239, 240, 3062, 3099, range(162100, 162113), range(222001, 222256), 228021, 228022, 228129, 228130, 228143, 228144, 228216, 228228, 228251, range(231001, 231003), 231005, 231010, 231012, 231057, 231058, range(233000, 233031), 260259)) { return static_cast(StatisticsType::Accumulation); } // Strike-probability products if (matchAny(param, 131060, 131061, 131062, 131063, 131064, 131085, 131098, 131099)) { return static_cast(StatisticsType::Accumulation); } if (matchAny(param, range(141101, 141105), 141208, 141209, 141215, 141216, 141220, 141229, 141232, 141233, 141245, 228004, 228005, 228051, 228053, range(228057, 228060), 235020, 235021, range(235029, 235031), range(235033, 235043), range(235048, 235053), 235055, 235058, range(235077, 235080), 235083, 235084, 235087, 235088, 235090, 235091, 235093, 235094, 235097, 235098, 235100, 235108, range(235129, 235138), 235151, 235152, 235155, 235157, 235159, 235165, 235166, 235168, 235189, 235203, 235246, 235263, 235269, 235283, 235287, 235288, 235290, 235305, 235309, 235322, 235326, 235339, 235383, 263024, 263107)) { return static_cast(StatisticsType::Average); } // Strike-probability products if (matchAny(param, 131065, 131066, 131067)) { return static_cast(StatisticsType::Average); } if (matchAny(param, 49, 121, 123, 201, range(143101, 143105), 143208, 143209, 143215, 143216, 143220, 143229, 143232, 143233, 143245, 228026, 228028, 228035, 228036, 228222, 228224, 228226, 237013, 237041, 237042, 237055, 237077, 237078, 237080, 237083, 237084, 237087, 237088, 237090, 237091, 237093, 237094, 237097, 237108, 237117, 237131, 237132, 237134, 237137, 237151, 237159, range(237165, 237168), 237203, 237207, 237263, 237287, 237288, 237290, 237305, 237309, 237318, 237321, 237322, 237326, 265024)) { return static_cast(StatisticsType::Maximum); } // Strike-probability products if (matchAny(param, 131071, 131072, 131100)) { return static_cast(StatisticsType::Maximum); } if (matchAny(param, 122, 202, range(144101, 144105), 144208, 144209, 144215, 144216, 144220, 144229, 144232, 144233, 144245, 228027, 228223, 228225, 228227, 238013, 238041, 238042, 238055, 238077, 238078, 238080, 238083, 238084, 238087, 238088, 238090, 238091, 238093, 238094, 238097, 238108, 238131, 238132, 238134, 238137, 238151, 238159, range(238165, 238168), 238203, 238207, 238263, 238287, 238288, 238290, 238305, 238309, 238322, 238326, 266024, 131070)) { return static_cast(StatisticsType::Minimum); } if (matchAny(param, 260320, 260321, 260339, 260683)) { return static_cast(StatisticsType::Mode); } if (matchAny(param, 260318, 260319, 260338, 260682)) { return static_cast(StatisticsType::Severity); } if (matchAny(param, range(145101, 145105), 145208, 145209, 145215, 145216, 145220, 145229, 145232, 145233, 145245, 239041, 239042, 239077, 239078, 239080, 239083, 239084, 239087, 239088, 239090, 239091, 239093, 239094, 239097, 239108, 239131, 239132, 239134, 239137, 239151, 239159, range(239165, 239168), 239203, 239207, 239263, 239287, 239288, 239290, 239305, 239309, 239322, 239326, 267024)) { return static_cast(StatisticsType::StandardDeviation); } // Strike-probability products if (matchAny(param, 133093, 133094, 133095, 133096, 133097, 133098)) { return static_cast(StatisticsType::StandardDeviation); } // Chemical products if (matchAny(param, range(228080, 228082), range(233032, 233035), range(235062, 235064))) { return static_cast(StatisticsType::Accumulation); } // TODO: Don't handle products with timespan as non-statistical if they are not handled above! // if (has(mars, "timespan")) { // throw utils::exceptions::Mars2GribMatcherException("MARS contains `timespan` but // typeOfStatisticalProcessing is defined for param " + std::to_string(param), Here()); // } if (matchAny(param, 132044, 132045, 132049, 132059, 132144, 132165, 132167, 132201, 132202, 132228)) { return static_cast(StatisticsType::IndexProcessing); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `statistics` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/statistics/statisticsEnum.h0000664000175000017500000002421315232567717030051 0ustar alastairalastair/* * (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 statisticsEnum.h /// @brief Definition of the `statistics` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `statistics` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`statisticsName`) /// - the exhaustive enumeration of supported statistical processing variants (`StatisticsType`) /// - a compile-time typelist of all variants (`StatisticsList`) /// - a compile-time mapping from variant to string identifier /// - a compile-time mapping from variant to GRIB `typeOfStatisticalProcessing` code /// /// 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 /// - static mapping to GRIB statistical processing identifiers /// /// @note /// This header is part of the **concept definition layer**. /// Runtime behavior is implemented separately in the corresponding /// `statisticsEncoding.h` 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/backend/tables/typeOfStatisticalProcessing.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 `statistics` 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 `statistics` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view statisticsName{"statistics"}; /// /// @brief Enumeration of all supported `statistics` concept variants. /// /// Each enumerator represents a distinct statistical processing operation /// applied to time ranges, ensembles, or aggregated datasets, as defined /// by GRIB conventions and ECMWF usage. /// /// 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 includes both standard GRIB statistical operators /// and extended or ECMWF-specific processing types. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class StatisticsType : std::size_t { Average = 0, Accumulation, Maximum, Minimum, DifferenceFromStart, RootMeanSquare, StandardDeviation, Covariance, DifferenceFromEnd, Ratio, StandardizedAnomaly, Summation, ReturnPeriod, Median, Severity, Mode, IndexProcessing, Default }; /// /// @brief Compile-time list of all `statistics` 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 StatisticsList = ValueList; /// /// @brief Compile-time mapping from `StatisticsType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given statistical processing variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Statistics 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 statisticsTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view statisticsTypeName() { \ return NAME; \ } DEF(StatisticsType::Average, "average"); DEF(StatisticsType::Accumulation, "accumulation"); DEF(StatisticsType::Maximum, "maximum"); DEF(StatisticsType::Minimum, "minimum"); DEF(StatisticsType::DifferenceFromStart, "differenceFromStart"); DEF(StatisticsType::RootMeanSquare, "rootMeanSquare"); DEF(StatisticsType::StandardDeviation, "standardDeviation"); DEF(StatisticsType::Covariance, "covariance"); DEF(StatisticsType::DifferenceFromEnd, "differenceFromEnd"); DEF(StatisticsType::Ratio, "ratio"); DEF(StatisticsType::StandardizedAnomaly, "standardizedAnomaly"); DEF(StatisticsType::Summation, "summation"); DEF(StatisticsType::ReturnPeriod, "returnPeriod"); DEF(StatisticsType::Median, "median"); DEF(StatisticsType::Severity, "severity"); DEF(StatisticsType::Mode, "mode"); DEF(StatisticsType::IndexProcessing, "indexProcessing"); DEF(StatisticsType::Default, "default"); #undef DEF /// /// @brief Compile-time mapping from `StatisticsType` to GRIB `typeOfStatisticalProcessing` code. /// /// This function returns the numeric GRIB code associated with a given /// statistical processing variant, as defined by GRIB tables and /// ECMWF extensions. /// /// The returned value is used during encoding to populate the /// `typeOfStatisticalProcessing` key in GRIB messages. /// /// @tparam T Statistics variant /// @return GRIB code corresponding to the variant /// /// @note /// The returned values must remain stable across releases to ensure /// GRIB compatibility. /// template constexpr long typeOfStatisticalProcessing(); #define DEF(T, NAME) \ template <> \ constexpr long typeOfStatisticalProcessing() { \ return NAME; \ } DEF(StatisticsType::Average, 0); DEF(StatisticsType::Accumulation, 1); DEF(StatisticsType::Maximum, 2); DEF(StatisticsType::Minimum, 3); DEF(StatisticsType::DifferenceFromStart, 4); DEF(StatisticsType::RootMeanSquare, 5); DEF(StatisticsType::StandardDeviation, 6); DEF(StatisticsType::Covariance, 7); DEF(StatisticsType::DifferenceFromEnd, 8); DEF(StatisticsType::Ratio, 9); DEF(StatisticsType::StandardizedAnomaly, 10); DEF(StatisticsType::Summation, 11); DEF(StatisticsType::ReturnPeriod, 12); DEF(StatisticsType::Median, 13); DEF(StatisticsType::Severity, 100); DEF(StatisticsType::Mode, 101); DEF(StatisticsType::IndexProcessing, 102); DEF(StatisticsType::Default, 255); #undef DEF /// /// @brief Compile-time mapping from `StatisticsType` to /// `tables::TypeOfStatisticalProcessing` (GRIB Code Table 4.10). /// /// Parallel to `typeOfStatisticalProcessing()` (which returns the raw /// `long` GRIB code). Use this overload when the call site needs the /// strongly-typed enum value — e.g. when passing the inner statistical /// operation to `resolve_TypeOfStatisticalProcessing_or_throw`. /// /// The two specialization sets MUST stay in numerical lock-step; any /// future addition to `StatisticsType` MUST update both. /// /// @note /// Three enumerator names differ between the concept-side `StatisticsType` /// and the table-side `tables::TypeOfStatisticalProcessing` (the numeric /// codes match exactly): /// - `StatisticsType::DifferenceFromStart` → `DifferenceEndMinusStart` (4) /// - `StatisticsType::DifferenceFromEnd` → `DifferenceStartMinusEnd` (8) /// - `StatisticsType::Default` → `Missing` (255) /// template constexpr tables::TypeOfStatisticalProcessing typeOfStatisticalProcessingEnum(); #define DEF(T, VAL) \ template <> \ constexpr tables::TypeOfStatisticalProcessing typeOfStatisticalProcessingEnum() { \ return tables::TypeOfStatisticalProcessing::VAL; \ } DEF(StatisticsType::Average, Average); DEF(StatisticsType::Accumulation, Accumulation); DEF(StatisticsType::Maximum, Maximum); DEF(StatisticsType::Minimum, Minimum); DEF(StatisticsType::DifferenceFromStart, DifferenceEndMinusStart); DEF(StatisticsType::RootMeanSquare, RootMeanSquare); DEF(StatisticsType::StandardDeviation, StandardDeviation); DEF(StatisticsType::Covariance, Covariance); DEF(StatisticsType::DifferenceFromEnd, DifferenceStartMinusEnd); DEF(StatisticsType::Ratio, Ratio); DEF(StatisticsType::StandardizedAnomaly, StandardizedAnomaly); DEF(StatisticsType::Summation, Summation); DEF(StatisticsType::ReturnPeriod, ReturnPeriod); DEF(StatisticsType::Median, Median); DEF(StatisticsType::Severity, Severity); DEF(StatisticsType::Mode, Mode); DEF(StatisticsType::IndexProcessing, IndexProcessing); DEF(StatisticsType::Default, Missing); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/statistics/statisticsConceptDescriptor.h0000664000175000017500000001206215232567717032576 0ustar alastairalastair/* * (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 statisticsConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `statistics` concept. /// /// This header defines `StatisticsConcept`, the **compile-time descriptor** /// that registers the GRIB `statistics` 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/statistics/statisticsEncoding.h" #include "metkit/mars2grib/backend/concepts/statistics/statisticsEnum.h" #include "metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.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 `statistics` concept. /// /// `StatisticsConcept` registers the GRIB `statistics` concept into the /// compile-time registry engine. /// struct StatisticsConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return statisticsName; } /// /// @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 statisticsTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `statistics` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (statisticsApplicable()) { return &StatisticsOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `statistics` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &statisticsMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/statistics/statisticsEncoding.h0000664000175000017500000002142615232567717030676 0ustar alastairalastair/* * (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 statisticsEncoding.h /// @brief Implementation of the GRIB `statistics` concept operation. /// /// The `statistics` concept encodes GRIB Section-4 keys describing /// statistical processing over time (PDT 4.8 / 4.11). It runs in three /// stages: /// /// ### StageAllocate /// - Validates that the Product Definition Section supports statistics. /// - Encodes `numberOfTimeRanges` (from `ProductTime`). /// - Sets `hoursAfterDataCutoff` / `minutesAfterDataCutoff` to missing. /// /// ### StagePreset /// - Computes the per-loop statistical processing descriptor from a /// `ProductTime` and writes the 6 SoA vectors verbatim: /// * `typeOfStatisticalProcessing` /// * `typeOfTimeIncrement` /// * `indicatorOfUnitForTimeRange` /// * `lengthOfTimeRange` /// * `indicatorOfUnitForTimeIncrement` /// * `lengthOfTimeIncrement` /// No instant / single-window / multi-window branching: a single uniform /// path covers all cases (including the AIFS no-increment hack which is /// handled inside `compute_StatisticalProcessing`). /// /// ### StageRuntime /// - Encodes the time-dependent keys from the resolved `ProductTime`: /// - `forecastTime` (hours between `pt.referenceDateTime` and `pt.windowStart`) /// - `OfEndOfOverallTimeInterval` (from `pt.windowEnd`) /// /// All temporal data is sourced exclusively from the `ProductTime` /// produced by `resolve_ProductTime_or_throw` (§15 of `timeProducts.md`). /// /// @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/backend/concepts/statistics/impl/statisticsDescriptor.h" #include "metkit/mars2grib/backend/concepts/statistics/statisticsEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions (ProductTime + per-loop type-of-statistical-processing resolver) #include "metkit/mars2grib/backend/deductions/productTime.h" #include "metkit/mars2grib/backend/deductions/typeOfStatisticalProcessing.h" // Checks #include "metkit/mars2grib/backend/checks/checkStatisticsProductDefinitionSection.h" // Tables #include "metkit/mars2grib/backend/tables/typeOfStatisticalProcessing.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 `statistics` concept. /// /// The concept is applicable for the *Product Definition Section* (Section 4) /// at any encoding stage. /// template constexpr bool statisticsApplicable() { return (Section == SecProductDefinitionSection && Variant != StatisticsType::IndexProcessing); } /// /// @brief Execute the `statistics` concept operation. /// /// See file-level documentation for stage-by-stage semantics. /// template void StatisticsOp(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::dict_traits::setMissing_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (statisticsApplicable()) { try { MARS2GRIB_LOG_CONCEPT(statistics); // ============================================================= // StageAllocate // ============================================================= if constexpr (Stage == StageAllocate) { auto pt = deductions::resolve_ProductTime_or_throw(mars, par, opt); // Checks/Validation validation::check_StatisticsProductDefinitionSection_or_throw(opt, out); // Encoding setMissing_or_throw(out, "hoursAfterDataCutoff"); setMissing_or_throw(out, "minutesAfterDataCutoff"); set_or_throw(out, "numberOfTimeRanges", deductions::numberOfTimeRanges(pt)); } // ============================================================= // StagePreset // // Single uniform path. No branching for instant / single-window // / multi-window: `compute_StatisticalProcessing` handles all // cases (including the AIFS no-increment hack) internally. // ============================================================= if constexpr (Stage == StagePreset) { auto pt = deductions::resolve_ProductTime_or_throw(mars, par, opt); auto inner = typeOfStatisticalProcessingEnum(); auto types = deductions::resolve_TypeOfStatisticalProcessing_or_throw(inner, mars, par, opt); auto desc = impl::compute_StatisticalProcessing(pt, types); set_or_throw>(out, "typeOfStatisticalProcessing", desc.typeOfStatisticalProcessing); set_or_throw>(out, "typeOfTimeIncrement", desc.typeOfTimeIncrement); set_or_throw>(out, "indicatorOfUnitForTimeRange", desc.indicatorOfUnitForTimeRange); set_or_throw>(out, "lengthOfTimeRange", desc.lengthOfTimeRange); set_or_throw>(out, "indicatorOfUnitForTimeIncrement", desc.indicatorOfUnitForTimeIncrement); set_or_throw>(out, "timeIncrement", desc.lengthOfTimeIncrement); } // ============================================================= // StageRuntime // // Time-dependent keys: forecastTime and end-of-interval date/time. // ============================================================= if constexpr (Stage == StageRuntime) { auto pt = deductions::resolve_ProductTime_or_throw(mars, par, opt); // forecastTime is the distance in hours between reference time // and WindowBegin (pt.windowStart). It must be a non-negative // integer number of hours. const long deltaSeconds = static_cast(static_cast(pt.windowStart - pt.referenceDateTime)); if (deltaSeconds < 0) { throw Mars2GribConceptException( std::string(statisticsName), std::string(statisticsTypeName()), std::to_string(Stage), std::to_string(Section), "statistics: forecastTime must be non-negative", Here()); } if (deltaSeconds % 3600 != 0) { throw Mars2GribConceptException( std::string(statisticsName), std::string(statisticsTypeName()), std::to_string(Stage), std::to_string(Section), "statistics: forecastTime must be a whole number of hours", Here()); } set_or_throw(out, "forecastTime", deltaSeconds / 3600); const eckit::DateTime& end = pt.windowEnd; set_or_throw(out, "yearOfEndOfOverallTimeInterval", end.date().year()); set_or_throw(out, "monthOfEndOfOverallTimeInterval", end.date().month()); set_or_throw(out, "dayOfEndOfOverallTimeInterval", end.date().day()); set_or_throw(out, "hourOfEndOfOverallTimeInterval", end.time().hours()); set_or_throw(out, "minuteOfEndOfOverallTimeInterval", end.time().minutes()); set_or_throw(out, "secondOfEndOfOverallTimeInterval", end.time().seconds()); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(statistics, "Unable to set `statistics` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(statistics, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/statistics/impl/0000775000175000017500000000000015232567717025620 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/statistics/impl/statisticsDescriptor.h0000664000175000017500000002520315232567717032224 0ustar alastairalastair/* * (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 impl/statisticsDescriptor.h /// @brief Pure SoA builder for the GRIB Section-4 statistical processing keys. /// /// Defines: /// - `StatisticalProcessing`: a transparent shadow of the GRIB header keys /// `typeOfStatisticalProcessing`, `typeOfTimeIncrement`, /// `indicatorOfUnitForTimeRange`, `lengthOfTimeRange`, /// `indicatorOfUnitForTimeIncrement`, `lengthOfTimeIncrement`. All /// members are `long` / `vector` so the struct can be written /// verbatim to eccodes. /// /// - `compute_StatisticalProcessing`: a pure function turning a /// resolved `ProductTime` plus the per-loop typeOfStatisticalProcessing /// vector into a fully-populated `StatisticalProcessing`. /// /// All semantic decisions (instant vs single-window vs multi-window, AIFS /// no-increment hack, calendar-month length) live inside this single /// function. The encoding layer remains a thin write-out. /// /// @ingroup mars2grib_backend_concepts /// #pragma once #include #include #include #include #include "eckit/exception/Exceptions.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/backend/deductions/detail/ProductTime.h" #include "metkit/mars2grib/backend/tables/timeUnits.h" #include "metkit/mars2grib/backend/tables/typeOfStatisticalProcessing.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_::impl { /// /// @brief Transparent SoA shadow of the GRIB Section-4 statistical keys. /// /// All vectors have the same length: `numberOfTimeRanges`. Ordering is /// outermost → innermost, matching `ProductTime::statisticalWindows`. /// /// All members are `long` / `vector` because eccodes does not /// understand `tables::TimeUnit` or `tables::TypeOfStatisticalProcessing` /// as types — it expects raw numeric codes. Callers MUST cast the table /// enums to `static_cast` before populating the vectors (the builder /// in this file does this internally). /// struct StatisticalProcessing { long numberOfTimeRanges = 0; std::vector typeOfStatisticalProcessing; std::vector typeOfTimeIncrement; std::vector indicatorOfUnitForTimeRange; std::vector lengthOfTimeRange; std::vector indicatorOfUnitForTimeIncrement; std::vector lengthOfTimeIncrement; }; namespace detail { /// /// @brief Length in hours of the calendar month that ENDS at `(year, month)`. /// /// Mirrors the legacy `previousMonthLengthHours` from /// `deductions/detail/timeUtils.h`. A monthly statistical window whose /// `windowEnd` is the first of the month covers the *previous* calendar /// month, hence the helper is named accordingly. /// inline long previousMonthLengthHours(long year, long month) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; if (month < 1 || month > 12) { throw Mars2GribGenericException("Invalid month (must be 1..12)", Here()); } switch (month) { case 1: case 2: case 4: case 6: case 8: case 9: case 11: return 31 * 24; case 5: case 7: case 10: case 12: return 30 * 24; case 3: return ((year % 4) == 0 ? 29 : 28) * 24; } throw Mars2GribGenericException("Unreachable", Here()); } /// /// @brief Convert a `StatisticalWindow` to its length in hours. /// /// - `Second`: `count` must be a multiple of 3600, returns `count / 3600`. /// - `Day`: returns `count * 24`. /// - `Month`: returns `count * previousMonthLengthHours(endYear, endMonth)`. /// /// `endYear`/`endMonth` are taken from `pt.windowEnd` for monthly windows. /// inline long windowLengthInHours(const deductions::detail::StatisticalWindow& w, long endYear, long endMonth) { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; switch (w.unit) { case tables::TimeUnit::Second: { if (w.count % 3600 != 0) { throw Mars2GribDeductionException( "StatisticalWindow with unit=Second must have count divisible by 3600 " "to be expressed in hours; actual count=" + std::to_string(w.count), Here()); } return w.count / 3600; } case tables::TimeUnit::Day: return w.count * 24; case tables::TimeUnit::Month: return w.count * previousMonthLengthHours(endYear, endMonth); default: throw Mars2GribDeductionException("Unsupported StatisticalWindow unit for hour conversion: '" + tables::enum2name_TimeUnit_or_throw(w.unit) + "'", Here()); } } } // namespace detail /// /// @brief Build the GRIB statistical-processing SoA from a `ProductTime`. /// /// Single uniform loop over `pt.statisticalWindows[0 .. statisticalWindowCount)`. /// No instant / single-window / multi-window branching: every slot is /// filled identically except for the AIFS no-increment edge case detailed /// below. /// /// **Per-slot semantics**: /// - `typeOfTimeIncrement[i] = 2` (multIO fixed value) /// - `indicatorOfUnitForTimeRange[i] = Hour (1)` (legacy convention) /// - `indicatorOfUnitForTimeIncrement[i] = Second (13)` (legacy convention) /// - `typeOfStatisticalProcessing[i] = static_cast(types[i])` /// - `lengthOfTimeRange[i] = windowLengthInHours(window[i])` /// - `lengthOfTimeIncrement[i]`: /// * inner slot (`i == n - 1`): `pt.timeIncrementInSeconds.value()` /// (or AIFS hack — see below) /// * outer slot: `lengthOfTimeRange[i+1] * 3600` (i.e. /// the hour-length of the next-deeper loop expressed in seconds). /// /// **AIFS single-window no-increment hack** (§9.4): /// when `pt.statisticalWindowCount == 1` AND /// `!pt.timeIncrementInSeconds.has_value()`, the inner slot is filled with /// - `lengthOfTimeIncrement[0] = 0` /// - `indicatorOfUnitForTimeIncrement[0] = static_cast(TimeUnit::Missing)` (255) /// to mark the increment as undefined. /// /// **Preconditions**: /// - `types.size() == pt.statisticalWindowCount`. /// - For `pt.statisticalWindowCount >= 2`, `pt.timeIncrementInSeconds` /// MUST have a value (the AIFS hack is single-window only). /// /// @param[in] pt Resolved `ProductTime`. /// @param[in] types Per-loop GRIB type-of-statistical-processing codes /// (outermost → innermost), same length as /// `pt.statisticalWindowCount`. /// /// @return Fully-populated `StatisticalProcessing` ready to be written to /// the GRIB header verbatim. /// /// @throws Mars2GribDeductionException on size mismatch, missing required /// increment for multi-window products, or unsupported window unit. /// inline StatisticalProcessing compute_StatisticalProcessing( const deductions::detail::ProductTime& pt, const std::vector& types) { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; const std::size_t n = pt.statisticalWindowCount; if (types.size() != n) { throw Mars2GribDeductionException( "compute_StatisticalProcessing: types.size()=" + std::to_string(types.size()) + " != pt.statisticalWindowCount=" + std::to_string(n), Here()); } StatisticalProcessing out; out.numberOfTimeRanges = static_cast(n); out.typeOfStatisticalProcessing.resize(n); out.typeOfTimeIncrement.resize(n); out.indicatorOfUnitForTimeRange.resize(n); out.lengthOfTimeRange.resize(n); out.indicatorOfUnitForTimeIncrement.resize(n); out.lengthOfTimeIncrement.resize(n); if (n == 0) { // Instant product: nothing to encode. Caller (Allocate stage) will // still write numberOfTimeRanges=0; Preset will write 6 empty // vectors which eccodes treats as no-op for these keys. return out; } // AIFS single-window no-increment detection (§9.4). const bool aifsSingleWindowHack = (n == 1) && !pt.timeIncrementInSeconds.has_value(); if (n >= 2 && !pt.timeIncrementInSeconds.has_value()) { throw Mars2GribDeductionException("compute_StatisticalProcessing: multi-window product (n=" + std::to_string(n) + ") requires pt.timeIncrementInSeconds; missing.", Here()); } const long endYear = pt.windowEnd.date().year(); const long endMonth = pt.windowEnd.date().month(); // First pass: fill typeOfStatisticalProcessing, fixed indicators, and // lengthOfTimeRange. lengthOfTimeIncrement is filled in a second pass // so outer slots can reference the inner slot's lengthOfTimeRange. for (std::size_t i = 0; i < n; ++i) { out.typeOfStatisticalProcessing[i] = static_cast(types[i]); out.typeOfTimeIncrement[i] = 2; out.indicatorOfUnitForTimeRange[i] = static_cast(tables::TimeUnit::Hour); out.indicatorOfUnitForTimeIncrement[i] = static_cast(tables::TimeUnit::Second); out.lengthOfTimeRange[i] = detail::windowLengthInHours(pt.statisticalWindows[i], endYear, endMonth); } // Second pass: lengthOfTimeIncrement. for (std::size_t i = 0; i < n; ++i) { const bool isInner = (i + 1 == n); if (isInner) { if (aifsSingleWindowHack) { out.lengthOfTimeIncrement[i] = 0; out.indicatorOfUnitForTimeIncrement[i] = static_cast(tables::TimeUnit::Missing); } else { // Guaranteed by the multi-window precondition check above // (and by single-window non-AIFS path). out.lengthOfTimeIncrement[i] = pt.timeIncrementInSeconds.value(); } } else { // Outer slot: increment equals the next-deeper window length // expressed in seconds (lengthOfTimeRange is in hours). out.lengthOfTimeIncrement[i] = out.lengthOfTimeRange[i + 1] * 3600; } } return out; } } // namespace metkit::mars2grib::backend::concepts_::impl metkit-1.19.2/src/metkit/mars2grib/backend/concepts/generating-process/0000775000175000017500000000000015232567717026264 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/generating-process/generatingProcessEnum.h0000664000175000017500000001103615232567717032745 0ustar alastairalastair/* * (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 generatingProcessEnum.h /// @brief Definition of the `generatingProcess` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `generatingProcess` /// concept used by the mars2grib backend. It contains: /// /// - the canonical concept name (`generatingProcessName`) /// - the enumeration of supported generating-process variants (`GeneratingProcessType`) /// - a compile-time typelist of all variants (`GeneratingProcessList`) /// - 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 /// `generatingProcessEncoding.h` 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 `generatingProcess` 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 `generatingProcess` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view generatingProcessName{"generatingProcess"}; /// /// @brief Enumeration of all supported `generatingProcess` concept variants. /// /// Each enumerator represents a specific generating-process classification /// 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 generating-process concept evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class GeneratingProcessType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `generatingProcess` 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 GeneratingProcessList = ValueList; /// /// @brief Compile-time mapping from `GeneratingProcessType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given generating-process variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Generating-process 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 generatingProcessTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view generatingProcessTypeName() { \ return NAME; \ } DEF(GeneratingProcessType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/generating-process/generatingProcessMatcher.h0000664000175000017500000000457515232567717033436 0ustar alastairalastair/* * (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 generatingProcessMatcher.h /// @brief Entry-level matcher for the GRIB `generatingProcess` concept. /// /// This header defines the runtime matcher used by the concept registry to /// activate the default generating-process concept variant. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/generating-process/generatingProcessEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `generatingProcess` concept variant. /// /// The generating-process concept is always active and resolves to /// `GeneratingProcessType::Default`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `GeneratingProcessType::Default`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t generatingProcessMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { return static_cast(GeneratingProcessType::Default); } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `generatingProcess` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ ././@LongLink0000644000000000000000000000015400000000000011603 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/generating-process/generatingProcessConceptDescriptor.hmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/generating-process/generatingProcessConceptDescr0000664000175000017500000001517015232567717034172 0ustar alastairalastair/* * (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 generatingProcessConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `generatingProcess` concept. /// /// This header defines `GeneratingProcessConcept`, the **compile-time descriptor** /// that registers the GRIB `generatingProcess` 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/generating-process/generatingProcessEncoding.h" #include "metkit/mars2grib/backend/concepts/generating-process/generatingProcessEnum.h" #include "metkit/mars2grib/backend/concepts/generating-process/generatingProcessMatcher.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 `generatingProcess` concept. /// /// `GeneratingProcessConcept` registers the GRIB `generatingProcess` /// 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 GeneratingProcessConcept : 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 generatingProcessName; } /// /// @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 generatingProcessTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `generatingProcess` 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 (generatingProcessApplicable()) { return &GeneratingProcessOp; } else { return nullptr; } } else { return nullptr; } // Avoid compiler warnings mars2gribUnreachable(); } /// /// @brief Variant-specific callbacks (not used for this concept). /// /// This hook is provided for completeness of the registry interface. /// The `generatingProcess` concept does not define variant-level callbacks, /// so this function always returns `nullptr`. /// /// @tparam Capability Encoding capability index /// @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 Always `nullptr` /// template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This callback is invoked to determine whether the /// `generatingProcess` concept should be activated for a given /// encoding request. /// /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Matcher function pointer /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &generatingProcessMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/generating-process/generatingProcessEncoding.h0000664000175000017500000002137715232567717033600 0ustar alastairalastair/* * (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 generatingProcessEncoding.h /// @brief Implementation of the GRIB `generatingProcess` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **generatingProcess concept** within the mars2grib backend. /// /// The generatingProcess concept is responsible for populating GRIB keys /// related to the *origin and nature of the data generation process*, including: /// /// - `backgroundProcess` /// - `generatingProcessIdentifier` /// - `typeOfGeneratingProcess` /// /// These keys are encoded in the Product Definition Section and are tightly /// coupled to both MARS semantics and GRIB code tables. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `generatingProcessApplicable` /// - Delegation of semantic resolution to dedicated deduction functions /// - Explicit handling of legacy encoder behavior /// - 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 // System includes #include #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/generating-process/generatingProcessEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Tables #include "metkit/mars2grib/backend/tables/backgroundProcess.h" #include "metkit/mars2grib/backend/tables/typeOfGeneratingProcess.h" // Deductions #include "metkit/mars2grib/backend/deductions/backgroundProcess.h" #include "metkit/mars2grib/backend/deductions/generatingProcessIdentifier.h" #include "metkit/mars2grib/backend/deductions/typeOfGeneratingProcess.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 `generatingProcess` concept. /// /// This predicate determines whether the generatingProcess 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 ensure that only valid concept instantiations occur. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Generating process concept variant /// /// @return `true` if the concept is applicable for the given parameters, /// `false` otherwise. /// /// @note /// The default applicability rule enables the concept when: /// - the encoding stage is `StagePreset`, **or** /// - the concept variant is `GeneratingProcessType::Default` /// - and the GRIB section is `SecIdentificationSection` /// /// This permissive rule reflects the historical behavior of the encoder and /// allows the concept to participate in multiple encoding paths. /// template constexpr bool generatingProcessApplicable() { return ((Section == SecProductDefinitionSection) && (Stage == StagePreset) && (Variant == GeneratingProcessType::Default)); } /// /// @brief Execute the generatingProcess concept operation. /// /// This function implements the runtime logic of the GRIB `generatingProcess` /// concept. When applicable, it: /// /// 1. Resolves the background process associated with the data. /// 2. Optionally resolves the generating process identifier. /// 3. Optionally resolves the type of generating process. /// /// The current implementation contains **explicit legacy compatibility paths** /// that mirror the behavior of the previous encoder, including reliance on /// pre-existing GRIB header state and ecCodes side effects. /// /// These paths are clearly marked and must be removed in a future cleanup /// iteration. /// /// 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 Generating process 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: /// - any deduction fails /// - a required GRIB key cannot be set /// - the concept is invoked outside its applicability domain /// /// @note /// - All runtime errors are wrapped with full concept context /// (concept name, variant, stage, section). /// - Several code paths are intentionally marked as legacy and must not /// be extended. /// /// @todo [owner: mds,dgov][scope: concept][reason: legacy][prio: high] /// - Remove all reliance on `std::optional` forwarding. /// - Remove reliance on ecCodes implicit behavior. /// - Enforce explicit defaults, mandatory values, or hard failures. /// /// @see generatingProcessApplicable /// template void GeneratingProcessOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { using metkit::mars2grib::backend::tables::TypeOfGeneratingProcess; using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (generatingProcessApplicable()) { try { MARS2GRIB_LOG_CONCEPT(generatingProcess); // Retrieve the information std::optional generatingProcessIdentifier = deductions::resolve_GeneratingProcessIdentifier_opt(mars, par, opt); std::optional typeOfGeneratingProcess = deductions::resolve_TypeOfGeneratingProcess_opt(mars, par, opt); tables::BackgroundProcess backgroundProcess = deductions::resolve_BackgroundProcess_or_throw(mars, par, opt); set_or_throw(out, "backgroundProcess", static_cast(backgroundProcess)); /// @todo [owner: mds,dgov][scope: concept][reason: legacy][prio: high] /// Remove this logic. /// /// Deductions must not forward `std::optional` values directly. /// A proper deduction must: /// - set an explicit value (e.g. `Missing`), /// - apply a DGOV-approved default, /// - or throw if the value is mandatory. if (generatingProcessIdentifier.has_value()) { set_or_throw(out, "generatingProcessIdentifier", generatingProcessIdentifier.value()); } /// @todo [owner: mds,dgov][scope: concept][reason: legacy][prio: high] /// Remove this logic. /// /// Relying on pre-existing GRIB header values is not reproducible /// and must be eliminated. if (typeOfGeneratingProcess.has_value()) { set_or_throw(out, "typeOfGeneratingProcess", static_cast(typeOfGeneratingProcess.value())); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(generatingProcess, "Unable to set `generatingProcess` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(generatingProcess, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/strike-probability/0000775000175000017500000000000015232567717026304 5ustar alastairalastair././@LongLink0000644000000000000000000000015400000000000011603 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityConceptDescriptor.hmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityConceptDescr0000664000175000017500000001332115232567717034226 0ustar alastairalastair/* * (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 strikeProbabilityConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `strikeProbability` concept. /// /// This header defines `StrikeProbabilityConcept`, the **compile-time descriptor** /// that registers the GRIB `strikeProbability` 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/strike-probability/strikeProbabilityEncoding.h" #include "metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityEnum.h" #include "metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityMatcher.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 `strikeProbability` concept. /// /// `StrikeProbabilityConcept` registers the GRIB `strikeProbability` 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 StrikeProbabilityConcept : 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 strikeProbabilityName; } /// /// @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 strikeProbabilityTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `strikeProbability` 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 (strikeProbabilityApplicable()) { return &StrikeProbabilityOp; } 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 &strikeProbabilityMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityEncoding.h0000664000175000017500000001124115232567717033625 0ustar alastairalastair/* * (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 strikeProbabilityEncoding.h /// @brief Implementation of the GRIB `strikeProbability` concept operation. /// /// This header defines the **strikeProbability concept** infrastructure within /// the mars2grib backend. /// /// The strikeProbability concept currently exists only as a registered semantic /// placeholder: /// - it defines the standard compile-time concept interface, /// - it participates in the registry infrastructure, /// - it is intentionally inactive until matching and encoding rules are defined. /// /// This implementation therefore follows the conservative placeholder model: /// - Compile-time applicability via `strikeProbabilityApplicable` /// - No active encoding domain /// - Explicit failure if the concept is invoked unexpectedly /// /// @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/strike-probability/strikeProbabilityEnum.h" #include "metkit/mars2grib/utils/generalUtils.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 `strikeProbability` concept. /// /// The strikeProbability concept is intentionally inactive until its runtime /// semantics are defined. /// /// This predicate always evaluates to `false` and exists to satisfy the /// uniform concept interface expected by the concept dispatcher. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Strike-probability concept variant /// /// @return Always `false`. /// template constexpr bool strikeProbabilityApplicable() { return false; } /// /// @brief Execute the `strikeProbability` concept operation. /// /// This function implements the registered runtime hook for the /// `strikeProbability` concept. /// /// Because the concept is currently a placeholder, it must never perform any /// encoding and any invocation is treated as a programming error. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Strike-probability 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 (unused) /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// @param[out] out Output GRIB dictionary (unused) /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribConceptException /// Always thrown if this function is invoked. /// /// @note /// This function intentionally does not provide a silent no-op. /// Any invocation is treated as an unexpected use of incomplete concept logic. /// template void StrikeProbabilityOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) noexcept(false) { using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (strikeProbabilityApplicable()) { // Debug output MARS2GRIB_LOG_CONCEPT(strikeProbability); // Successful no-op return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(strikeProbability, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityEnum.h0000664000175000017500000001103715232567717033006 0ustar alastairalastair/* * (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 strikeProbabilityEnum.h /// @brief Definition of the `strikeProbability` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `strikeProbability` /// concept used by the mars2grib backend. It contains: /// /// - the canonical concept name (`strikeProbabilityName`) /// - the enumeration of supported strike-probability variants (`StrikeProbabilityType`) /// - a compile-time typelist of all variants (`StrikeProbabilityList`) /// - 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 /// `strikeProbabilityEncoding.h` 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 `strikeProbability` 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 `strikeProbability` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view strikeProbabilityName{"strikeProbability"}; /// /// @brief Enumeration of all supported `strikeProbability` concept variants. /// /// The concept currently defines a single default variant because the /// runtime matching and encoding logic is intentionally left unspecified. /// /// 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 /// Additional variants may be introduced later once the strike-probability /// semantics are finalized. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class StrikeProbabilityType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `strikeProbability` 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 StrikeProbabilityList = ValueList; /// /// @brief Compile-time mapping from `StrikeProbabilityType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given strike-probability variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Strike-probability 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 strikeProbabilityTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view strikeProbabilityTypeName() { \ return NAME; \ } DEF(StrikeProbabilityType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityMatcher.h0000664000175000017500000000640215232567717033465 0ustar alastairalastair/* * (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 strikeProbabilityMatcher.h /// @brief Entry-level matcher for the GRIB `strikeProbability` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether strike-probability semantics are active for a request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// The current implementation intentionally keeps the concept inactive until /// matching semantics are defined. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/strike-probability/strikeProbabilityEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/paramMatcher.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `strikeProbability` concept variant. /// /// The strikeProbability concept is currently inactive and therefore never /// selects a variant. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local `StrikeProbabilityType` variant index or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t strikeProbabilityMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; using metkit::mars2grib::utils::dict_traits::get_or_throw; const auto param = get_or_throw(mars, "param"); if (matchAny(param, 131060, 131061, 131062, 131063, 131064, 131065, 131066, 131067, 131068, 131069, 131070, 131071, 131072, 131073, range(131074, 131077), 131085, 131089, 131090, 131091, 131098, 131099, 131100, 133093, 133094, 133095, 133096, 133097, 133098)) { return static_cast(StrikeProbabilityType::Default); } else { return compile_time_registry_engine::MISSING; } } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `strikeProbability` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/param/0000775000175000017500000000000015232567717023565 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/param/paramEncoding.h0000664000175000017500000001332015232567717026504 0ustar alastairalastair/* * (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 paramEncoding.h /// @brief Implementation of the GRIB `param` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **param concept** within the mars2grib backend. /// /// The param concept is responsible for resolving and encoding the GRIB /// parameter identifier (`paramId`) in the *Product Definition Section*. /// The value is deduced from the MARS and parameter dictionaries using /// dedicated deduction logic. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `paramApplicable` /// - Runtime deduction of the parameter identifier /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/param/paramEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/paramId.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 `param` concept. /// /// The default applicability enables this concept only when: /// - `Variant == ParamType::ParamId` /// - `Stage == StagePreset` or `Stage == StageRuntime` /// - `Section == SecProductDefinitionSection` /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Parameter concept variant /// /// @return `true` if the concept is applicable for the given parameters, /// `false` otherwise. /// /// @note /// The concept is intentionally enabled both at preset and runtime stages /// to allow late binding of the parameter identifier if required. /// template constexpr bool paramApplicable() { // Confitions to apply concept return ((Variant == ParamType::ParamId) && (Stage == StagePreset || Stage == StageRuntime) && (Section == SecProductDefinitionSection)); } /// /// @brief Execute the `param` concept operation. /// /// This function implements the runtime logic of the GRIB `param` concept. /// When applicable, it: /// /// 1. Deduces the GRIB parameter identifier (`paramId`) from the input /// MARS and parameter dictionaries. /// 2. Encodes the resolved `paramId` into the GRIB 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 Parameter 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 concept is called when not applicable /// - the parameter identifier cannot be resolved /// - any encoding step fails /// /// @note /// - This concept performs no implicit defaulting. /// - The resolved `paramId` is expected to be fully validated by the /// underlying deduction logic. /// /// @see paramApplicable /// template void ParamOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { using metkit::mars2grib::utils::dict_traits::dump_or_ignore; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (paramApplicable()) { long paramId; try { MARS2GRIB_LOG_CONCEPT(param); // Deductions paramId = deductions::resolve_ParamId_or_throw(mars, par, opt); // Encoding set_or_throw(out, "paramId", paramId); } catch (...) { dump_or_ignore(out, "mars2grib_param_concept_failure.grib"); MARS2GRIB_CONCEPT_RETHROW( param, [&]() { return "Unable to set `param` concept with paramId=" + std::to_string(paramId); }()); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(param, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/param/paramMatcher.h0000664000175000017500000000434215232567717026345 0ustar alastairalastair/* * (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 paramMatcher.h /// @brief Entry-level matcher for the GRIB `param` concept. /// /// This header defines the runtime matcher used by the concept registry to /// activate the parameter-identity concept variant. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/param/paramEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `param` concept variant. /// /// The parameter concept is always active and resolves to `ParamType::ParamId`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `ParamType::ParamId`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t paramMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { return static_cast(ParamType::ParamId); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException("Unable to match `param` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/param/paramConceptDescriptor.h0000664000175000017500000001166015232567717030415 0ustar alastairalastair/* * (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 paramConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `param` concept. /// /// This header defines `ParamConcept`, the **compile-time descriptor** /// that registers the GRIB `param` 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/param/paramEncoding.h" #include "metkit/mars2grib/backend/concepts/param/paramEnum.h" #include "metkit/mars2grib/backend/concepts/param/paramMatcher.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 `param` concept. /// /// `ParamConcept` registers the GRIB `param` concept into the /// compile-time registry engine. /// struct ParamConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return paramName; } /// /// @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 paramTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `param` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (paramApplicable()) { return &ParamOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `param` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return ¶mMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/param/paramEnum.h0000664000175000017500000001030115232567717025656 0ustar alastairalastair/* * (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 paramEnum.h /// @brief Definition of the `param` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `param` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`paramName`) /// - the enumeration of supported parameter variants (`ParamType`) /// - a compile-time typelist of all variants (`ParamList`) /// - 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 /// `paramEncoding.h` 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 `param` 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 `param` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view paramName{"param"}; /// /// @brief Enumeration of all supported `param` concept variants. /// /// Each enumerator represents a specific parameter identification /// mechanism 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 and currently reflects /// parameter identification via paramId. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class ParamType : std::size_t { ParamId = 0 }; /// /// @brief Compile-time list of all `param` 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 ParamList = ValueList; /// /// @brief Compile-time mapping from `ParamType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given parameter variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Parameter 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 paramTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view paramTypeName() { \ return NAME; \ } DEF(ParamType::ParamId, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/longrange/0000775000175000017500000000000015232567717024441 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/longrange/longrangeEncoding.h0000664000175000017500000001436015232567717030241 0ustar alastairalastair/* * (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 longrangeEncoding.h /// @brief Implementation of the GRIB `longrange` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **longrange concept** within the mars2grib backend. /// /// The longrange 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 `longrangeApplicable` /// - 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/longrange/longrangeEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/methodNumber.h" #include "metkit/mars2grib/backend/deductions/systemNumber.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 `longrange` concept. /// /// This predicate determines whether the longrange 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 Longrange 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 == LongrangeType::Default` /// - `Stage == StagePreset` /// - `Section == SecLocalUseSection` /// template constexpr bool longrangeApplicable() { return ((Variant == LongrangeType::Default) && (Stage == StagePreset) && (Section == SecLocalUseSection)); } /// /// @brief Execute the `longrange` concept operation. /// /// This function implements the runtime logic of the GRIB `longrange` 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 Longrange 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 longrangeApplicable /// template void LongrangeOp(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 (longrangeApplicable()) { try { MARS2GRIB_LOG_CONCEPT(longrange); // Preconditions / contracts validation::match_LocalDefinitionNumber_or_throw(opt, out, {15L}); // Deductions auto methodVal = deductions::resolve_MethodNumber_or_throw(mars, par, opt); auto systemVal = deductions::resolve_SystemNumber_or_throw(mars, par, opt); // Encoding set_or_throw(out, "methodNumber", methodVal); set_or_throw(out, "systemNumber", systemVal); } catch (...) { MARS2GRIB_CONCEPT_RETHROW(longrange, "Unable to set `longrange` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(longrange, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/longrange/longrangeEnum.h0000664000175000017500000001052515232567717027416 0ustar alastairalastair/* * (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 longrangeEnum.h /// @brief Definition of the `longrange` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `longrange` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`longrangeName`) /// - the enumeration of supported long-range variants (`LongrangeType`) /// - a compile-time typelist of all variants (`LongrangeList`) /// - 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 /// `longrangeEncoding.h` 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 `longrange` 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 `longrange` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view longrangeName{"longrange"}; /// /// @brief Enumeration of all supported `longrange` 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 LongrangeType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `longrange` 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 LongrangeList = ValueList; /// /// @brief Compile-time mapping from `LongrangeType` 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 longrangeTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view longrangeTypeName() { \ return NAME; \ } DEF(LongrangeType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/longrange/longrangeConceptDescriptor.h0000664000175000017500000001300615232567717032141 0ustar alastairalastair/* * (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 longrangeConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `longrange` concept. /// /// This header defines `LongrangeConcept`, the **compile-time descriptor** /// that registers the GRIB `longrange` 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/longrange/longrangeEncoding.h" #include "metkit/mars2grib/backend/concepts/longrange/longrangeEnum.h" #include "metkit/mars2grib/backend/concepts/longrange/longrangeMatcher.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 `longrange` concept. /// /// `LongrangeConcept` registers the GRIB `longrange` 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 LongrangeConcept : 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 longrangeName; } /// /// @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 longrangeTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `longrange` 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 (longrangeApplicable()) { return &LongrangeOp; } 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 &longrangeMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/longrange/longrangeMatcher.h0000664000175000017500000000531615232567717030077 0ustar alastairalastair/* * (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 longrangeMatcher.h /// @brief Entry-level matcher for the GRIB `longrange` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether long-range forecast system metadata is active for a request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/backend/concepts/longrange/longrangeEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `longrange` concept variant. /// /// The concept is active as `LongrangeType::Default` when both `method` and /// `system` are present in the MARS request. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `LongrangeType::Default`, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t longrangeMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::has; if (has(mars, "method") && has(mars, "system")) { return static_cast(LongrangeType::Default); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `longrange` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/destine/0000775000175000017500000000000015232567717024120 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/destine/destineConceptDescriptor.h0000664000175000017500000001453715232567717031311 0ustar alastairalastair/* * (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 destineConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `destine` concept. /// /// This header defines `DestineConcept`, the **compile-time descriptor** /// that registers the GRIB `destine` 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/destine/destineEncoding.h" #include "metkit/mars2grib/backend/concepts/destine/destineEnum.h" #include "metkit/mars2grib/backend/concepts/destine/destineMatcher.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 `destine` concept. /// /// `DestineConcept` registers the GRIB `destine` 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 DestineConcept : 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 destineName; } /// /// @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 destineTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `destine` 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 (destineApplicable()) { return &DestineOp; } else { return nullptr; } } else { return nullptr; } // Avoid compiler warnings mars2gribUnreachable(); } /// /// @brief Variant-specific callbacks (not used for this concept). /// /// This hook is provided for completeness of the registry interface. /// The `destine` concept does not define variant-level callbacks, /// so this function always returns `nullptr`. /// /// @tparam Capability Encoding capability index /// @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 Always `nullptr` /// template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This callback is invoked to determine whether the `destine` /// concept should be activated for a given encoding request. /// /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Matcher function pointer /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &destineMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/destine/destineEncoding.h0000664000175000017500000002203015232567717027370 0ustar alastairalastair/* * (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 destineEncoding.h /// @brief Implementation of the GRIB `destine` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **DestinE concept** within the mars2grib backend. /// /// The concept is responsible for populating GRIB keys in the /// *Local Use Section* associated with **Destination Earth (DestinE) datasets**, /// including: /// /// - dataset identification (`dataset`) /// - experiment metadata (`activity`, `experiment`) /// - model and resolution identifiers /// - ensemble realization and generation indices /// /// The behavior of the concept depends on the selected DestinE variant: /// /// - `DestineType::ClimateDT` /// - `DestineType::ExtremesDT` /// /// Each variant enforces strict dataset consistency and encodes a /// different subset of metadata keys. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `destineApplicable` /// - Runtime validation of Local Use Section constraints /// - Delegation of semantic resolution to dedicated deduction functions /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/destine/destineEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/activity.h" #include "metkit/mars2grib/backend/deductions/experiment.h" #include "metkit/mars2grib/backend/deductions/generation.h" #include "metkit/mars2grib/backend/deductions/model.h" #include "metkit/mars2grib/backend/deductions/realization.h" #include "metkit/mars2grib/backend/deductions/resolution.h" // checks #include "metkit/mars2grib/backend/checks/checkDestinELocalSection.h" #include "metkit/mars2grib/backend/checks/matchDataset.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 `destine` concept. /// /// This predicate determines whether the DestinE concept is applicable /// for a given combination of: /// - encoding stage /// - GRIB section /// - DestinE variant /// /// Applicability is evaluated entirely at compile time and is used by the /// concept dispatcher to ensure that only valid concept instantiations occur. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant DestinE 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` is either `ClimateDT` or `ExtremesDT` /// - `Stage == StagePreset` /// - `Section == SecLocalUseSection` /// /// This reflects the current DestinE GRIB encoding requirements. /// template constexpr bool destineApplicable() { // Confitions to apply concept return ((Variant == DestineType::ClimateDT || Variant == DestineType::ExtremesDT) && (Stage == StagePreset) && (Section == SecLocalUseSection)); } /// /// @brief Execute the DestinE concept operation. /// /// This function implements the runtime logic of the GRIB `destine` concept. /// When applicable, it: /// /// 1. Validates that the Local Use Section is compatible with DestinE encoding. /// 2. Enforces dataset consistency based on the selected DestinE variant. /// 3. Deduces DestinE-specific metadata from MARS and parameter dictionaries. /// 4. Encodes the corresponding GRIB keys in the output dictionary. /// /// The concept supports two variants: /// /// - **ExtremesDT** /// - Enforces dataset `"extremes-dt"` /// - Encodes only the dataset identifier /// /// - **ClimateDT** /// - Enforces dataset `"climate-dt"` /// - Encodes activity, experiment, resolution, model, /// generation, and realization metadata /// /// 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 DestinE 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 Use Section does not match DestinE requirements /// - dataset consistency checks fail /// - any deduction fails /// - any GRIB key cannot be set /// /// @note /// - All runtime errors are wrapped with full concept context /// (concept name, variant, stage, section). /// - This concept does not rely on any pre-existing GRIB header state. /// /// @see destineApplicable /// template void DestineOp(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 (destineApplicable()) { try { MARS2GRIB_LOG_CONCEPT(destine); // ============================================================= // Structural validation // ============================================================= validation::check_DestinELocalSection_or_throw(opt, out); // ============================================================= // Variant-specific logic // ============================================================= if constexpr (Variant == DestineType::ExtremesDT) { // Enforce dataset consistency validation::match_Dataset_or_throw(opt, out, "extremes-dt"); // Encode dataset identifier set_or_throw(out, "dataset", "extremes-dt"); } else if constexpr (Variant == DestineType::ClimateDT) { // Enforce dataset consistency validation::match_Dataset_or_throw(opt, out, "climate-dt"); // Encode dataset identifier set_or_throw(out, "dataset", "climate-dt"); // Deductions std::string activityVal = deductions::resolve_Activity_or_throw(mars, par, opt); std::string experimentVal = deductions::resolve_Experiment_or_throw(mars, par, opt); std::string resolutionVal = deductions::resolve_Resolution_or_throw(mars, par, opt); std::string modelVal = deductions::resolve_Model_or_throw(mars, par, opt); long generationVal = deductions::resolve_Generation_or_throw(mars, par, opt); long realizationVal = deductions::resolve_Realization_or_throw(mars, par, opt); // Encoding set_or_throw(out, "activity", activityVal); set_or_throw(out, "experiment", experimentVal); set_or_throw(out, "resolution", resolutionVal); set_or_throw(out, "model", modelVal); set_or_throw(out, "generation", generationVal); set_or_throw(out, "realization", realizationVal); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(destine, "Unable to set `destine` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(destine, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/destine/destineEnum.h0000664000175000017500000001052715232567717026556 0ustar alastairalastair/* * (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 destineEnum.h /// @brief Definition of the `destine` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `destine` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`destineName`) /// - the enumeration of supported DESTINe variants (`DestineType`) /// - a compile-time typelist of all variants (`DestineList`) /// - 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 /// `destineEncoding.h` 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 `destine` 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 `destine` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view destineName{"destine"}; /// /// @brief Enumeration of all supported `destine` concept variants. /// /// Each enumerator represents a specific DESTINe data stream or /// processing context 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 concise and reflects the /// current DESTINe use cases supported by the encoder. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class DestineType : std::size_t { ClimateDT = 0, ExtremesDT }; /// /// @brief Compile-time list of all `destine` 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 DestineList = ValueList; /// /// @brief Compile-time mapping from `DestineType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given DESTINe variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T DESTINe 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 destineTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view destineTypeName() { \ return NAME; \ } DEF(DestineType::ClimateDT, "climateDT"); DEF(DestineType::ExtremesDT, "extremesDT"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/destine/destineMatcher.h0000664000175000017500000000744015232567717027235 0ustar alastairalastair/* * (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 destineMatcher.h /// @brief Entry-level matcher for the GRIB `destine` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether DestinE digital-twin metadata is active for a MARS request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/destine/destineEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `destine` concept variant. /// /// The concept is active for class `d1` requests without `anoffset`. The /// `dataset` keyword selects the concrete DestinE variant. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local DestinE variant index, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If required DestinE metadata is missing, unsupported `dataset` values are /// encountered, or lower-level matcher evaluation fails. Lower-level exceptions /// are preserved through `std::throw_with_nested`. /// template std::size_t destineMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribMatcherException; if (!has(mars, "anoffset") && get_or_throw(mars, "class") == "d1") { if (has(mars, "dataset")) { if (get_or_throw(mars, "dataset") == "extremes-dt") { return static_cast(DestineType::ExtremesDT); } else if (get_or_throw(mars, "dataset") == "climate-dt") { return static_cast(DestineType::ClimateDT); } else { throw Mars2GribMatcherException{"Unknown value \"" + get_or_throw(mars, "dataset") + "\" for mars keyword \"dataset\"!", Here()}; } } else { throw Mars2GribMatcherException{ "Missing required mars keyword \"dataset\" for class \"d1\" without \"anoffset\"!", Here()}; } } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `destine` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/concepts.md0000664000175000017500000001705315232567717024633 0ustar alastairalastair# Concepts This document describes the **concept system** used in the *mars2grib* backend and how concepts integrate with the compile-time registry engine. The goal of the concept layer is to provide a **declarative, type-safe, and fully compile-time description** of all semantic dimensions that participate in GRIB header construction, matching, and encoding. --- ## 1. What is a Concept? A **concept** represents one *semantic axis* of a GRIB message. Examples include: * parameter identity (e.g. temperature, wind) * vertical level definition * time semantics * grid representation * data packing * originating centre Each concept answers the question: > *Which variant of this semantic dimension applies to the current request?* Concepts are **independent** of each other and are composed together by the encoder to form a complete GRIB message. --- ## 2. Concept Variants Each concept defines a **finite set of variants**, usually represented by a scoped enum. Examples: * `LevelType::{surface, pressure, model}` * `RepresentationType::{regular_ll, reduced_gaussian}` * `PackingType::{simple, jpeg, ccsds}` Variants are: * closed and known at compile time * ordered (the order is semantically significant) * mapped to GRIB template numbers and encoding rules The ordered list of variants for a concept defines its **local variant index space**. --- ## 3. New Concept vs New Variant When changing the concept system, first decide whether the requested behavior is a new concept or a new variant of an existing concept. Use a **new concept** when the feature is an independent semantic axis that must be composable with other concepts. Use a **new variant** when the feature is an alternative realization inside an existing semantic axis and does not require independent composability. This distinction is often a domain decision and is usually not reliably deducible from the code alone. If a request does not explicitly state whether a new concept or a new variant is required, ask before implementing. --- ## 4. Level Concept Guardrail The `level` concept is one of the most constrained concepts in mars2grib. Although GRIB ultimately represents vertical levels through six low-level fixed-surface keys: * `typeOfFirstFixedSurface` * `scaleFactorOfFirstFixedSurface` * `scaledValueOfFirstFixedSurface` * `typeOfSecondFixedSurface` * `scaleFactorOfSecondFixedSurface` * `scaledValueOfSecondFixedSurface` mars2grib must not set these keys directly. Many combinations of these keys are syntactically possible but semantically meaningless for ECMWF products. Instead, the encoder must rely on the official level abstraction: * `typeOfLevel` * `level`, when required * `topLevel` / `bottomLevel`, when required * PV-array data, when required Each supported `typeOfLevel` corresponds to a `LevelType` variant, apart from a few virtual type-of-level values kept in mars2grib because they cannot be added to ecCodes for backward-compatibility reasons. Each variant maps to a prescribed configuration of the low-level fixed-surface keys. Do not implement level fixes by injecting `typeOfFirstFixedSurface`, `scaleFactorOfFirstFixedSurface`, `scaledValueOfFirstFixedSurface`, `typeOfSecondFixedSurface`, `scaleFactorOfSecondFixedSurface`, or `scaledValueOfSecondFixedSurface`. If a new level behavior is required, add or adjust the appropriate `LevelType` variant, matcher mapping, or deduction so the level remains encoded through `typeOfLevel` and the official level interface. --- ## 5. Concept Descriptor Contract Each concept is implemented as a **descriptor type** that conforms to the `RegisterEntryDescriptor` interface. At minimum, a concept descriptor provides: * a variant enum type * a compile-time list of variants (`VariantList`) * a human-readable concept name * human-readable variant names * optional dispatch functions for: * matching * variant-level encoding * phase-level encoding The descriptor contains **no runtime state** and no virtual functions. --- ## 6. Capabilities Concepts may expose multiple independent *capabilities*. A capability is identified by a compile-time `std::size_t` index and allows the same concept to participate in different registries, such as: * semantic matching * encoding * validation * diagnostics Capabilities are selected at registry instantiation time. This avoids duplicating concept descriptors while still allowing multiple independent dispatch planes. --- ## 7. The Concept Universe (`AllConcepts`) All concepts known to the system are aggregated into a single ordered typelist: ``` AllConcepts = TypeList ``` This list is the **root input** to the compile-time registry engine. The order of concepts in this list: * defines the concept identifier (`conceptId`) * defines the layout of the global variant index space * determines the ordering of all generated dispatch tables Changing this order is a **breaking structural change**. --- ## 8. Concept Identifiers Each concept is assigned a **stable numeric identifier** based on its position in `AllConcepts`. ``` conceptId = index in AllConcepts ``` Concept identifiers are: * contiguous * zero-based * compile-time constants They are used as indices into: * matching callback tables * concept metadata tables * diagnostic utilities --- ## 9. Variant Index Spaces Variants are indexed in two ways: ### Local variant index The index of a variant *within its concept*. ``` localIndex(variant) ``` ### Global variant index A flattened index across **all concepts and all variants**. ``` globalIndex = conceptOffset + localIndex ``` The global variant index is the primary key used by: * encoding callback registries * phase dispatch tables * encoding plans --- ## 10. Matching Phase Matching determines **which concepts and variants are active** for a given input request. Each concept may optionally provide a matcher function: ``` Fm(MarsDict, OptDict) -> variantId | MISSING ``` The matching phase: 1. iterates over all concepts 2. invokes the corresponding matcher (if present) 3. records the active variant (or absence thereof) The result is an `ActiveConceptsData` structure. --- ## 11. Encoding Phases Encoding is divided into **logical stages**, such as: * allocation * preset * override * runtime For each active variant, the encoder may execute zero or more callbacks per (stage, section) pair. This produces a three-dimensional dispatch space: ``` [variant][stage][section] -> Fn | nullptr ``` All dispatch tables are generated **entirely at compile time**. --- ## 12. Design Principles The concept system is designed around the following principles: * **No runtime polymorphism** * **No dynamic allocation** * **Deterministic structure** * **Compile-time validation** * **Separation of concerns** Concepts describe *what exists*. Registries describe *how things are wired*. Execution code performs *only iteration and invocation*. --- ## 13. Adding a New Concept To add a new concept: 1. Implement a new descriptor conforming to `RegisterEntryDescriptor` 2. Define its variant enum and `VariantList` 3. Provide matching and/or encoding callbacks as needed 4. Include the descriptor header in `AllConcepts.h` 5. Append the concept to the `AllConcepts` typelist No registry code needs to be modified. --- ## 14. Summary Concepts are the **semantic backbone** of the mars2grib backend. They provide: * a declarative description of the domain * compile-time structure and guarantees * a clean separation between semantics and execution All higher-level mechanisms — matching, layout resolution, encoding — are built mechanically on top of the concept system. metkit-1.19.2/src/metkit/mars2grib/backend/concepts/composition/0000775000175000017500000000000015232567717025030 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/composition/compositionEncoding.h0000664000175000017500000001602415232567717031216 0ustar alastairalastair/* * (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 compositionEncoding.h /// @brief Implementation of the GRIB `composition` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **composition concept** within the mars2grib backend. /// /// The concept is responsible for populating GRIB keys related to the /// *composition* of the encoded product, specifically the /// `constituentType` key in the Product Definition Section. /// /// The composition concept is variant-driven. Different variants correspond /// to different composition semantics (e.g. chemical constituents), and /// only selected variants perform encoding actions. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `compositionApplicable` /// - Variant-specific runtime deduction /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/composition/compositionEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/chemId.h" #include "metkit/mars2grib/backend/deductions/firstWavelength.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 `composition` concept. /// /// This predicate determines whether the `composition` 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 ensure that only valid concept instantiations occur. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Composition 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: /// - `Stage == StagePreset` /// - `Section == SecProductDefinitionSection` /// /// Variant-specific behavior is handled inside the concept operation. /// template constexpr bool compositionApplicable() { return (Stage == StagePreset && Section == SecProductDefinitionSection); } /// /// @brief Execute the `composition` concept operation. /// /// This function implements the runtime logic of the GRIB `composition` concept. /// When applicable, it: /// /// - Performs variant-specific deductions from the MARS and parameter dictionaries /// - Encodes the corresponding GRIB keys into the output dictionary /// /// Currently, only the `CompositionType::Chem` variant performs encoding, /// setting the GRIB key `constituentType`. /// /// 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 Composition 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 concept is called when not applicable /// - a deduction fails /// - a GRIB key cannot be set /// /// @note /// - All runtime errors are wrapped with full concept context /// (concept name, variant, stage, section). /// - Variants not explicitly handled result in a no-op when applicable. /// /// @see compositionApplicable /// template void CompositionOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (compositionApplicable()) { try { MARS2GRIB_LOG_CONCEPT(composition); // NOTE : Can also simply check if chemId is present, and if wavelength is present and set it. if constexpr (Variant == CompositionType::AerosolOptical) { // Deductions const auto chemId = deductions::resolve_ChemId_or_throw(mars, par, opt); const auto firstWavelength = deductions::resolve_FirstWavelength_or_throw(mars, par, opt); // Encoding set_or_throw(out, "enableChemSplit", true); set_or_throw(out, "chemId", chemId); set_or_throw(out, "firstWavelength", firstWavelength); } else if constexpr (Variant == CompositionType::Chem || Variant == CompositionType::Aerosol || Variant == CompositionType::ChemicalSource) { // Deductions const auto chemId = deductions::resolve_ChemId_or_throw(mars, par, opt); // Encoding set_or_throw(out, "enableChemSplit", true); set_or_throw(out, "chemId", chemId); } else { MARS2GRIB_CONCEPT_THROW(composition, "Concept variant is not implemented!"); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(composition, "Unable to set `composition` concept..."); } return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(composition, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/composition/compositionEnum.h0000664000175000017500000001163715232567717030401 0ustar alastairalastair/* * (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 compositionEnum.h /// @brief Definition of the `composition` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `composition` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`compositionName`) /// - the exhaustive enumeration of supported composition variants (`CompositionType`) /// - a compile-time typelist of all variants (`CompositionList`) /// - 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 /// `compositionEncoding.h` 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 `composition` 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 `composition` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view compositionName{"composition"}; /// /// @brief Enumeration of all supported `composition` concept variants. /// /// Each enumerator represents a distinct physical or semantic interpretation /// of atmospheric composition 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 includes both state variables (e.g. chemical or aerosol /// concentrations) and source-related representations. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class CompositionType : std::size_t { Chem = 0, Aerosol, AerosolOptical, ChemicalSource, AerosolOpticalSource, Default }; /// /// @brief Compile-time list of all `composition` 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 CompositionList = ValueList; /// /// @brief Compile-time mapping from `CompositionType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given composition variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Composition 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 compositionTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view compositionTypeName() { \ return NAME; \ } DEF(CompositionType::Chem, "chemical"); DEF(CompositionType::Aerosol, "aerosol"); DEF(CompositionType::AerosolOptical, "aerosolOptical"); DEF(CompositionType::ChemicalSource, "chemicalSource"); DEF(CompositionType::AerosolOpticalSource, "aerosolOpticalSource"); DEF(CompositionType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/composition/compositionConceptDescriptor.h0000664000175000017500000001471315232567717033125 0ustar alastairalastair/* * (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 compositionConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `composition` concept. /// /// This header defines `CompositionConcept`, the **compile-time descriptor** /// that registers the GRIB `composition` 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/composition/compositionEncoding.h" #include "metkit/mars2grib/backend/concepts/composition/compositionEnum.h" #include "metkit/mars2grib/backend/concepts/composition/compositionMatcher.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 `composition` concept. /// /// `CompositionConcept` registers the GRIB `composition` 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 CompositionConcept : 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 compositionName; } /// /// @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 compositionTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `composition` 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 (compositionApplicable()) { return &CompositionOp; } else { return nullptr; } } else { return nullptr; } // Avoid compiler warnings mars2gribUnreachable(); } /// /// @brief Variant-specific callbacks (not used for this concept). /// /// This hook is provided for completeness of the registry interface. /// The `composition` concept does not define variant-level callbacks, /// so this function always returns `nullptr`. /// /// @tparam Capability Encoding capability index /// @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 Always `nullptr` /// template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This callback is invoked to determine whether the `composition` /// concept should be activated for a given encoding request. /// /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Matcher function pointer /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &compositionMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/composition/compositionMatcher.h0000664000175000017500000001577615232567717031070 0ustar alastairalastair/* * (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 compositionMatcher.h /// @brief Entry-level matcher for the GRIB `composition` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether chemical or aerosol composition metadata is active for a /// MARS request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/composition/compositionEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/paramMatcher.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `composition` concept variant. /// /// The concept is active as `CompositionType::Chem` when `chem` is present, /// and as `CompositionType::Aerosol` when `wavelength` is present. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local composition variant index, or /// `compile_time_registry_engine::MISSING` when no composition metadata is active. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t compositionMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribMatcherException; const auto param = get_or_throw(mars, "param"); // TODO: This is the range for CAMS, there are some unmapped parameters that may need to be supported for ERA6, // etc. if (param < 400000 || param >= 500000) { return compile_time_registry_engine::MISSING; } const auto chem = get_or_throw(mars, "chem"); const auto hasWavelength = has(mars, "wavelength"); if (hasWavelength) { if (matchAny(param, 457000)) { if (matchAny(chem, range(900, 914), 918, 922, 923, range(933, 936))) { return static_cast(CompositionType::AerosolOptical); } } else if (matchAny(param, 458000, 459000, 460000, 461000, 462000, 472000)) { if (matchAny(chem, 922)) { return static_cast(CompositionType::AerosolOptical); } } } else { if (matchAny(param, 401000)) { if (matchAny(chem, range(900, 916))) { return static_cast(CompositionType::Aerosol); } if (matchAny(chem, 2, 3, range(5, 24), range(26, 30), range(32, 50), 52, 53, range(55, 58), range(63, 80), 82, 83, 85, 86, range(99, 101), 107, 112, 159, 161, 169, range(173, 178), range(186, 204), 222, range(224, 231), 233, 311, 359, 404, 917)) { return static_cast(CompositionType::Chem); } } else if (matchAny(param, 402000)) { if (matchAny(chem, range(900, 917), 924)) { return static_cast(CompositionType::Aerosol); } if (matchAny(chem, range(2, 24), range(26, 30), range(32, 50), 52, 53, range(55, 59), range(63, 80), 82, 83, 85, 86, range(99, 101), 107, 112, 118, 159, 161, 169, range(173, 178), range(186, 204), 222, range(224, 230), 233, 236, 311)) { return static_cast(CompositionType::Chem); } } else if (matchAny(param, 406000, 407000, 410000, 411000, 451000)) { if (matchAny(chem, range(901, 916))) { return static_cast(CompositionType::Aerosol); } } else if (matchAny(param, 453000)) { if (matchAny(chem, range(901, 916), 922)) { return static_cast(CompositionType::Aerosol); } } else if (matchAny(param, 400000)) { if (matchAny(chem, range(929, 931))) { return static_cast(CompositionType::Aerosol); } } else if (matchAny(param, 444000)) { if (matchAny(chem, 6, 8, 13, 15, 17, 19, 26, 27, 33)) { return static_cast(CompositionType::Chem); } } else if (matchAny(param, 445000)) { if (matchAny(chem, 6, 8, 13, 15, 17, 19, 27, 33, 236)) { 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)) { return static_cast(CompositionType::ChemicalSource); } } } throw Mars2GribMatcherException( "compositionMatcher: matching logic is not implemented for param=" + std::to_string(param) + ", chem=" + std::to_string(chem) + ", hasWavelength=" + (hasWavelength ? "true" : "false"), Here()); } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `composition` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/satellite/0000775000175000017500000000000015232567717024453 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/satellite/satelliteEncoding.h0000664000175000017500000002373315232567717030271 0ustar alastairalastair/* * (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 satelliteEncoding.h /// @brief Implementation of the GRIB `satellite` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **satellite concept**, which is responsible for encoding satellite-related /// metadata into GRIB messages. /// /// The concept populates keys related to: /// - satellite identification /// - instrument characteristics /// - spectral channel information /// /// The encoding is distributed across multiple GRIB sections and encoding /// stages, reflecting the structure of GRIB Product Definition Templates /// for satellite products. /// /// --- /// /// ## Encoding stages and sections /// /// The `satellite` concept operates in the following contexts: /// /// ### Local Use Section (Section 2) /// - **StagePreset** /// - Encodes the `channel` key /// - Validates the Local Definition Number /// /// ### Product Definition Section (Section 4) /// - **StageAllocate** /// - Allocates space for spectral band information /// - Sets `numberOfContributingSpectralBands` /// /// - **StagePreset** /// - Encodes satellite and instrument identifiers /// - Encodes spectral wave number information /// /// --- /// /// ## Supported Product Definition Templates /// /// The concept currently supports GRIB Product Definition Templates: /// /// - Template 32 /// - Template 33 /// /// Any other template results in a validation error. /// /// --- /// /// ## Deductions /// /// All satellite-related values are obtained via dedicated deduction /// functions, including: /// /// - channel /// - satellite series and number /// - instrument type /// - spectral wave number scaling /// /// The concept does not perform implicit defaults or fallbacks. /// Missing or inconsistent information results in a deduction error. /// /// --- /// /// ## Applicability model /// /// The satellite concept is applicable when **any** of the following holds: /// /// - `Stage == StagePreset` and `Section == SecLocalUseSection` /// - `Stage == StageAllocate` and `Section == SecProductDefinitionSection` /// - `Stage == StagePreset` and `Section == SecProductDefinitionSection` /// /// Variant-specific behavior (via `SatelliteType`) is currently not /// differentiated and may be refined in future iterations. /// /// --- /// /// ## Error handling /// /// - Structural mismatches with GRIB templates are detected via validation helpers. /// - All deduction and encoding errors are wrapped in a /// `Mars2GribConceptException` with full context. /// /// --- /// /// @note /// The namespace name `concepts_` is intentionally used instead of `concepts` /// to avoid conflicts with the C++20 `concept` language feature. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/satellite/satelliteEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Checks #include "metkit/mars2grib/backend/checks/matchLocalDefinitionNumber.h" #include "metkit/mars2grib/backend/checks/matchProductDefinitionTemplateNumber.h" // Deductions #include "metkit/mars2grib/backend/deductions/channel.h" #include "metkit/mars2grib/backend/deductions/instrumentType.h" #include "metkit/mars2grib/backend/deductions/numberOfFrequencies.h" #include "metkit/mars2grib/backend/deductions/satelliteNumber.h" #include "metkit/mars2grib/backend/deductions/satelliteSeries.h" #include "metkit/mars2grib/backend/deductions/scaleFactorOfCentralWaveNumber.h" #include "metkit/mars2grib/backend/deductions/scaledValueOfCentralWaveNumber.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 `satellite` concept. /// /// This predicate determines whether the `satellite` concept is instantiated /// for a given encoding stage and GRIB section. /// /// The concept is applicable in multiple stages and sections, reflecting /// the distribution of satellite metadata across the GRIB message: /// /// - Local Use Section during preset /// - Product Definition Section during allocation and preset /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Satellite concept variant /// /// @return `true` if the concept is applicable, `false` otherwise. /// template constexpr bool satelliteApplicable() { bool condition1 = (Stage == StagePreset && Section == SecLocalUseSection); bool condition2 = (Stage == StageAllocate && Section == SecProductDefinitionSection); bool condition3 = (Stage == StagePreset && Section == SecProductDefinitionSection); return (condition1 || condition2 || condition3); } /// /// @brief Execute the `satellite` concept operation. /// /// This function implements the runtime logic of the GRIB `satellite` concept. /// Depending on the encoding stage and GRIB section, it: /// /// - validates structural constraints /// - deduces satellite and instrument metadata /// - encodes satellite-related GRIB keys /// /// The operation is stage- and section-aware and performs different actions /// in each context. /// /// @tparam Stage Encoding stage /// @tparam Section GRIB section index /// @tparam Variant Satellite 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 concept is called when not applicable /// - GRIB structural validation fails /// - any required deduction fails /// - encoding of satellite metadata fails /// /// @note /// - The concept assumes that satellite products contribute exactly one /// spectral band. /// - All values are explicitly set; no reliance on pre-existing GRIB state /// is permitted. /// /// @see satelliteApplicable /// template void SatelliteOp(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 (satelliteApplicable()) { try { MARS2GRIB_LOG_CONCEPT(satellite); if constexpr (Section == SecLocalUseSection && Stage == StagePreset) { // Check/Validation validation::match_LocalDefinitionNumber_or_throw(opt, out, {24, 37}); // Deductions long channelNumber = deductions::resolve_Channel_or_throw(mars, par, opt); // long numberOfFrequencies = deductions::resolve_NumberOfFrequencies_or_throw(mars, par, opt); // Encoding set_or_throw(out, "channelNumber", channelNumber); // set_or_throw(out, "numberOfFrequencies", numberOfFrequencies); } if constexpr (Section == SecProductDefinitionSection && Stage == StageAllocate) { // Check/Validation validation::match_ProductDefinitionTemplateNumber_or_throw(opt, out, {32, 33}); // Encoding set_or_throw(out, "numberOfContributingSpectralBands", 1L); } if constexpr (Section == SecProductDefinitionSection && Stage == StagePreset) { // Check/Validation validation::match_ProductDefinitionTemplateNumber_or_throw(opt, out, {32, 33}); // Deductions long satelliteNumber = deductions::resolve_satelliteNumber_or_throw(mars, par, opt); long instrumentType = deductions::resolve_InstrumentType_or_throw(mars, par, opt); long satelliteSeries = deductions::resolve_SatelliteSeries_or_throw(mars, par, opt); long scaleFactorOfCentralWaveNumber = deductions::resolve_ScaleFactorOfCentralWaveNumber_or_throw(mars, par, opt); long scaledValueOfCentralWaveNumber = deductions::resolve_ScaledValueOfCentralWaveNumber_or_throw(mars, par, opt); // Encoding set_or_throw(out, "satelliteSeries", satelliteSeries); set_or_throw(out, "satelliteNumber", satelliteNumber); set_or_throw(out, "instrumentType", instrumentType); set_or_throw(out, "scaleFactorOfCentralWaveNumber", scaleFactorOfCentralWaveNumber); set_or_throw(out, "scaledValueOfCentralWaveNumber", scaledValueOfCentralWaveNumber); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(satellite, "Unable to set `satellite` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(satellite, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/satellite/satelliteConceptDescriptor.h0000664000175000017500000001203015232567717032161 0ustar alastairalastair/* * (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 satelliteConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `satellite` concept. /// /// This header defines `SatelliteConcept`, the **compile-time descriptor** /// that registers the GRIB `satellite` 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/satellite/satelliteEncoding.h" #include "metkit/mars2grib/backend/concepts/satellite/satelliteEnum.h" #include "metkit/mars2grib/backend/concepts/satellite/satelliteMatcher.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 `satellite` concept. /// /// `SatelliteConcept` registers the GRIB `satellite` concept into the /// compile-time registry engine. /// struct SatelliteConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return satelliteName; } /// /// @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 satelliteTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `satellite` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (satelliteApplicable()) { return &SatelliteOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `satellite` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &satelliteMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/satellite/satelliteEnum.h0000664000175000017500000001050015232567717027433 0ustar alastairalastair/* * (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 satelliteEnum.h /// @brief Definition of the `satellite` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `satellite` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`satelliteName`) /// - the enumeration of supported satellite variants (`SatelliteType`) /// - a compile-time typelist of all variants (`SatelliteList`) /// - 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 /// `satelliteEncoding.h` 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 `satellite` 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 `satellite` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view satelliteName{"satellite"}; /// /// @brief Enumeration of all supported `satellite` concept variants. /// /// Each enumerator represents a specific satellite-related semantic /// classification 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 satellite handling evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class SatelliteType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `satellite` 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 SatelliteList = ValueList; /// /// @brief Compile-time mapping from `SatelliteType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given satellite variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Satellite 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 satelliteTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view satelliteTypeName() { \ return NAME; \ } DEF(SatelliteType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/satellite/satelliteMatcher.h0000664000175000017500000000553615232567717030127 0ustar alastairalastair/* * (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 satelliteMatcher.h /// @brief Entry-level matcher for the GRIB `satellite` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether satellite product metadata is active for a MARS request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/satellite/satelliteEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `satellite` concept variant. /// /// The concept is active as `SatelliteType::Default` when `channel`, `ident`, /// and `instrument` are present in the MARS request. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `SatelliteType::Default`, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t satelliteMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; // Default satellite: requires full satellite identification keys if (has(mars, "channel") && has(mars, "ident") && has(mars, "instrument")) { return static_cast(SatelliteType::Default); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `satellite` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/packing/0000775000175000017500000000000015232567717024101 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/packing/packingEncoding.h0000664000175000017500000001743615232567717027350 0ustar alastairalastair/* * (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 packingEncoding.h /// @brief Implementation of the GRIB `packing` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **packing concept** within the mars2grib backend. /// /// The packing concept is responsible for configuring the GRIB /// *Data Representation Section* according to the selected packing algorithm. /// It validates the underlying data representation template and sets the /// required packing-specific GRIB keys. /// /// Supported packing variants include: /// - Simple packing /// - CCSDS packing /// - Spectral complex packing /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `packingApplicable` /// - Variant-specific runtime validation /// - Deduction of packing parameters from MARS and parameter dictionaries /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/packing/packingEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Checks #include "metkit/mars2grib/backend/checks/matchDataRepresentationTemplateNumber.h" // Deductions #include "metkit/mars2grib/backend/deductions/bitsPerValue.h" #include "metkit/mars2grib/backend/deductions/laplacianOperator.h" #include "metkit/mars2grib/backend/deductions/subSetTrunc.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 `packing` concept. /// /// The default applicability enables this concept only when: /// - `Stage == StagePreset` /// - `Section == SecDataRepresentationSection` /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Packing concept variant /// /// @return `true` if the concept is applicable for the given parameters, /// `false` otherwise. /// template constexpr bool packingApplicable() { if constexpr (Stage == StagePreset && Section == SecDataRepresentationSection) { return true; } return false; } /// /// @brief Execute the `packing` concept operation. /// /// When applicable, this concept: /// 1. Validates the GRIB data representation template number. /// 2. Deduces packing-specific parameters from the input dictionaries. /// 3. Encodes the corresponding GRIB keys into the output dictionary. /// /// The exact behavior depends on the selected packing variant: /// /// - **Simple packing** /// - Template: 0 /// - Keys set: `bitsPerValue` /// /// - **CCSDS packing** /// - Template: 42 /// - Keys set: `bitsPerValue` /// /// - **Spectral complex packing** /// - Template: 51 /// - Keys set: /// - `bitsPerValue` /// - `laplacianOperator` /// - `subSetJ`, `subSetK`, `subSetM` /// - `TS` /// /// 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 Packing 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 concept is called when not applicable /// - the GRIB data representation template does not match the variant /// - any deduction or encoding step fails /// /// @note /// This concept does not rely on any pre-existing GRIB header state. /// /// @see packingApplicable /// template void PackingOp(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 (packingApplicable()) { try { MARS2GRIB_LOG_CONCEPT(packing); // ============================================================= // Variant-specific logic // ============================================================= if constexpr (Variant == PackingType::Simple) { // Check sample structure validation::match_DataRepresentationTemplateNumber_or_throw(opt, out, {0}); // Set bits per value long bitsPerValue = deductions::resolve_BitsPerValueGridded_or_throw(mars, par, opt); set_or_throw(out, "bitsPerValue", bitsPerValue); } if constexpr (Variant == PackingType::Ccsds) { // Check sample structure validation::match_DataRepresentationTemplateNumber_or_throw(opt, out, {42}); // Set bits per value long bitsPerValue = deductions::resolve_BitsPerValueGridded_or_throw(mars, par, opt); set_or_throw(out, "bitsPerValue", bitsPerValue); } if constexpr (Variant == PackingType::SpectralComplex) { // Check sample structure validation::match_DataRepresentationTemplateNumber_or_throw(opt, out, {51}); // Set bits per value long bitsPerValue = deductions::resolve_BitsPerValueSpectral_or_throw(mars, par, opt); set_or_throw(out, "bitsPerValue", bitsPerValue); // double laplacianOperator = deductions::resolve_LaplacianOperator_or_throw(mars, par, opt); long subSetTruncation = deductions::resolve_SubSetTruncation_or_throw(mars, par, opt); // set_or_throw(out, "laplacianOperator", laplacianOperator); set_or_throw(out, "subSetJ", subSetTruncation); set_or_throw(out, "subSetK", subSetTruncation); set_or_throw(out, "subSetM", subSetTruncation); set_or_throw(out, "TS", (subSetTruncation + 1) * (subSetTruncation + 2)); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(packing, "Unable to set `packing` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(packing, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/packing/packingConceptDescriptor.h0000664000175000017500000001174415232567717031250 0ustar alastairalastair/* * (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 packingConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `packing` concept. /// /// This header defines `PackingConcept`, the **compile-time descriptor** /// that registers the GRIB `packing` 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/packing/packingEncoding.h" #include "metkit/mars2grib/backend/concepts/packing/packingEnum.h" #include "metkit/mars2grib/backend/concepts/packing/packingMatcher.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 `packing` concept. /// /// `PackingConcept` registers the GRIB `packing` concept into the /// compile-time registry engine. /// struct PackingConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return packingName; } /// /// @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 packingTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `packing` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (packingApplicable()) { return &PackingOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `packing` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &packingMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/packing/packingEnum.h0000664000175000017500000001075715232567717026525 0ustar alastairalastair/* * (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 packingEnum.h /// @brief Definition of the `packing` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `packing` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`packingName`) /// - the enumeration of supported packing variants (`PackingType`) /// - a compile-time typelist of all variants (`PackingList`) /// - 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 /// `packingEncoding.h` 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 `packing` 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 `packing` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view packingName{"packing"}; /// /// @brief Enumeration of all supported `packing` concept variants. /// /// Each enumerator represents a specific GRIB packing or compression /// strategy used to encode field values. /// /// 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 includes both grid-point and spectral packing /// representations, as well as a default fallback. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class PackingType : std::size_t { Simple = 0, Ccsds, SpectralComplex, Default }; /// /// @brief Compile-time list of all `packing` 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 PackingList = ValueList; /// /// @brief Compile-time mapping from `PackingType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given packing variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Packing 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 packingTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view packingTypeName() { \ return NAME; \ } DEF(PackingType::Simple, "simple"); DEF(PackingType::Ccsds, "ccsds"); DEF(PackingType::SpectralComplex, "spectral_complex"); DEF(PackingType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/packing/packingMatcher.h0000664000175000017500000000607215232567717027177 0ustar alastairalastair/* * (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 packingMatcher.h /// @brief Entry-level matcher for the GRIB `packing` concept. /// /// This header defines the runtime matcher used by the concept registry to /// select the GRIB data packing variant requested by MARS metadata. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/packing/packingEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `packing` concept variant. /// /// The matcher maps the MARS `packing` keyword onto the corresponding local /// packing concept variant. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local packing variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If the `packing` keyword is missing, has an unsupported value, or lower-level /// matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t packingMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribMatcherException; const auto& packing = get_or_throw(mars, "packing"); if (packing == "simple") { return static_cast(PackingType::Simple); } if (packing == "ccsds") { return static_cast(PackingType::Ccsds); } if (packing == "complex") { return static_cast(PackingType::SpectralComplex); } throw Mars2GribMatcherException{"Unknown value \"" + packing + "\" for mars keyword \"packing\"!", Here()}; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `packing` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/level/0000775000175000017500000000000015232567717023574 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/level/levelConceptDescriptor.h0000664000175000017500000001445115232567717030434 0ustar alastairalastair/* * (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 levelConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `level` concept. /// /// This header defines `LevelConcept`, the **compile-time descriptor** /// that registers the GRIB `level` 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/level/levelEncoding.h" #include "metkit/mars2grib/backend/concepts/level/levelEnum.h" #include "metkit/mars2grib/backend/concepts/level/levelMatcher.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 `level` concept. /// /// `LevelConcept` registers the GRIB `level` 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 LevelConcept : 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 levelName; } /// /// @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 levelTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `level` 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 (levelApplicable()) { return &LevelOp; } else { return nullptr; } } else { return nullptr; } // Avoid compiler warnings mars2gribUnreachable(); } /// /// @brief Variant-specific callbacks (not used for this concept). /// /// This hook is provided for completeness of the registry interface. /// The `level` concept does not define variant-level callbacks, /// so this function always returns `nullptr`. /// /// @tparam Capability Encoding capability index /// @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 Always `nullptr` /// template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This callback is invoked to determine whether the `level` /// concept should be activated for a given encoding request. /// /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Matcher function pointer /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &levelMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/level/levelEnum.h0000664000175000017500000002435415232567717025711 0ustar alastairalastair/* * (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 levelEnum.h /// @brief Definition of the `level` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `level` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`levelName`) /// - the exhaustive enumeration of supported level variants (`LevelType`) /// - a compile-time typelist of all variants (`LevelList`) /// - 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 /// `levelEncoding.h` 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 `level` 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 `level` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view levelName{"level"}; /// /// @brief Enumeration of all supported `level` concept variants. /// /// Each enumerator represents a distinct GRIB vertical level or layer /// interpretation as defined by the GRIB2 specification and ECMWF /// conventions. /// /// 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 exhaustive and includes both: /// - concrete GRIB levels (e.g. isobaric, hybrid, heightAboveGround) /// - abstract or logical levels used internally by the encoder /// /// @note /// Model-level coordinates are split into two variants: /// - `ModelSingleLevel` for fields published as a single 2D layer on the /// model-level system (no vertical column, no PV array). /// - `ModelMultipleLevel` for full vertical columns of model-level data, /// which require allocation and population of the PV array describing /// the vertical hybrid coordinate transformation. /// Both variants share the GRIB `typeOfLevel` string `"hybrid"`; they /// differ only in encoder behaviour (PV allocation). /// /// @note /// Three abstract variants exist: /// - `AbstractSingleLevel` and `AbstractMultipleLevel`: opaque level /// identifiers without an associated numeric `level` value. /// - `AbstractLevel`: opaque level identifier that carries a numeric /// `level` value (encoded via the `level` GRIB key). /// /// @warning /// Do not reorder existing enumerators in future changes, as their /// numeric values are used in compile-time tables and registries. /// enum class LevelType : std::size_t { Surface = 0, EntireAtmosphere, EntireLake, CloudBase, Tropopause, NominalTop, MostUnstableParcel, MixedLayerParcel, Isothermal, IsobaricInPa, IsobaricInHpa, LowCloudLayer, MediumCloudLayer, HighCloudLayer, MeanSea, HeightAboveSea, HeightAboveGround, ModelSingleLevel, ///< 2D field on model-level system; no PV array. ModelMultipleLevel, ///< Full vertical column of model-level data; requires PV array. Theta, PotentialVorticity, SnowLayer, SoilLayer, SeaIceLayer, OceanSurface, DepthBelowSeaLayer, OceanSurfaceToBottom, LakeBottom, MixingLayer, OceanModel, OceanModelLayer, MixedLayerDepthByDensity, MixedLayerDepthByTemperature, SnowLayerOverIceOnWater, IceTopOnWater, IceLayerOnWater, EntireMeltPond, WaterSurfaceToIsothermalOceanLayer, AbstractSingleLevel, AbstractLevel, ///< Opaque level identifier carrying a numeric `level` value. AbstractMultipleLevel, HeightAboveSeaAt10M, HeightAboveSeaAt2M, HeightAboveGroundAt10M, HeightAboveGroundAt2M, FlightLevel, Default }; /// /// @brief Compile-time list of all `level` 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 LevelList = ValueList; /// /// @brief Compile-time mapping from `LevelType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given level variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Level 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 levelTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view levelTypeName() { \ return NAME; \ } DEF(LevelType::Surface, "surface"); DEF(LevelType::EntireAtmosphere, "entireAtmosphere"); DEF(LevelType::EntireLake, "entireLake"); DEF(LevelType::CloudBase, "cloudBase"); DEF(LevelType::Tropopause, "tropopause"); DEF(LevelType::NominalTop, "nominalTop"); DEF(LevelType::MostUnstableParcel, "mostUnstableParcel"); DEF(LevelType::MixedLayerParcel, "mixedLayerParcel"); DEF(LevelType::Isothermal, "isothermal"); DEF(LevelType::IsobaricInPa, "isobaricInPa"); DEF(LevelType::IsobaricInHpa, "isobaricInhPa"); DEF(LevelType::LowCloudLayer, "lowCloudLayer"); DEF(LevelType::MediumCloudLayer, "mediumCloudLayer"); DEF(LevelType::HighCloudLayer, "highCloudLayer"); DEF(LevelType::MeanSea, "meanSea"); DEF(LevelType::HeightAboveSea, "heightAboveSea"); DEF(LevelType::HeightAboveGround, "heightAboveGround"); // ModelSingleLevel and ModelMultipleLevel both encode as GRIB // `typeOfLevel="hybrid"`; they differ only in encoder behaviour // (PV array allocation, see needPv in levelEncoding.h). DEF(LevelType::ModelSingleLevel, "hybrid"); DEF(LevelType::ModelMultipleLevel, "hybrid"); DEF(LevelType::Theta, "theta"); DEF(LevelType::PotentialVorticity, "potentialVorticity"); DEF(LevelType::SnowLayer, "snowLayer"); DEF(LevelType::SoilLayer, "soilLayer"); DEF(LevelType::SeaIceLayer, "seaIceLayer"); DEF(LevelType::OceanSurface, "oceanSurface"); DEF(LevelType::DepthBelowSeaLayer, "depthBelowSeaLayer"); DEF(LevelType::OceanSurfaceToBottom, "oceanSurfaceToBottom"); DEF(LevelType::LakeBottom, "lakeBottom"); DEF(LevelType::MixingLayer, "mixingLayer"); DEF(LevelType::OceanModel, "oceanModel"); DEF(LevelType::OceanModelLayer, "oceanModelLayer"); DEF(LevelType::MixedLayerDepthByDensity, "mixedLayerDepthByDensity"); DEF(LevelType::MixedLayerDepthByTemperature, "mixedLayerDepthByTemperature"); DEF(LevelType::SnowLayerOverIceOnWater, "snowLayerOverIceOnWater"); DEF(LevelType::IceTopOnWater, "iceTopOnWater"); DEF(LevelType::IceLayerOnWater, "iceLayerOnWater"); DEF(LevelType::EntireMeltPond, "entireMeltPond"); DEF(LevelType::WaterSurfaceToIsothermalOceanLayer, "waterSurfaceToIsothermalOceanLayer"); DEF(LevelType::AbstractSingleLevel, "abstractSingleLevel"); DEF(LevelType::AbstractLevel, "abstractLevel"); DEF(LevelType::AbstractMultipleLevel, "abstractMultipleLevel"); DEF(LevelType::HeightAboveSeaAt10M, "heightAboveSeaAt10m"); DEF(LevelType::HeightAboveSeaAt2M, "heightAboveSeaAt2m"); DEF(LevelType::HeightAboveGroundAt10M, "heightAboveGroundAt10m"); DEF(LevelType::HeightAboveGroundAt2M, "heightAboveGroundAt2m"); DEF(LevelType::FlightLevel, "flightLevel"); DEF(LevelType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/level/levelMatcher.h0000664000175000017500000007405715232567717026375 0ustar alastairalastair/* * (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 levelMatcher.h /// @brief Entry-level matcher for the GRIB `level` concept. /// /// This header defines the runtime matcher used by the concept registry to /// map MARS `levtype`, `param`, and, where required, `levelist` metadata onto /// GRIB level concept variants. /// /// The implementation is split into levtype-specific helper functions in the /// internal `impl` namespace and a public entry-level matcher. Each helper is /// responsible for one MARS level type and returns a local `LevelType` variant /// index. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// Nested error handling is intentionally applied both at the entry point and /// inside the levtype-specific helpers so diagnostics retain the full matcher /// call chain and the levtype-specific mapping context. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include #include // Utils #include "metkit/mars2grib/backend/concepts/level/levelEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/paramMatcher.h" namespace metkit::mars2grib::backend::concepts_ { namespace impl { /// /// @brief Match surface-level MARS parameters. /// /// Maps `levtype=sfc` parameters onto their GRIB level concept variant. /// Wave-period parameters deliberately return `MISSING` because they are handled /// by the wave concept rather than by level encoding. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index, or /// `compile_time_registry_engine::MISSING` for surface products owned by another /// concept. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no surface-level mapping exists. Lower-level exceptions are preserved /// through `std::throw_with_nested`. /// inline std::size_t matchSFC(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, 228023)) { return static_cast(LevelType::CloudBase); } if (matchAny(param, 262118)) { return static_cast(LevelType::DepthBelowSeaLayer); } if (matchAny(param, 59, 78, 79, 136, 137, 164, 194, 206, range(162059, 162063), 162071, 162072, 162093, 228001, 228044, 228050, 228052, range(228088, 228090), 228164, 235087, 235088, 235136, 235137, 235287, 235288, 235290, 235326, 235383, 237087, 237088, 237137, 237287, 237288, 237290, 237326, 238087, 238088, 238137, 238287, 238288, 238290, 238326, 239087, 239088, 239137, 239287, 239288, 239290, 239326, 260132)) { return static_cast(LevelType::EntireAtmosphere); } // efi if (matchAny(param, 132045)) { return static_cast(LevelType::EntireAtmosphere); } if (matchAny(param, 228007, 228011)) { return static_cast(LevelType::EntireLake); } if (matchAny(param, 129172)) { return static_cast(LevelType::HeightAboveGround); } if (matchAny(param, 49, 123, 165, 166, 207, 228005, 228028, 228029, 228131, 228132, 235165, 235166, 237165, 237166, 237207, 237318, 238165, 238166, 238207, 239165, 239166, 239207, 260260)) { return static_cast(LevelType::HeightAboveGroundAt10M); } // Strike-probability if (matchAny(param, 131068, 131069, 131070, 131071, 131072, 131100)) { return static_cast(LevelType::HeightAboveGroundAt10M); } // efi if (matchAny(param, 132049, 132165)) { return static_cast(LevelType::HeightAboveGroundAt10M); } if (matchAny(param, 121, 122, 167, 168, 201, 202, 174096, 228004, 228037, 235168, 237167, 237168, 238167, 238168, 239167, 239168, 260242)) { return static_cast(LevelType::HeightAboveGroundAt2M); } // Strike-probability if (matchAny(param, 131073)) { return static_cast(LevelType::HeightAboveGroundAt2M); } // efi if (matchAny(param, 132167, 132201, 132202)) { return static_cast(LevelType::HeightAboveGroundAt2M); } if (matchAny(param, 140233, 140245, 140249, 141233, 141245, 143233, 143245, 144233, 144245, 145233, 145245)) { return static_cast(LevelType::HeightAboveSeaAt10M); } if (matchAny(param, 188, 3075)) { return static_cast(LevelType::HighCloudLayer); } if (matchAny(param, 228014, 235309, 237309, 238309, 239309)) { return static_cast(LevelType::IceLayerOnWater); } if (matchAny(param, 228013)) { return static_cast(LevelType::IceTopOnWater); } if (matchAny(param, 262104)) { return static_cast(LevelType::Isothermal); } if (matchAny(param, 228010, 235305, 237305, 238305, 239305)) { return static_cast(LevelType::LakeBottom); } if (matchAny(param, 186, 3073, 235108, 237108, 238108, 239108)) { return static_cast(LevelType::LowCloudLayer); } if (matchAny(param, 151, 235151, 237151, 238151, 239151)) { return static_cast(LevelType::MeanSea); } if (matchAny(param, 187, 3074)) { return static_cast(LevelType::MediumCloudLayer); } if (matchAny(param, range(228231, 228234))) { return static_cast(LevelType::MixedLayerParcel); } if (matchAny(param, 228008, 228009, 235090, 235091, 237090, 237091, 238090, 238091, 239090, 239091)) { return static_cast(LevelType::MixingLayer); } if (matchAny(param, range(228235, 228237))) { return static_cast(LevelType::MostUnstableParcel); } // efi if (matchAny(param, 132044, 132059)) { return static_cast(LevelType::MostUnstableParcel); } if (matchAny(param, 178, 179, 208, 209, 212, 235039, 235040, 235049, 235050, 235053)) { return static_cast(LevelType::NominalTop); } if (matchAny(param, 263024, 265024, 266024, 267024)) { return static_cast(LevelType::SeaIceLayer); } if (matchAny(param, 235077, 235094, 237077, 237094, 238077, 238094, 239077, 239094)) { return static_cast(LevelType::SoilLayer); } if (matchAny(param, 8, 9, range(15, 18), 20, range(26, 45), 47, 50, 57, 58, 66, 67, 74, 129, 134, 139, range(141, 148), range(159, 163), 169, 170, range(172, 177), range(180, 182), 189, range(195, 198), 205, 210, 211, 213, range(228, 232), range(234, 236), range(238, 240), range(243, 245), 3020, 3062, 3067, 3099, range(140098, 140105), 140112, 140113, range(140121, 140129), range(140131, 140134), range(140207, 140209), 140211, 140212, range(140214, 140232), range(140234, 140239), 140244, range(140246, 140248), range(140252, 140254), range(141101, 141105), 141208, 141209, 141215, 141216, 141220, 141229, 141232, range(143101, 143105), 143208, 143209, 143215, 143216, 143220, 143229, 143232, range(144101, 144105), 144208, 144209, 144215, 144216, 144220, 144229, 144232, range(145101, 145105), 145208, 145209, 145215, 145216, 145220, 145229, 145232, 160198, range(162100, 162113), 200199, range(210186, 210191), range(210198, 210202), range(210260, 210264), range(222001, 222256), 228002, 228003, 228012, range(228015, 228022), 228024, 228026, 228027, 228032, 228035, 228036, range(228046, 228048), 228051, 228053, range(228057, 228060), 228129, 228130, 228141, 228143, 228144, range(228216, 228228), 228251, 229001, 229007, range(231001, 231003), 231005, 231010, 231012, 231057, 231058, range(233000, 233031), 235020, 235021, range(235029, 235031), range(235033, 235038), range(235041, 235043), 235048, 235051, 235052, 235055, 235058, range(235078, 235080), 235083, 235084, 235093, 235134, 235159, 235189, 235263, 235283, 235339, 237013, 237041, 237042, 237055, 237078, 237080, 237083, 237084, 237093, 237117, 237134, 237159, 237263, 237321, 238013, 238041, 238042, 238055, 238078, 238080, 238083, 238084, 238093, 238134, 238159, 238263, 239041, 239042, 239078, 239080, 239083, 239084, 239093, 239134, 239159, 239263, 260004, 260005, 260015, 260038, 260048, 260109, 260121, 260123, 260255, 260259, 260289, 260292, 260293, range(260318, 260321), 260338, 260339, 260509, 260682, 260683, 260688, 261001, 261002, range(261014, 261016), 261018, 261023, 262000, 262100, 262124, 262139, 262140, 262144)) { return static_cast(LevelType::Surface); } // Strike-probability if (matchAny(param, 131022, 131024, 131060, 131061, 131062, 131063, 131064, 131065, 131066, 131067, range(131074, 131077), 131085, 131089, 131090, 131091, 131098, 131099, 133096, 133097)) { return static_cast(LevelType::Surface); } // efi if (matchAny(param, 132228, 132144)) { return static_cast(LevelType::Surface); } if (matchAny(param, 228045, 235322, 237322, 238322, 239322)) { return static_cast(LevelType::Tropopause); } // Chemical if (matchAny(param, range(228080, 228085), range(233032, 233035), range(235062, 235064), range(400000, 499999))) { return static_cast(LevelType::Surface); } // Wave period if (matchAny(param, range(140114, 140120))) { return compile_time_registry_engine::MISSING; } // ECMWF covariance paramIds (254001..254017) are defined in // eccodes/definitions/grib2/localConcepts/{ecmf,era6}/paramId.def with // typeOfFirstFixedSurface=254, which maps to the eccodes typeOfLevel // concept "abstractLevel". if (matchAny(param, range(254001, 254017))) { return static_cast(LevelType::AbstractLevel); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype SFC", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "sfc", "Unable to match `level` concept for levtype \"sfc\"", Here())); } } /// /// @brief Match height-level MARS parameters. /// /// Maps `levtype=hl` parameters onto their GRIB level concept variant. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no height-level mapping exists. Lower-level exceptions are preserved /// through `std::throw_with_nested`. /// inline std::size_t matchHL(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, 10, 54, range(130, 132), 157, 246, 247, 3031, 235097, 235131, 235132, 237097, 237131, 237132, 238097, 238131, 238132, 239097, 239131, 239132)) { return static_cast(LevelType::HeightAboveGround); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype HL", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "hl", "Unable to match `level` concept for levtype \"hl\"", Here())); } } /// /// @brief Match model-level MARS parameters. /// /// Maps `levtype=ml` parameters onto their GRIB level concept variant. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no model-level mapping exists. Lower-level exceptions are preserved /// through `std::throw_with_nested`. /// inline std::size_t matchML(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; // Single-level subset of ML params: 2D fields published on the // model-level levtype but not requiring a vertical PV array. This // guard fires before the multi-level rule below; params listed here // are removed from the multi-level set. if (matchAny(param, 22, 127, 128, 129, 152)) { return static_cast(LevelType::ModelSingleLevel); } // Multi-level model fields: full vertical column, require allocation // and population of the PV array describing the hybrid coordinate. if (matchAny(param, 21, 23, range(75, 77), range(130, 133), 135, 138, range(155, 157), 203, range(246, 248), range(162100, 162113), 260290, 260292, 260293, range(400000, 499999))) { return static_cast(LevelType::ModelMultipleLevel); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype ML", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "ml", "Unable to match `level` concept for levtype \"ml\"", Here())); } } /// /// @brief Match pressure-level MARS parameters. /// /// Maps `levtype=pl` parameters onto pressure-level GRIB variants. The MARS /// `levelist` value determines whether the pressure level is interpreted in hPa /// or Pa. /// /// @param[in] param MARS parameter identifier /// @param[in] level MARS pressure level value from `levelist` /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no pressure-level mapping exists. Lower-level exceptions are preserved /// through `std::throw_with_nested`. /// inline std::size_t matchPL(const long param, const long level) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, 1, 2, 10, 60, 75, 76, range(129, 135), 138, 152, range(155, 157), 203, range(246, 248), 235100, range(235129, 235133), 235135, 235138, 235152, 235155, 235157, 235203, 235246, 260290, 263107, range(400000, 499999))) { if (level >= 100) { return static_cast(LevelType::IsobaricInHpa); } else { return static_cast(LevelType::IsobaricInPa); } } // Strike-probability if (matchAny(param, 131020, 131021, 131022, 131023, 131024, 131025, 133093, 133094, 133095, 133096, 133097, 133098)) { if (level >= 100) { return static_cast(LevelType::IsobaricInHpa); } else { return static_cast(LevelType::IsobaricInPa); } } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype PL", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "pl", "Unable to match `level` concept for levtype \"pl\" and levelist \"" + std::to_string(level) + "\"", Here())); } } /// /// @brief Match flight-level MARS parameters. /// /// Maps `levtype=fl` parameters onto their GRIB level concept variant. /// The MARS `levelist` value is not relevant for flight levels because the /// conversion from flight level to pressure level is not one-to-one and requires additional /// metadata (e.g. temperature profile) that is not available at the point of level concept matching. /// Therefore, all flight levels map to the same GRIB level concept variant regardless of `levelist`. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no flight-level mapping exists. Lower-level exceptions are preserved /// through `std::throw_with_nested`. /// inline std::size_t matchFL(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, 260290)) { return static_cast(LevelType::FlightLevel); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype FL", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "fl", "Unable to match `level` concept for levtype \"fl\"", Here())); } } /// /// @brief Match potential-temperature-level MARS parameters. /// /// Maps `levtype=pt` parameters onto their GRIB level concept variant. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no potential-temperature-level mapping exists. Lower-level exceptions are /// preserved through `std::throw_with_nested`. /// inline std::size_t matchPT(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, 53, 54, 60, range(131, 133), 138, 155, 203, 235100, 235203, 237203, 238203, 239203, range(400000, 499999))) { return static_cast(LevelType::Theta); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype PT", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "pt", "Unable to match `level` concept for levtype \"pt\"", Here())); } } /// /// @brief Match potential-vorticity-level MARS parameters. /// /// Maps `levtype=pv` parameters onto their GRIB level concept variant. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no potential-vorticity-level mapping exists. Lower-level exceptions are /// preserved through `std::throw_with_nested`. /// inline std::size_t matchPV(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, 3, 54, 129, range(131, 133), 203, 235098, 235269, range(400000, 499999))) { return static_cast(LevelType::PotentialVorticity); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype PV", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "pv", "Unable to match `level` concept for levtype \"pv\"", Here())); } } /// /// @brief Match soil-level MARS parameters. /// /// Maps `levtype=sol` parameters onto their GRIB level concept variant. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no soil-level mapping exists. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// inline std::size_t matchSOL(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, 262000, 262024)) { return static_cast(LevelType::SeaIceLayer); } if (matchAny(param, 33, 74, 238, 228038, 228141, 235078, 235080, 237080, 238080, 239080)) { return static_cast(LevelType::SnowLayer); } if (matchAny(param, 183, 235077, 260199, 260360)) { return static_cast(LevelType::SoilLayer); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype SOL", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "sol", "Unable to match `level` concept for levtype \"sol\"", Here())); } } /// /// @brief Match abstract-level MARS parameters. /// /// Maps `levtype=al` parameters onto their GRIB level concept variant. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no abstract-level mapping exists. Lower-level exceptions are preserved /// through `std::throw_with_nested`. /// inline std::size_t matchAL(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, range(213101, 213160))) { return static_cast(LevelType::AbstractSingleLevel); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype AL", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "al", "Unable to match `level` concept for levtype \"al\"", Here())); } } /// /// @brief Match two-dimensional ocean-level MARS parameters. /// /// Maps `levtype=o2d` parameters onto their GRIB level concept variant. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no two-dimensional ocean-level mapping exists. Lower-level exceptions are /// preserved through `std::throw_with_nested`. /// inline std::size_t matchO2D(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, 262000, 262003, 262004, 262008, 262014, 262023)) { return static_cast(LevelType::IceLayerOnWater); } if (matchAny(param, 262001, 262005, 262006, 262906, 262907)) { return static_cast(LevelType::IceTopOnWater); } if (matchAny(param, 262002, 262009, 262011, 262015)) { return static_cast(LevelType::SnowLayerOverIceOnWater); } if (matchAny(param, 262017, 262018)) { return static_cast(LevelType::EntireMeltPond); } if (matchAny(param, 262100, 262101, range(262108, 262112), 262124, 262125, 262130, 262139, 262140, 262143, 262900)) { return static_cast(LevelType::OceanSurface); } if (matchAny(param, range(262102, 262106))) { return static_cast(LevelType::Isothermal); } if (matchAny(param, range(262113, 262115))) { return static_cast(LevelType::MixedLayerDepthByDensity); } if (matchAny(param, 262116)) { return static_cast(LevelType::MixedLayerDepthByTemperature); } if (matchAny(param, 262118, 262119, 262121, 262122, 262146, 262147)) { return static_cast(LevelType::DepthBelowSeaLayer); } if (matchAny(param, 262120, 262123, 262148)) { return static_cast(LevelType::OceanSurfaceToBottom); } if (matchAny(param, 262141)) { return static_cast(LevelType::WaterSurfaceToIsothermalOceanLayer); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype O2D", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "o2d", "Unable to match `level` concept for levtype \"o2d\"", Here())); } } /// /// @brief Match three-dimensional ocean-level MARS parameters. /// /// Maps `levtype=o3d` parameters onto their GRIB level concept variant. /// /// @param[in] param MARS parameter identifier /// /// @return Local `LevelType` variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If no three-dimensional ocean-level mapping exists. Lower-level exceptions /// are preserved through `std::throw_with_nested`. /// inline std::size_t matchO3D(const long param) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; if (matchAny(param, range(262500, 262502), 262505, 262506)) { return static_cast(LevelType::OceanModelLayer); } if (matchAny(param, 262507)) { return static_cast(LevelType::OceanModel); } throw utils::exceptions::Mars2GribMatcherException( "No mapping exists for param \"" + std::to_string(param) + "\" on levtype O3D", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException( param, "o3d", "Unable to match `level` concept for levtype \"o3d\"", Here())); } } } // namespace impl /// /// @brief Match the `level` concept variant. /// /// The matcher first excludes products owned by other concepts, such as wave /// spectra and satellite products. For remaining products, it reads `param` and /// `levtype` and dispatches to the corresponding levtype-specific helper. /// Pressure-level products additionally require `levelist`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local level variant index, or /// `compile_time_registry_engine::MISSING` when the level concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If required MARS metadata is missing, `levtype` is unsupported, no mapping /// exists for the `(levtype, param)` combination, or lower-level matcher /// evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t levelMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; // Skip wave spectra and satellite products if ((has(mars, "frequency") && has(mars, "direction")) || // Wave spectra (has(mars, "channel") && has(mars, "ident") && has(mars, "instrument")) // Satellite ) { return compile_time_registry_engine::MISSING; } const auto param = get_or_throw(mars, "param"); const auto levtype = get_or_throw(mars, "levtype"); if (levtype == "sfc") { return impl::matchSFC(param); } if (levtype == "hl") { return impl::matchHL(param); } if (levtype == "ml") { return impl::matchML(param); } if (levtype == "pl") { const auto level = get_or_throw(mars, "levelist"); return impl::matchPL(param, level); } if (levtype == "fl") { return impl::matchFL(param); } if (levtype == "pt") { return impl::matchPT(param); } if (levtype == "pv") { return impl::matchPV(param); } if (levtype == "sol") { return impl::matchSOL(param); } if (levtype == "al") { return impl::matchAL(param); } if (levtype == "o2d") { return impl::matchO2D(param); } if (levtype == "o3d") { return impl::matchO3D(param); } throw utils::exceptions::Mars2GribMatcherException("Unknown levtype \"" + levtype + "\"", Here()); } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException("Unable to match `level` concept", Here())); } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/level/levelEncoding.h0000664000175000017500000003242615232567717026532 0ustar alastairalastair/* * (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 levelEncoding.h /// @brief Implementation of the GRIB `level` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **level concept** within the mars2grib backend. /// /// The level concept is responsible for encoding GRIB keys related to the /// *vertical coordinate system* of the data, including: /// /// - `typeOfLevel` /// - `level` /// - model-level coordinates and PV array (multi-level model layouts only) /// - layered levels expressed via `topLevel` / `bottomLevel` /// /// The encoder behaviour is governed by three orthogonal compile-time /// predicates over the variant: /// /// - `needPv()` : the variant requires a PV array /// (currently only `ModelMultipleLevel`). /// - `needLevel()` : the variant carries a numeric `level` /// value to be written. /// - `needTopBottomLevel()` : the variant is expressed as a /// layer with explicit `topLevel` and /// `bottomLevel` GRIB keys. /// /// These three axes are independent: any subset may apply to a given /// variant. A small number of variants additionally have hard-coded /// special cases inside `LevelOp` (for example the `*At2M` / `*At10M` /// shortcuts and the `IsobaricInHpa` Pa->hPa conversion). /// /// Depending on the selected level variant, the concept may: /// - set only the level type, /// - set both level type and numeric level, /// - allocate and populate the PV array (multi-level model layouts). /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `levelApplicable` /// - Stage-aware behavior (allocation vs preset/runtime) /// - Explicit handling of hybrid vertical coordinates /// - 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 // System includes #include #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/level/levelEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/level.h" #include "metkit/mars2grib/backend/deductions/pvArray.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 predicate indicating whether a PV array is required. /// /// Only multi-level model-level layouts require a PV array describing /// the vertical hybrid coordinate transformation. Single-level model /// fields share the GRIB `typeOfLevel="hybrid"` string but do not carry /// a vertical column and therefore do not need a PV array. /// /// @tparam Variant Level concept variant /// /// @return `true` if a PV array is required, /// `false` otherwise. /// template constexpr bool needPv() { if constexpr (Variant == LevelType::ModelMultipleLevel) { return true; } else { return false; } // Remove compiler warning mars2gribUnreachable(); } /// /// @brief Compile-time predicate indicating whether a numeric `level` value is required. /// /// Some level types require an associated numeric level (e.g. pressure, /// height, model-level number). Both model-level variants /// (`ModelSingleLevel` and `ModelMultipleLevel`) require it; they differ /// only in whether a PV array is also needed. /// /// `AbstractLevel` carries an opaque numeric `level` value (in contrast /// to `AbstractSingleLevel` and `AbstractMultipleLevel`, which do not). /// /// @tparam Variant Level concept variant /// /// @return `true` if a numeric `level` value must be set, /// `false` otherwise. /// template constexpr bool needLevel() { if constexpr (Variant == LevelType::HeightAboveGroundAt10M || Variant == LevelType::HeightAboveGroundAt2M || Variant == LevelType::HeightAboveGround || Variant == LevelType::HeightAboveSeaAt10M || Variant == LevelType::HeightAboveSeaAt2M || Variant == LevelType::HeightAboveSea || Variant == LevelType::ModelSingleLevel || Variant == LevelType::ModelMultipleLevel || Variant == LevelType::IsobaricInHpa || Variant == LevelType::IsobaricInPa || Variant == LevelType::Isothermal || Variant == LevelType::PotentialVorticity || Variant == LevelType::Theta || Variant == LevelType::OceanModel || Variant == LevelType::AbstractLevel || Variant == LevelType::FlightLevel) { return true; } else { return false; } // Remove compiler warning mars2gribUnreachable(); } /// /// @brief Compile-time predicate indicating whether a numeric `topLevel` and `bottomLevel` value is required. /// /// Some level types require two associated numeric levels (e.g. soilLayer), /// while others encode only the level type and/or level. /// /// @tparam Variant Level concept variant /// /// @return `true` if a numeric `topLevel` and `bottomLevel` value must be set, /// `false` otherwise. /// template constexpr bool needTopBottomLevel() { if constexpr (Variant == LevelType::SoilLayer || Variant == LevelType::SeaIceLayer || Variant == LevelType::SnowLayer || Variant == LevelType::OceanModelLayer) { return true; } else { return false; } // Remove compiler warning mars2gribUnreachable(); } /// /// @brief Compile-time applicability predicate for the `level` concept. /// /// This predicate determines whether the level concept is applicable for a given /// combination of: /// - encoding stage /// - GRIB section /// - level variant /// /// Applicability is evaluated entirely at compile time and is used by the /// concept dispatcher to control instantiation and execution. /// /// Multi-level model layouts require special handling: /// - during allocation stage to reserve space for the PV array, /// - during preset/runtime stages to set the level type and parameters. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Level concept variant /// /// @return `true` if the concept is applicable for the given parameters, /// `false` otherwise. /// template constexpr bool levelApplicable() { if constexpr (Section == SecProductDefinitionSection && needPv()) { // pvArray needs to be allocated at allocation stage return true; } if constexpr (Section == SecProductDefinitionSection && !needPv()) { return (Stage != StageAllocate); } return false; } /// /// @brief Execute the `level` concept operation. /// /// This function implements the runtime logic of the GRIB `level` concept. /// When applicable, it: /// /// - allocates and sets the PV array for hybrid levels during allocation stage, /// - sets the GRIB `typeOfLevel` key, /// - sets the numeric `level` key when required. /// /// The behavior is explicitly stage-dependent: /// - `StageAllocate` is used for memory allocation (PV array), /// - `StagePreset` and `StageRuntime` are used for semantic encoding. /// /// 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 Level 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: /// - required deductions fail, /// - invalid stage/variant combinations are invoked, /// - any GRIB key cannot be set. /// /// @note /// - All runtime errors are wrapped with full concept context /// (concept name, variant, stage, section). /// - The concept does not rely on pre-existing GRIB header state. /// - Setting of typeOfLevel is happening at both preset and runtime stages because /// sometimes due to sideeffects in eccodes the typeOfLevel set at preset stage /// can be overwritten before runtime stage. /// /// /// @see levelApplicable /// @see needLevel /// @see needPv /// template void LevelOp(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 (levelApplicable()) { try { MARS2GRIB_LOG_CONCEPT(level); // ============================================================= // Allocation stage (PV array) // ============================================================= if constexpr (Stage == StageAllocate && needPv()) { // Allocate space for pv array std::vector pv_array = deductions::resolve_PvArray_or_throw(mars, par, opt); // Set the PV array set_or_throw(out, "PVPresent", 1L); set_or_throw>(out, "pv", pv_array); } // ============================================================= // Preset / runtime stage // ============================================================= if constexpr (Stage == StagePreset || Stage == StageRuntime) { // Set level type (and level) if constexpr (Variant == LevelType::HeightAboveGroundAt2M) { set_or_throw(out, "typeOfLevel", "heightAboveGround"); set_or_throw(out, "level", 2L); } else if constexpr (Variant == LevelType::HeightAboveGroundAt10M) { set_or_throw(out, "typeOfLevel", "heightAboveGround"); set_or_throw(out, "level", 10L); } else if constexpr (Variant == LevelType::HeightAboveSeaAt2M) { set_or_throw(out, "typeOfLevel", "heightAboveSea"); set_or_throw(out, "level", 2L); } else if constexpr (Variant == LevelType::HeightAboveSeaAt10M) { set_or_throw(out, "typeOfLevel", "heightAboveSea"); set_or_throw(out, "level", 10L); } else if constexpr (Variant == LevelType::IsobaricInHpa) { long levelVal = deductions::resolve_Level_or_throw(mars, par, opt); set_or_throw(out, "typeOfLevel", "isobaricInhPa"); set_or_throw(out, "level", levelVal / 100); } else { set_or_throw(out, "typeOfLevel", std::string(levelTypeName())); if constexpr (needLevel()) { long levelVal = deductions::resolve_Level_or_throw(mars, par, opt); set_or_throw(out, "level", levelVal); } if constexpr (needTopBottomLevel()) { long levelVal = deductions::resolve_Level_or_throw(mars, par, opt); long topLevel = levelVal - 1; long bottomLevel = levelVal; set_or_throw(out, "topLevel", topLevel); set_or_throw(out, "bottomLevel", bottomLevel); } } } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(level, "Unable to set `level` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(level, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-time/0000775000175000017500000000000015232567717025357 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-time/referenceTimeConceptDescriptor.h0000664000175000017500000001220315232567717033656 0ustar alastairalastair/* * (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 referenceTimeConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `referenceTime` concept. /// /// This header defines `ReferenceTimeConcept`, the **compile-time descriptor** /// that registers the GRIB `referenceTime` 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/reference-time/referenceTimeEncoding.h" #include "metkit/mars2grib/backend/concepts/reference-time/referenceTimeEnum.h" #include "metkit/mars2grib/backend/concepts/reference-time/referenceTimeMatcher.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 `referenceTime` concept. /// /// `ReferenceTimeConcept` registers the GRIB `referenceTime` concept into the /// compile-time registry engine. /// struct ReferenceTimeConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return referenceTimeName; } /// /// @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 referenceTimeTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `referenceTime` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (referenceTimeApplicable()) { return &ReferenceTimeOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `referenceTime` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &referenceTimeMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-time/referenceTimeEncoding.h0000664000175000017500000002525015232567717031760 0ustar alastairalastair/* * (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 referenceTimeEncoding.h /// @brief Implementation of the GRIB `referenceTime` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **referenceTime concept** within the mars2grib backend. /// /// The referenceTime concept is responsible for encoding the temporal /// *reference instant* of a GRIB product, including: /// /// - the **significance of the reference time** /// - the **actual reference date/time** /// - optional **model version date/time** metadata for reforecasts /// /// The behavior depends on both the **concept variant** and the **GRIB section** /// in which the concept is applied. /// /// Supported variants: /// - `ReferenceTimeType::Standard` /// - `ReferenceTimeType::Reforecast` /// /// Supported sections: /// - Identification Section /// - Product Definition Section (reforecast only) /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `referenceTimeApplicable` /// - Variant- and section-specific deduction logic /// - Strict validation of GRIB template compatibility /// - Context-rich error handling /// /// @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 // System includes #include // External libraries #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/reference-time/referenceTimeEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/productTime.h" #include "metkit/mars2grib/backend/deductions/significanceOfReferenceTime.h" // Tables #include "metkit/mars2grib/backend/tables/significanceOfReferenceTime.h" // Checks #include "metkit/mars2grib/backend/checks/matchProductDefinitionTemplateNumber.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 `referenceTime` concept. /// /// This predicate determines whether the referenceTime concept is applicable /// for a given combination of: /// - encoding stage /// - GRIB section /// - reference time variant /// /// The default rules enable: /// /// - **Standard reference time** /// - Identification Section /// - Preset stage /// /// - **Reforecast reference time** /// - Identification Section (Preset stage) /// - Product Definition Section (Preset stage) /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Reference time concept variant /// /// @return `true` if the concept is applicable for the given parameters, /// `false` otherwise. /// template constexpr bool referenceTimeApplicable() { // Compile time conditions to apply this concept bool condition1 = (Variant == ReferenceTimeType::Standard || Variant == ReferenceTimeType::Reforecast) && (Stage == StageRuntime) && (Section == SecIdentificationSection); bool condition2 = (Variant == ReferenceTimeType::Reforecast) && (Stage == StageRuntime) && (Section == SecProductDefinitionSection); // Confitions to apply concept return condition1 || condition2; } /// /// @brief Execute the `referenceTime` concept operation. /// /// This function implements the runtime logic of the GRIB `referenceTime` concept. /// When applicable, it: /// /// - Encodes the significance of the reference time /// - Sets the reference date and time fields /// - Optionally encodes model version date/time metadata for reforecasts /// /// The behavior is driven by: /// - the concept variant (`Standard` vs `Reforecast`) /// - the target GRIB section /// /// Section-specific behavior: /// /// - **Identification Section** /// - Sets `significanceOfReferenceTime` /// - Sets reference date/time fields /// /// - **Product Definition Section (Reforecast only)** /// - Validates template compatibility /// - Sets model version date/time fields /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Reference time 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 concept is invoked outside its applicability domain /// - GRIB template constraints are violated /// - any deduction or encoding step fails /// /// @see referenceTimeApplicable /// template void ReferenceTimeOp(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 (referenceTimeApplicable()) { try { MARS2GRIB_LOG_CONCEPT(referenceTime); // Resolve the canonical ProductTime once per concept invocation. // All three branches below source their date/time exclusively // from this object (§15 of timeProducts.md). auto pt = deductions::resolve_ProductTime_or_throw(mars, par, opt); // ============================================================= // Variant-specific logic // ============================================================= if constexpr (Section == SecIdentificationSection) { // Deductions: significanceOfReferenceTime is orthogonal to // ProductTime (driven by mars.type / mars.stream). tables::SignificanceOfReferenceTime significanceOfReferenceTime = deductions::resolve_SignificanceOfReferenceTime_or_throw(mars, par, opt); // Encoding set_or_throw(out, "significanceOfReferenceTime", static_cast(significanceOfReferenceTime)); } if constexpr ((Section == SecIdentificationSection) && (Variant == ReferenceTimeType::Standard)) { // For a standard product, the GRIB "reference date/time" // is the canonical ProductTime::referenceDateTime. const eckit::DateTime& dateTime = pt.referenceDateTime; // Encoding set_or_throw(out, "year", dateTime.date().year()); set_or_throw(out, "month", dateTime.date().month()); set_or_throw(out, "day", dateTime.date().day()); set_or_throw(out, "hour", dateTime.time().hours()); set_or_throw(out, "minute", dateTime.time().minutes()); set_or_throw(out, "second", dateTime.time().seconds()); } if constexpr ((Section == SecIdentificationSection) && (Variant == ReferenceTimeType::Reforecast)) { // For a reforecast product, the Identification Section's // reference date/time is the hindcast date — i.e. the // canonical ProductTime::labelDateTime (from date / // time). The ProductDefinitionSection branch below writes // the model-version date from referenceDateTime instead. const eckit::DateTime& referenceDateTime = pt.labelDateTime; // Encoding set_or_throw(out, "year", referenceDateTime.date().year()); set_or_throw(out, "month", referenceDateTime.date().month()); set_or_throw(out, "day", referenceDateTime.date().day()); set_or_throw(out, "hour", referenceDateTime.time().hours()); set_or_throw(out, "minute", referenceDateTime.time().minutes()); set_or_throw(out, "second", referenceDateTime.time().seconds()); } if constexpr ((Section == SecProductDefinitionSection) && (Variant == ReferenceTimeType::Reforecast)) { // Validation validation::match_ProductDefinitionTemplateNumber_or_throw(opt, out, {60L, 61L}); // Model-version date/time = ProductTime::referenceDateTime // (derived from date/time or year/month per §7.4). // TODO: Need to clarify with DGOV if this is reference or initialConditionsDateTime. const eckit::DateTime& dateTime = pt.referenceDateTime; // Encoding set_or_throw(out, "YearOfModelVersion", dateTime.date().year()); set_or_throw(out, "MonthOfModelVersion", dateTime.date().month()); set_or_throw(out, "DayOfModelVersion", dateTime.date().day()); set_or_throw(out, "HourOfModelVersion", dateTime.time().hours()); set_or_throw(out, "MinuteOfModelVersion", dateTime.time().minutes()); set_or_throw(out, "SecondOfModelVersion", dateTime.time().seconds()); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(referenceTime, "Unable to set `referenceTime` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(referenceTime, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-time/referenceTimeEnum.h0000664000175000017500000001101615232567717031131 0ustar alastairalastair/* * (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 referenceTimeEnum.h /// @brief Definition of the `referenceTime` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `referenceTime` /// concept used by the mars2grib backend. It contains: /// /// - the canonical concept name (`referenceTimeName`) /// - the enumeration of supported reference-time variants (`ReferenceTimeType`) /// - a compile-time typelist of all variants (`ReferenceTimeList`) /// - 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 /// `referenceTimeEncoding.h` 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 `referenceTime` 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 `referenceTime` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view referenceTimeName{"referenceTime"}; /// /// @brief Enumeration of all supported `referenceTime` concept variants. /// /// Each enumerator represents a distinct interpretation of the forecast /// reference time, as defined by ECMWF conventions and GRIB usage. /// /// 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 distinguishes between operational forecasts /// and reforecast datasets. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class ReferenceTimeType : std::size_t { Standard = 0, Reforecast = 1 }; /// /// @brief Compile-time list of all `referenceTime` 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 ReferenceTimeList = ValueList; /// /// @brief Compile-time mapping from `ReferenceTimeType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given reference-time variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Reference-time 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 referenceTimeTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view referenceTimeTypeName() { \ return NAME; \ } DEF(ReferenceTimeType::Standard, "standard"); DEF(ReferenceTimeType::Reforecast, "reforecast"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/reference-time/referenceTimeMatcher.h0000664000175000017500000000522615232567717031616 0ustar alastairalastair/* * (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 referenceTimeMatcher.h /// @brief Entry-level matcher for the GRIB `referenceTime` concept. /// /// This header defines the runtime matcher used by the concept registry to /// choose between standard and reforecast reference-time handling. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/reference-time/referenceTimeEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `referenceTime` concept variant. /// /// The matcher selects `ReferenceTimeType::Reforecast` when `hdate` is present /// in the MARS request, and `ReferenceTimeType::Standard` otherwise. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for the selected reference-time variant. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t referenceTimeMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::has; if (has(mars, "hdate")) { return static_cast(ReferenceTimeType::Reforecast); } return static_cast(ReferenceTimeType::Standard); } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `referenceTime` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/derived/0000775000175000017500000000000015232567717024107 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/derived/derivedEnum.h0000664000175000017500000001062415232567717026532 0ustar alastairalastair/* * (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 derivedEnum.h /// @brief Definition of the `derived` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `derived` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`derivedName`) /// - the exhaustive enumeration of supported derived variants (`DerivedType`) /// - a compile-time typelist of all variants (`DerivedList`) /// - 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 /// `derivedEncoding.h` 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 `derived` 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 `derived` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view derivedName{"derived"}; /// /// @brief Enumeration of all supported `derived` concept variants. /// /// Each enumerator represents a specific derived product or statistical /// transformation applied to ensemble or multi-field data. /// /// 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 includes both direct field selections and /// post-processing/statistical aggregations. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class DerivedType : std::size_t { ShiftOfTails = 0, ///< Shift-of-tails derived product Default }; /// /// @brief Compile-time list of all `derived` 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 DerivedList = ValueList; /// /// @brief Compile-time mapping from `DerivedType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given derived variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Derived 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 derivedTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view derivedTypeName() { \ return NAME; \ } DEF(DerivedType::Default, "default"); DEF(DerivedType::ShiftOfTails, "shift_of_tails"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/derived/derivedConceptDescriptor.h0000664000175000017500000001453715232567717031267 0ustar alastairalastair/* * (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 derivedConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `derived` concept. /// /// This header defines `DerivedConcept`, the **compile-time descriptor** /// that registers the GRIB `derived` 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/derived/derivedEncoding.h" #include "metkit/mars2grib/backend/concepts/derived/derivedEnum.h" #include "metkit/mars2grib/backend/concepts/derived/derivedMatcher.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 `derived` concept. /// /// `DerivedConcept` registers the GRIB `derived` 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 DerivedConcept : 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 derivedName; } /// /// @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 derivedTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `derived` 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 (derivedApplicable()) { return &DerivedOp; } else { return nullptr; } } else { return nullptr; } // Avoid compiler warnings mars2gribUnreachable(); } /// /// @brief Variant-specific callbacks (not used for this concept). /// /// This hook is provided for completeness of the registry interface. /// The `derived` concept does not define variant-level callbacks, /// so this function always returns `nullptr`. /// /// @tparam Capability Encoding capability index /// @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 Always `nullptr` /// template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This callback is invoked to determine whether the `derived` /// concept should be activated for a given encoding request. /// /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Matcher function pointer /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &derivedMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/derived/derivedMatcher.h0000664000175000017500000000646015232567717027214 0ustar alastairalastair/* * (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 derivedMatcher.h /// @brief Entry-level matcher for the GRIB `derived` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether derived ensemble-product metadata is active for a request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include #include // Utils #include "metkit/mars2grib/backend/concepts/derived/derivedEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `derived` concept variant. /// /// The concept is active for ensemble-derived MARS `type` values such as /// ensemble mean, spread, EFI, and shift-of-tails products. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `DerivedType::Default`, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t derivedMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; const auto& type = get_or_throw(mars, "type"); if (type == "em" || // Ensemble mean type == "es" || // Ensemble standard deviation type == "ses" || // Ensemble spread of estimation type == "taem" || // Time-averaged ensemble mean type == "taes" || // Time-averaged ensemble standard deviation type == "efi" || // Extreme forecast index type == "efic" // Extreme forecast index ) { return static_cast(DerivedType::Default); } if (type == "sot") { return static_cast(DerivedType::ShiftOfTails); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `derived` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/derived/derivedEncoding.h0000664000175000017500000002037115232567717027354 0ustar alastairalastair/* * (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 derivedEncoding.h /// @brief Implementation of the GRIB `derived` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **derived concept** within the mars2grib backend. /// /// The concept is responsible for populating GRIB keys related to /// *derived ensemble products*, including: /// /// - `derivedForecast` /// - `numberOfForecastsInEnsemble` /// /// These keys are encoded in the Product Definition Section and are used /// to describe ensemble-derived statistical products (e.g. means, spreads, /// probabilities). /// /// The concept implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `derivedApplicable` /// - Runtime structural validation of the Product Definition Section /// - Delegation of semantic deduction to dedicated deduction functions /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/derived/derivedEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/channel.h" #include "metkit/mars2grib/backend/deductions/derivedForecast.h" #include "metkit/mars2grib/backend/deductions/numberOfForecastsInEnsemble.h" #include "metkit/mars2grib/backend/deductions/numberOfFrequencies.h" // Tables #include "metkit/mars2grib/backend/tables/derivedForecast.h" // Checks #include "metkit/mars2grib/backend/checks/checkDerivedProductDefinitionSection.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 `derived` concept. /// /// This predicate determines whether the `derived` 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 ensure that only valid concept instantiations occur. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Derived 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: /// - `Stage == StagePreset` /// - `Section == SecProductDefinitionSection` /// /// @todo [owner: mds][scope: concept][reason: correctness][prio: medium] /// - Refine applicability rules once derived product usage is fully constrained /// by stage, section, or variant. /// template constexpr bool derivedApplicable() { return (Stage == StagePreset) && ((Section == SecProductDefinitionSection) || (Section == SecLocalUseSection)); } /// /// @brief Execute the `derived` concept operation. /// /// This function implements the runtime logic of the GRIB `derived` concept. /// When applicable, it: /// /// 1. Validates that the Product Definition Section is compatible with /// derived ensemble products. /// 2. Deduces the type of derived forecast from MARS and parameter dictionaries. /// 3. Deduces the number of ensemble members involved. /// 4. Encodes the corresponding GRIB keys in the output dictionary. /// /// The concept acts as a **coordination layer**: /// - Structural validation is performed explicitly. /// - Semantic deduction is delegated to backend deductions. /// - Value correctness is guaranteed by GRIB table-backed enumerations. /// /// 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 Derived 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: /// - structural validation of the Product Definition Section fails /// - a deduction fails /// - a GRIB key cannot be set /// /// @note /// - All runtime errors are wrapped with full concept context /// (concept name, variant, stage, section). /// - This concept does not rely on any pre-existing GRIB header state. /// /// @see derivedApplicable /// template void DerivedOp(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 (derivedApplicable()) { try { MARS2GRIB_LOG_CONCEPT(derived); if constexpr (Variant == DerivedType::Default) { if constexpr (Section == SecProductDefinitionSection && Stage == StagePreset) { // Structural validation validation::check_DerivedProductDefinitionSection_or_throw(opt, out); // Deductions tables::DerivedForecast derivedForecast = deductions::resolve_DerivedForecast_or_throw(mars, par, opt); long numberOfForecastsInEnsemble = deductions::resolve_NumberOfForecastsInEnsemble_or_throw(mars, par, opt); // Encoding set_or_throw(out, "derivedForecast", static_cast(derivedForecast)); set_or_throw(out, "numberOfForecastsInEnsemble", numberOfForecastsInEnsemble); } } if constexpr (Variant == DerivedType::ShiftOfTails) { if constexpr (Section == SecProductDefinitionSection && Stage == StagePreset) { // Structural validation // validation::check_DerivedProductDefinitionSection_or_throw(opt, out); // Deductions tables::DerivedForecast derivedForecast = deductions::resolve_DerivedForecast_or_throw(mars, par, opt); // long numberOfForecastsInEnsemble = // deductions::resolve_NumberOfForecastsInEnsemble_or_throw(mars, par, opt); // Encoding set_or_throw(out, "derivedForecast", static_cast(derivedForecast)); // set_or_throw(out, "numberOfForecastsInEnsemble", numberOfForecastsInEnsemble); } } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(derived, "Unable to set `derived` concept..."); } return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(derived, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/iteration/0000775000175000017500000000000015232567717024463 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/iteration/iterationConceptDescriptor.h0000664000175000017500000001300615232567717032205 0ustar alastairalastair/* * (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 iterationConceptDescriptor.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_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/iteration/iterationEncoding.h0000664000175000017500000001465215232567717030311 0ustar alastairalastair/* * (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 iterationEncoding.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_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/iteration/iterationEnum.h0000664000175000017500000001052515232567717027462 0ustar alastairalastair/* * (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 /// `iterationEncoding.h` 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_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/iteration/iterationMatcher.h0000664000175000017500000000345515232567717030145 0ustar alastairalastair/* * (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 iterationMatcher.h /// @brief Entry-level matcher for the GRIB `iteration` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether the **iteration concept** is active for a MARS request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active. /// /// @note /// The namespace name `concepts_` is intentionally used instead of /// `concepts` to avoid conflicts with the C++20 `concept` language /// feature. /// /// @ingroup mars2grib_backend_concepts /// #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_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/0000775000175000017500000000000015232567717027170 5ustar alastairalastair././@LongLink0000644000000000000000000000016400000000000011604 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureConceptDescriptor.hmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureConc0000664000175000017500000001443015232567717034306 0ustar alastairalastair/* * (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 brightnessTemperatureConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `brightnessTemperature` concept. /// /// This header defines `BrightnessTemperatureConcept`, the **compile-time /// descriptor** that registers the GRIB `brightnessTemperature` 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 includes #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/brightness-temperature/brightnessTemperatureEncoding.h" #include "metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureEnum.h" #include "metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureMatcher.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 `brightnessTemperature` concept. /// /// `BrightnessTemperatureConcept` registers the GRIB `brightnessTemperature` /// 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 BrightnessTemperatureConcept : 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 brightnessTemperatureName; } /// @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 brightnessTemperatureTypeName(); } /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the callback /// implementing the `brightnessTemperature` 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 (brightnessTemperatureApplicable()) { return &BrightnessTemperatureOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } /// @brief Variant-specific callbacks. /// /// The brightnessTemperature concept does not currently require /// variant-specific runtime callbacks. All runtime behavior is handled /// through phase callbacks. /// /// @return Always `nullptr` template static constexpr Fn variantCallbacks() { return nullptr; } /// @brief Entry-level matcher callback. /// /// This callback is used by the registry engine to decide whether the /// brightnessTemperature concept is active for a given MARS request. /// /// @tparam Capability Encoding capability index /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Function pointer to the matcher, or `nullptr` template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &brightnessTemperatureMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureEncoding.hmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureEnco0000664000175000017500000001540215232567717034310 0ustar alastairalastair/* * (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 brightnessTemperatureEncoding.h /// @brief Implementation of the GRIB `brightnessTemperature` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **brightnessTemperature concept** within the mars2grib backend. /// /// The brightnessTemperature concept is responsible for encoding GRIB keys /// associated with brightness-temperature metadata stored in the Local Use /// Section, specifically: /// /// - `channelNumber` /// - `numberOfFrequencies` /// /// These fields identify the satellite channel and the number of frequencies /// associated with the brightness-temperature product. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `brightnessTemperatureApplicable` /// - 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/brightness-temperature/brightnessTemperatureEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/channel.h" #include "metkit/mars2grib/backend/deductions/numberOfFrequencies.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 `brightnessTemperature` concept. /// /// This predicate determines whether the brightnessTemperature 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 /// @tparam Section GRIB section index /// @tparam Variant Brightness-temperature 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 == BrightnessTemperatureType::Default` /// - `Stage == StagePreset` /// - `Section == SecLocalUseSection` template constexpr bool brightnessTemperatureApplicable() { return ((Stage == StagePreset) && (Section == SecLocalUseSection)); } /// @brief Execute the `brightnessTemperature` concept operation. /// /// This function implements the runtime logic of the GRIB /// `brightnessTemperature` concept. /// /// When applicable, it: /// /// 1. Validates that the Local Use Section matches the expected definition. /// 2. Deduces the brightness-temperature related 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 /// @tparam Section GRIB section index /// @tparam Variant Brightness-temperature 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 decide whether the surrounding product is encoded /// through the satellite path or the derived-product path. /// /// @see brightnessTemperatureApplicable template void BrightnessTemperatureOp(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 (brightnessTemperatureApplicable()) { try { MARS2GRIB_LOG_CONCEPT(brightnessTemperature); // Preconditions / contracts validation::match_LocalDefinitionNumber_or_throw(opt, out, {37L}); // number of frequencies is always 1 for brightness temperature products auto numberOfFrequenciesVal = deductions::resolve_NumberOfFrequencies_or_throw(mars, par, opt); set_or_throw(out, "numberOfFrequencies", numberOfFrequenciesVal); // In Ensemble Mean variant, channel number is required; in Default variant it is already set by the // satellite concept if constexpr (Variant == BrightnessTemperatureType::EnsembleMean) { auto channelNumberVal = deductions::resolve_Channel_or_throw(mars, par, opt); set_or_throw(out, "channelNumber", channelNumberVal); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(brightnessTemperature, "Unable to set `brightnessTemperature` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(brightnessTemperature, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureMatcher.hmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureMatc0000664000175000017500000001027015232567717034306 0ustar alastairalastair/* * (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 brightnessTemperatureMatcher.h /// @brief Entry-level matcher for the GRIB `brightnessTemperature` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether the **brightnessTemperature concept** is active for a MARS request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active. /// /// @note /// The namespace name `concepts_` is intentionally used instead of /// `concepts` to avoid conflicts with the C++20 `concept` language /// feature. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include #include // Utils #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// @brief Entry-level matcher for the `brightnessTemperature` concept. /// /// The concept is activated for brightness-temperature products identified by: /// /// - `param == 194` /// - `stream == "oper"` or `stream == "elda"` /// - presence of the MARS key `channel` /// /// The stream is intentionally **not** represented as a concept variant. /// It only determines which surrounding product-family concept is active: /// /// - `elda` may coexist with the satellite concept /// - `oper` may coexist with the derived-product concept /// /// The brightness-temperature concept itself owns only the common /// brightness-temperature Local Use Section metadata. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Index of the selected concept variant, or /// `compile_time_registry_engine::MISSING` if the concept does not apply /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If the request is identified as a brightness-temperature request but the /// mandatory `channel` key is missing. template std::size_t brightnessTemperatureMatcher(const MarsDict_t& mars, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; // Concept does not apply unless "param" is present and equals 194 if (!has(mars, "param") || get_or_throw(mars, "param") != 194) { return compile_time_registry_engine::MISSING; } // Concept does not apply unless the stream is explicitly supported if (!has(mars, "stream")) { return compile_time_registry_engine::MISSING; } const auto& stream = get_or_throw(mars, "stream"); if (stream != "oper" && stream != "elda") { return compile_time_registry_engine::MISSING; } // At this point the request is a brightness-temperature request: // "channel" is mandatory if (!has(mars, "channel")) { throw utils::exceptions::Mars2GribMatcherException( "brightnessTemperature concept requires MARS key \"channel\" " "when param=194 and stream is either \"oper\" or \"elda\"", Here()); } if (stream == "elda") { return static_cast(BrightnessTemperatureType::EnsembleMean); } else if (stream == "oper") { return static_cast(BrightnessTemperatureType::Default); } return compile_time_registry_engine::MISSING; } } // namespace metkit::mars2grib::backend::concepts_././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureEnum.hmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/brightness-temperature/brightnessTemperatureEnum0000664000175000017500000001112715232567717034330 0ustar alastairalastair/* * (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 brightnessTemperatureEnum.h /// @brief Definition of the `brightnessTemperature` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB /// `brightnessTemperature` concept used by the mars2grib backend. /// It contains: /// /// - the canonical concept name (`brightnessTemperatureName`) /// - the enumeration of supported brightness-temperature variants /// - a compile-time typelist of all variants /// - 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 /// Brightness temperature is represented as an independent concept because /// it is orthogonal to the surrounding product-family concept. Depending on /// the stream, the same parameter may coexist with either the satellite path /// or the derived-product path. /// /// @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 `brightnessTemperature` 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 concept /// /// The value must remain stable across releases. inline constexpr std::string_view brightnessTemperatureName{"brightnessTemperature"}; /// @brief Enumeration of all supported `brightnessTemperature` concept variants. /// /// The concept currently has a single variant because both supported streams /// share the same Local Use Section encoding logic. /// /// 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 /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. enum class BrightnessTemperatureType : std::size_t { EnsembleMean = 0, Default }; /// @brief Compile-time list of all `brightnessTemperature` 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 BrightnessTemperatureList = ValueList; /// @brief Compile-time mapping from `BrightnessTemperatureType` to human-readable name. /// /// This function returns the canonical string identifier associated with a /// given brightness-temperature variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Brightness-temperature 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 brightnessTemperatureTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view brightnessTemperatureTypeName() { \ return NAME; \ } DEF(BrightnessTemperatureType::EnsembleMean, "ensembleMean"); DEF(BrightnessTemperatureType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_metkit-1.19.2/src/metkit/mars2grib/backend/concepts/model-error/0000775000175000017500000000000015232567717024714 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/model-error/modelErrorEncoding.h0000664000175000017500000001573415232567717030660 0ustar alastairalastair/* * (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 modelErrorEncoding.h /// @brief Implementation of the GRIB `modelError` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **modelError concept** within the mars2grib backend. /// /// The modelError concept is responsible for encoding GRIB keys related to /// *model-error metadata* stored in the Local Use Section, specifically: /// /// - `componentIndex` /// - `numberOfComponents` /// - `modelErrorType` /// /// These fields identify the realization within a model-error ensemble, /// the total number of realizations, and the type of model error. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `modelErrorApplicable` /// - 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/model-error/modelErrorEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/componentIndex.h" #include "metkit/mars2grib/backend/deductions/modelErrorType.h" #include "metkit/mars2grib/backend/deductions/numberOfComponents.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 `modelError` concept. /// /// This predicate determines whether the modelError 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 ModelError 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 == ModelErrorType::Default` /// - `Stage == StagePreset` /// - `Section == SecLocalUseSection` /// template constexpr bool modelErrorApplicable() { return ((Stage == StagePreset) && (Section == SecLocalUseSection)); } /// /// @brief Execute the `modelError` concept operation. /// /// This function implements the runtime logic of the GRIB `modelError` concept. /// When applicable, it: /// /// 1. Validates that the Local Use Section matches the expected definition. /// 2. Deduces the model-error related 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 ModelError 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 modelErrorApplicable /// template void ModelErrorOp(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 (modelErrorApplicable()) { try { MARS2GRIB_LOG_CONCEPT(modelError); if (Variant == ModelErrorType::ComponentIndex) { validation::match_LocalDefinitionNumber_or_throw(opt, out, {25L, 39L}); // Deductions auto componentIndexVal = deductions::resolve_ComponentIndex_or_throw(mars, par, opt); auto numberOfComponentsVal = deductions::resolve_NumberOfComponents_or_throw(mars, par, opt); auto modelErrorTypeVal = deductions::resolve_ModelErrorType_or_throw(mars, par, opt); // Encoding set_or_throw(out, "componentIndex", componentIndexVal); set_or_throw(out, "numberOfComponents", numberOfComponentsVal); set_or_throw(out, "modelErrorType", modelErrorTypeVal); } else if (Variant == ModelErrorType::FourierCoefficients) { validation::match_LocalDefinitionNumber_or_throw(opt, out, {45L}); MARS2GRIB_CONCEPT_THROW(modelError, "Variant not implemented..."); } else { MARS2GRIB_CONCEPT_THROW(modelError, "Unknown variant..."); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(modelError, "Unable to set `modelError` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(modelError, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/model-error/modelErrorMatcher.h0000664000175000017500000000617315232567717030512 0ustar alastairalastair/* * (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 modelErrorMatcher.h /// @brief Entry-level matcher for the GRIB `modelError` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether the **modelError concept** is active for a MARS request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active. /// /// @note /// The namespace name `concepts_` is intentionally used instead of /// `concepts` to avoid conflicts with the C++20 `concept` language /// feature. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/backend/concepts/model-error/modelErrorEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { template std::size_t modelErrorMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; if (!has(mars, "type")) { throw metkit::mars2grib::utils::exceptions::Mars2GribMatcherException( "MARS key `type` is required to determine applicability of the `modelError` concept but is missing. " "This is a contract violation by the upstream tool that populates the MARS dictionary.", Here()); } // Concept does not apply unless "type" is present and equals "eme" if ((get_or_throw(mars, "type") != "eme" && get_or_throw(mars, "type") != "me")) { return compile_time_registry_engine::MISSING; } // At this point the request is a model-error request: "number" is mandatory if (has(mars, "number")) { return static_cast(ModelErrorType::ComponentIndex); } if (has(mars, "coeffindex")) { return static_cast(ModelErrorType::FourierCoefficients); } return compile_time_registry_engine::MISSING; } catch (...) { // Rethrow nested exceptions with a more specific message std::throw_with_nested(metkit::mars2grib::utils::exceptions::Mars2GribMatcherException( "An error occurred while matching the `modelError` concept. Check nested exception for details.", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/model-error/modelErrorEnum.h0000664000175000017500000001100215232567717030016 0ustar alastairalastair/* * (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 modelErrorEnum.h /// @brief Definition of the `modelError` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `modelError` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`modelErrorName`) /// - the enumeration of supported model-error variants (`ModelErrorType`) /// - a compile-time typelist of all variants (`ModelErrorList`) /// - 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 /// `modelErrorEncoding.h` 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 `modelError` 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 `modelError` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view modelErrorName{"modelError"}; /// /// @brief Enumeration of all supported `modelError` concept variants. /// /// Each enumerator represents a specific model-error /// 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 model-error concept evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class ModelErrorType : std::size_t { ComponentIndex = 0, FourierCoefficients }; /// /// @brief Compile-time list of all `modelError` 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 ModelErrorList = ValueList; /// /// @brief Compile-time mapping from `ModelErrorType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given model-error variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Model-error 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 modelErrorTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view modelErrorTypeName() { \ return NAME; \ } DEF(ModelErrorType::ComponentIndex, "componentIndex"); DEF(ModelErrorType::FourierCoefficients, "fourierCoefficients"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/model-error/modelErrorConceptDescriptor.h0000664000175000017500000001304215232567717032552 0ustar alastairalastair/* * (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 modelErrorConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `modelError` concept. /// /// This header defines `ModelErrorConcept`, the **compile-time descriptor** /// that registers the GRIB `modelError` 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/model-error/modelErrorEncoding.h" #include "metkit/mars2grib/backend/concepts/model-error/modelErrorEnum.h" #include "metkit/mars2grib/backend/concepts/model-error/modelErrorMatcher.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 `modelError` concept. /// /// `ModelErrorConcept` registers the GRIB `modelError` 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 ModelErrorConcept : 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 modelErrorName; } /// /// @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 modelErrorTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `modelError` 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 (modelErrorApplicable()) { return &ModelErrorOp; } 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 &modelErrorMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/origin/0000775000175000017500000000000015232567717023754 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/origin/originConceptDescriptor.h0000664000175000017500000001171215232567717030771 0ustar alastairalastair/* * (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 originConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `origin` concept. /// /// This header defines `OriginConcept`, the **compile-time descriptor** /// that registers the GRIB `origin` 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/origin/originEncoding.h" #include "metkit/mars2grib/backend/concepts/origin/originEnum.h" #include "metkit/mars2grib/backend/concepts/origin/originMatcher.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 `origin` concept. /// /// `OriginConcept` registers the GRIB `origin` concept into the /// compile-time registry engine. /// struct OriginConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return originName; } /// /// @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 originTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `origin` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (originApplicable()) { return &OriginOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `origin` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &originMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/origin/originMatcher.h0000664000175000017500000000437615232567717026732 0ustar alastairalastair/* * (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 originMatcher.h /// @brief Entry-level matcher for the GRIB `origin` concept. /// /// This header defines the runtime matcher used by the concept registry to /// activate the default originating-centre concept variant. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/origin/originEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `origin` concept variant. /// /// The origin concept is always active and resolves to `OriginType::Default`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `OriginType::Default`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t originMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { return static_cast(OriginType::Default); } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `origin` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/origin/originEnum.h0000664000175000017500000001035015232567717026240 0ustar alastairalastair/* * (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 originEnum.h /// @brief Definition of the `origin` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `origin` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`originName`) /// - the enumeration of supported origin variants (`OriginType`) /// - a compile-time typelist of all variants (`OriginList`) /// - 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 /// `originEncoding.h` 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 `origin` 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 `origin` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view originName{"origin"}; /// /// @brief Enumeration of all supported `origin` concept variants. /// /// Each enumerator represents a specific origin or provenance /// classification 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 origin concept evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class OriginType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `origin` 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 OriginList = ValueList; /// /// @brief Compile-time mapping from `OriginType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given origin variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Origin 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 originTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view originTypeName() { \ return NAME; \ } DEF(OriginType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/origin/originEncoding.h0000664000175000017500000001310015232567717027056 0ustar alastairalastair/* * (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 originEncoding.h /// @brief Implementation of the GRIB `origin` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **origin concept** within the mars2grib backend. /// /// The origin concept is responsible for encoding GRIB metadata identifying /// the producing centre and sub-centre. In the current backend this is written /// into the Identification Section using: /// - `origin` (string centre identifier) /// - `subCentre` (numeric sub-centre identifier) /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `originApplicable` /// - Runtime deduction /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/origin/originEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/centre.h" #include "metkit/mars2grib/backend/deductions/subCentre.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 `origin` concept. /// /// The default applicability enables this concept only when: /// - `Variant == OriginType::Default` /// - `Stage == StagePreset` /// - `Section == SecIdentificationSection` /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Origin concept variant /// /// @return `true` if the concept is applicable for the given parameters, /// `false` otherwise. /// template constexpr bool originApplicable() { // Conditions to apply concept return ((Variant == OriginType::Default) && (Stage == StagePreset) && (Section == SecIdentificationSection)); } /// /// @brief Execute the `origin` concept operation. /// /// When applicable, this concept: /// 1. Deduces the producing centre (`origin`) from the MARS dictionary. /// 2. Deduces the numeric sub-centre (`subCentre`) from the parameter dictionary, /// defaulting to `0` when not provided (as implemented by the deduction). /// 3. Encodes both keys into the output GRIB dictionary. /// /// If 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 Origin 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 concept is called when not applicable /// - any deduction or encoding step fails /// /// @note /// This concept does not rely on any pre-existing GRIB header state. /// /// @see originApplicable /// @see metkit::mars2grib::backend::deductions::resolve_Centre_or_throw /// @see metkit::mars2grib::backend::deductions::resolve_SubCentre_or_throw /// template void OriginOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) noexcept(false) { using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (originApplicable()) { try { MARS2GRIB_LOG_CONCEPT(origin); // Deductions std::string centre = deductions::resolve_Centre_or_throw(mars, par, opt); long subCentre = deductions::resolve_SubCentre_or_throw(mars, par, opt); // Encoding set_or_throw(out, "centre", centre); set_or_throw(out, "subCentre", subCentre); } catch (...) { MARS2GRIB_CONCEPT_RETHROW(origin, "Unable to set `origin` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(origin, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/mars/0000775000175000017500000000000015232567717023427 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/mars/marsMatcher.h0000664000175000017500000000522715232567717026054 0ustar alastairalastair/* * (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 marsMatcher.h /// @brief Entry-level matcher for the GRIB `mars` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether MARS provenance metadata is active for a request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/mars/marsEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `mars` concept variant. /// /// The concept is active as `MarsType::Default` when `class`, `type`, `stream`, /// and `expver` are present in the MARS request. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `MarsType::Default`, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t marsMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::has; if (has(mars, "class") && has(mars, "type") && has(mars, "stream") && has(mars, "expver")) { return static_cast(MarsType::Default); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException("Unable to match `mars` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/mars/marsConceptDescriptor.h0000664000175000017500000001261115232567717030116 0ustar alastairalastair/* * (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 marsConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `mars` concept. /// /// This header defines `MarsConcept`, the **compile-time descriptor** /// that registers the GRIB `mars` 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/mars/marsEncoding.h" #include "metkit/mars2grib/backend/concepts/mars/marsEnum.h" #include "metkit/mars2grib/backend/concepts/mars/marsMatcher.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 `mars` concept. /// /// `MarsConcept` registers the GRIB `mars` 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 MarsConcept : 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 marsName; } /// /// @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 marsTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `mars` 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 (marsApplicable()) { return &MarsOp; } 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 &marsMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/mars/marsEnum.h0000664000175000017500000001027015232567717025367 0ustar alastairalastair/* * (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 marsEnum.h /// @brief Definition of the `mars` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `mars` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`marsName`) /// - the enumeration of supported MARS variants (`MarsType`) /// - a compile-time typelist of all variants (`MarsList`) /// - 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 /// `marsEncoding.h` 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 `mars` 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 `mars` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view marsName{"mars"}; /// /// @brief Enumeration of all supported `mars` concept variants. /// /// Each enumerator represents a specific MARS-related semantic or /// processing context 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 MARS concept evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class MarsType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `mars` 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 MarsList = ValueList; /// /// @brief Compile-time mapping from `MarsType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given MARS variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T MARS 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 marsTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view marsTypeName() { \ return NAME; \ } DEF(MarsType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/mars/marsEncoding.h0000664000175000017500000001706015232567717026215 0ustar alastairalastair/* * (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 marsEncoding.h /// @brief Implementation of the GRIB `mars` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **mars concept** within the mars2grib backend. /// /// The mars concept is responsible for encoding core MARS identity metadata /// into the GRIB *Local Use Section*, specifically: /// /// - `class` /// - `type` /// - `stream` /// - `expver` /// /// These fields collectively define the provenance and classification of the /// encoded product and are required by downstream systems and workflows. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `marsApplicable` /// - Runtime structural validation of the Local Use Section /// - Explicit deduction of all required MARS identity fields /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/mars/marsEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/class.h" #include "metkit/mars2grib/backend/deductions/expver.h" #include "metkit/mars2grib/backend/deductions/stream.h" #include "metkit/mars2grib/backend/deductions/type.h" // checks #include "metkit/mars2grib/backend/checks/checkLocalUseSection.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 `mars` concept. /// /// This predicate determines whether the mars 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 ensure that only valid concept invocations /// are instantiated. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Mars 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 == MarsType::Default` /// - `Stage == StagePreset` /// - `Section == SecLocalUseSection` /// template constexpr bool marsApplicable() { // Confitions to apply concept return ((Variant == MarsType::Default) && (Stage == StagePreset) && (Section == SecLocalUseSection)); } /// /// @brief Execute the `mars` concept operation. /// /// This function implements the runtime logic of the GRIB `mars` concept. /// When applicable, it: /// /// 1. Validates the structural integrity of the GRIB Local Use Section. /// 2. Deduces core MARS identity fields from the input dictionaries. /// 3. Encodes the corresponding GRIB keys in the output dictionary. /// /// The concept establishes the fundamental identity of the GRIB message /// and is typically a prerequisite for other Local Use Section concepts. /// /// 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 Mars 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 Use Section is structurally invalid, /// - any required MARS field cannot be deduced, /// - 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. /// /// @note /// The keywords [marsClass, marsType, marsStream] correspond to *raw GRIB keys* /// and are written directly without triggering additional logic. /// /// In contrast, the high-level keywords [class, type, stream] are **ecCodes /// concepts**. Setting them may implicitly modify multiple underlying GRIB /// keys in order to maintain internal consistency. /// /// As a consequence, assigning high-level keywords can have side effects. /// Examples (non-exhaustive) include: /// - setting "type" may implicitly update "typeOfProcessedData" /// - setting "stream" may implicitly change the product definition template number /// /// @see marsApplicable /// template void MarsOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; // eccodes/definitions/grib2/local.98.36.def if constexpr (marsApplicable()) { try { MARS2GRIB_LOG_CONCEPT(mars); // Preconditions/contracts validation::check_LocalUseSection_or_throw(opt, out); // Deductions std::string marsClassVal = deductions::resolve_Class_or_throw(mars, par, opt); std::string marsTypeVal = deductions::resolve_Type_or_throw(mars, par, opt); std::string marsStreamVal = deductions::resolve_Stream_or_throw(mars, par, opt); std::string marsExpverVal = deductions::resolve_Expver_or_throw(mars, par, opt); // Encoding set_or_throw(out, "marsClass", marsClassVal); set_or_throw(out, "marsType", marsTypeVal); set_or_throw(out, "marsStream", marsStreamVal); set_or_throw(out, "expver", marsExpverVal); } catch (...) { MARS2GRIB_CONCEPT_RETHROW(mars, "Unable to set `mars` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(mars, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/tables/0000775000175000017500000000000015232567717023737 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/tables/tablesEncoding.h0000664000175000017500000001726715232567717027046 0ustar alastairalastair/* * (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 tablesEncoding.h /// @brief Implementation of the GRIB tables-versioning concept (`tables`). /// /// This header defines the applicability rules and execution logic for the /// **tables concept** within the mars2grib backend. /// /// The concept is responsible for selecting and encoding the GRIB2 tables /// versions used to interpret code tables and definitions in the produced /// message: /// - `tablesVersion` /// - `localTablesVersion` /// /// The concept is executed during the **allocation stage** for the /// *Identification Section* (Section 1). The rationale is that table versions /// influence how subsequent keys may be interpreted by ecCodes and downstream /// tools, and therefore should be established early and deterministically. /// /// Two variants are supported: /// - `TablesType::Default`: use the latest tables version provided by ecCodes. /// - `TablesType::Custom`: override the tables version from the parametrization /// dictionary. /// /// All failures are wrapped in a `Mars2GribConceptException` providing full /// concept context (concept name, variant, stage, section). /// /// @note /// The namespace name `concepts_` is intentionally used instead of `concepts` /// to avoid potential conflicts with the C++20 `concepts` language feature. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/tables/tablesEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/localTablesVersion.h" #include "metkit/mars2grib/backend/deductions/tablesVersion.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 `tables` concept. /// /// This predicate determines whether the `tables` concept is applicable for /// a given combination of: /// - encoding stage /// - GRIB section /// - concept variant /// /// The default applicability rule enables this concept only when: /// - `Stage == StageAllocate` /// - `Section == SecIdentificationSection` /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Tables concept variant /// /// @return `true` if applicable, `false` otherwise. /// template constexpr bool tablesApplicable() { // Conditions to apply concept return ((Stage == StageAllocate) && (Section == SecIdentificationSection)); } /// /// @brief Execute the `tables` concept operation. /// /// This function sets GRIB table versioning keys in the output dictionary: /// - `tablesVersion` /// - `localTablesVersion` /// /// The logic is variant-dependent: /// /// ### Variant `TablesType::Default` /// - `tablesVersion` is derived from an ecCodes GRIB2 sample via /// `resolve_TablesVersionLatest_or_throw()`. /// - `localTablesVersion` is deduced via /// `resolve_LocalTablesVersion_or_throw()` (currently returns `0`). /// /// ### Variant `TablesType::Custom` /// - `tablesVersion` is taken from the parametrization dictionary (override) /// via `resolve_TablesVersionCustom_or_throw()`. /// - `localTablesVersion` is deduced via /// `resolve_LocalTablesVersion_or_throw()`. /// /// The concept is intended to be deterministic: either it explicitly sets both /// keys, or it fails with an exception. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Tables 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 (unused by the current implementation) /// @param[in] par Parameter dictionary (used for custom tables override) /// @param[in] opt Options dictionary /// @param[out] out Output GRIB dictionary to be populated /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribConceptException /// If: /// - the concept is invoked outside its applicability domain /// - any table version deduction fails /// - any output encoding step fails /// /// @note /// The `tablesVersion` key may affect downstream decoding and validation; /// therefore, it should be set before any logic that depends on specific /// table entries or local definitions. /// /// @warning /// In the provided implementation, the call: /// `resolve_TablesVersionCustom_or_throw(par, par, opt)` /// appears inconsistent with the signature previously shown for that deduction /// (expected `(mars, par, opt)`). If the intention is to read only from `par`, /// consider either: /// - adjusting the deduction signature, or /// - passing the correct `mars` dictionary as first argument. /// /// @see tablesApplicable /// @see deductions::resolve_TablesVersionLatest_or_throw /// @see deductions::resolve_TablesVersionCustom_or_throw /// @see deductions::resolve_LocalTablesVersion_or_throw /// template void TablesOp(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 (tablesApplicable()) { try { MARS2GRIB_LOG_CONCEPT(tables); // Global deductions long localTablesVersionVal = deductions::resolve_LocalTablesVersion_or_throw(mars, par, opt); // set in output dictionary if constexpr (Variant == TablesType::Custom) { // Deductions long tablesVersionVal = deductions::resolve_TablesVersionCustom_or_throw(mars, par, opt); // Encoding set_or_throw(out, "tablesVersion", tablesVersionVal); set_or_throw(out, "localTablesVersion", localTablesVersionVal); } else if constexpr (Variant == TablesType::Default) { // Deductions long tablesVersionVal = deductions::resolve_TablesVersionLatest_or_throw(mars, par, opt); // Encoding set_or_throw(out, "tablesVersion", tablesVersionVal); set_or_throw(out, "localTablesVersion", localTablesVersionVal); } else { MARS2GRIB_CONCEPT_THROW(tables, "Unsupported variant for `tables` concept..."); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(tables, "Unable to set `tables` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(tables, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/tables/tablesConceptDescriptor.h0000664000175000017500000001171215232567717030737 0ustar alastairalastair/* * (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 tablesConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `tables` concept. /// /// This header defines `TablesConcept`, the **compile-time descriptor** /// that registers the GRIB `tables` 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/tables/tablesEncoding.h" #include "metkit/mars2grib/backend/concepts/tables/tablesEnum.h" #include "metkit/mars2grib/backend/concepts/tables/tablesMatcher.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 `tables` concept. /// /// `TablesConcept` registers the GRIB `tables` concept into the /// compile-time registry engine. /// struct TablesConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return tablesName; } /// /// @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 tablesTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `tables` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (tablesApplicable()) { return &TablesOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `tables` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &tablesMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/tables/tablesMatcher.h0000664000175000017500000000506415232567717026673 0ustar alastairalastair/* * (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 tablesMatcher.h /// @brief Entry-level matcher for the GRIB `tables` concept. /// /// This header defines the runtime matcher used by the concept registry to /// activate the default GRIB tables concept variant. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/tables/tablesEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/paramMatcher.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `tables` concept variant. /// /// The tables concept is always active and resolves to `TablesType::Default`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `TablesType::Default`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t tablesMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; using metkit::mars2grib::utils::dict_traits::get_or_throw; return static_cast(TablesType::Default); } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `tables` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/tables/tablesEnum.h0000664000175000017500000001042515232567717026211 0ustar alastairalastair/* * (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 tablesEnum.h /// @brief Definition of the `tables` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `tables` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`tablesName`) /// - the enumeration of supported table-selection variants (`TablesType`) /// - a compile-time typelist of all variants (`TablesList`) /// - 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 /// `tablesEncoding.h` 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 `tables` 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 `tables` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view tablesName{"tables"}; /// /// @brief Enumeration of all supported `tables` concept variants. /// /// Each enumerator represents a specific GRIB table-selection strategy /// used during encoding. /// /// 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 distinguishes between default GRIB tables and /// custom or locally defined table sets. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class TablesType : std::size_t { Custom = 0, Default }; /// /// @brief Compile-time list of all `tables` 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 TablesList = ValueList; /// /// @brief Compile-time mapping from `TablesType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given tables variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Tables 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 tablesTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view tablesTypeName() { \ return NAME; \ } DEF(TablesType::Custom, "custom"); DEF(TablesType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/EncodingCallbacksRegistry.h0000664000175000017500000002231315232567717027716 0ustar alastairalastair/* * (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 EncodingCallbacksRegistry.h /// @brief Compile-time registry of phase-level encoding callbacks. /// /// This header defines the **fully materialized encoding dispatch table** /// used by the mars2grib backend to perform GRIB header encoding. /// /// ----------------------------------------------------------------------------- /// Conceptual model /// ----------------------------------------------------------------------------- /// /// The encoding process is modeled as a three-dimensional dispatch space: /// /// \code /// encodingCallbacks[globalVariant][stage][section] -> Fn | nullptr /// \endcode /// /// where: /// - `globalVariant` is the flattened variant index defined by `GeneralRegistry`, /// - `stage` is a logical encoding phase (allocation, preset, override, runtime), /// - `section` is a GRIB2 section identifier. /// /// Each cell contains either: /// - a fully specialized encoding function (`Fn`), or /// - `nullptr`, indicating that the combination is not applicable. /// /// ----------------------------------------------------------------------------- /// Capability selection /// ----------------------------------------------------------------------------- /// /// This registry is parameterized by a compile-time **capability index** /// (here fixed to `0`). /// /// Capabilities allow the same concept universe to expose multiple independent /// dispatch planes (e.g. encoding, matching, validation) without duplicating /// registry machinery. /// /// ----------------------------------------------------------------------------- /// Scope and responsibility /// ----------------------------------------------------------------------------- /// /// This file is responsible only for: /// - binding the generic phase-callback registry machinery /// (`makePhaseCallbacksRegistry`) to: /// - the complete concept universe (`AllConcepts`), /// - a specific capability index, /// - concrete dictionary types. /// /// It does **not**: /// - define any concept descriptors, /// - implement any encoding logic, /// - perform any runtime dispatch, /// - introduce new compile-time algorithms. /// /// ----------------------------------------------------------------------------- /// Architectural role /// ----------------------------------------------------------------------------- /// /// `EncodingCallbacksRegistry` is the **lowest-level executable view** /// of the compile-time registry engine. /// /// Higher-level systems (e.g. encoding plan construction and hot-path execution) /// consume this registry as immutable, constexpr data. /// /// ----------------------------------------------------------------------------- /// Design constraints /// ----------------------------------------------------------------------------- /// /// - Header-only /// - Fully constexpr /// - No runtime state /// - No dynamic allocation /// /// This header is safe to include in performance-critical translation units. /// /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include // Project includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/compile-time-registry-engine/makePhaseCallbacksRegistry.h" #include "metkit/mars2grib/backend/concepts/AllConcepts.h" #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Compile-time registry exposing encoding callbacks for all concepts. /// /// This class template materializes a complete, three-dimensional dispatch /// table for encoding operations, specialized for a fixed set of dictionary /// types. /// /// @tparam MarsDict_t /// Type of the MARS request dictionary. /// /// @tparam ParDict_t /// Type of the parameter dictionary. /// /// @tparam OptDict_t /// Type of the encoder options dictionary. /// /// @tparam OutDict_t /// Type of the output GRIB handle or dictionary. /// /// ----------------------------------------------------------------------------- /// Lifetime and usage /// ----------------------------------------------------------------------------- /// /// All members of this registry are: /// - `static`, /// - `constexpr`, /// - immutable. /// /// No instances of this class are ever constructed. /// /// The registry is intended to be accessed as: /// /// \code /// EncodingCallbacksRegistry<...>::encodingCallbacks /// \endcode /// /// ----------------------------------------------------------------------------- /// Dependency guarantees /// ----------------------------------------------------------------------------- /// /// The structure and ordering of the registry are entirely determined by: /// - `detail::AllConcepts`, /// - the ordering of variants within each concept, /// - the canonical pipeline dimensions (`NUM_STAGES`, `NUM_SECTIONS`). /// /// Any change to these inputs results in a structurally different dispatch /// table and must be treated as a breaking change. /// template struct EncodingCallbacksRegistry { /// /// @brief Size of the registry along the variant dimension. /// /// This corresponds to the total number of flattened concept variants /// defined by `GeneralRegistry`. /// /// It defines the first (outermost) dimension of the encoding callback /// dispatch table. /// static constexpr std::size_t registry_size_along_dim0 = GeneralRegistry::NVariants; /// /// @brief Size of the registry along the encoding stage dimension. /// /// This corresponds to the number of logical encoding stages /// (e.g. allocation, preset, override, runtime) defined by /// `GeneralRegistry`. /// /// It defines the second dimension of the encoding callback /// dispatch table. /// static constexpr std::size_t registry_size_along_dim1 = GeneralRegistry::NStages; /// /// @brief Size of the registry along the GRIB section dimension. /// /// This corresponds to the number of GRIB sections handled by the /// encoding pipeline, as defined by `GeneralRegistry`. /// /// It defines the third (innermost) dimension of the encoding callback /// dispatch table. /// static constexpr std::size_t registry_size_along_dim2 = GeneralRegistry::NSections; /// /// @brief Canonical encoding function pointer type. /// /// This alias exposes the exact function signature used for all encoding /// callbacks stored in the registry. /// /// It is provided primarily for: /// - readability, /// - consistency with higher-level abstractions, /// - avoiding repetition of long qualified names. /// using Fn_t = metkit::mars2grib::backend::compile_time_registry_engine::Fn; /// /// @brief Fully materialized encoding dispatch table. /// /// This static data member contains the complete phase-level encoding /// callback registry for: /// - all concepts, /// - all variants, /// - all encoding stages, /// - all GRIB sections. /// /// The table is generated entirely at compile time by invoking /// `makePhaseCallbacksRegistry` with: /// - the full concept universe (`AllConcepts`), /// - capability index `0`, /// - the dictionary types bound to this registry. /// /// Each entry is either: /// - a valid function pointer, or /// - `nullptr` if the concept/variant/stage/section combination /// is not applicable. /// /// This table is intended to be consumed by higher-level planning /// and execution layers, not accessed directly by application code. /// static constexpr auto encodingCallbacks = metkit::mars2grib::backend::compile_time_registry_engine::makePhaseCallbacksRegistry< detail::AllConcepts, 0, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>(); /// /// @brief Compile-time structural verification. /// /// These static assertions ensure that the dimensions of the generated /// encoding callback table exactly match the canonical sizes defined by /// `GeneralRegistry`. /// /// Any mismatch indicates a structural inconsistency between the /// registry engine and the concept universe. /// static_assert(encodingCallbacks.size() == registry_size_along_dim0, "EncodingCallbacksRegistry: size along dimension 0 does not match GeneralRegistry"); static_assert(encodingCallbacks[0].size() == registry_size_along_dim1, "EncodingCallbacksRegistry: size along dimension 1 does not match GeneralRegistry"); static_assert(encodingCallbacks[0][0].size() == registry_size_along_dim2, "EncodingCallbacksRegistry: size along dimension 2 does not match GeneralRegistry"); }; } // namespace metkit::mars2grib::backend::concepts_metkit-1.19.2/src/metkit/mars2grib/backend/concepts/wave/0000775000175000017500000000000015232567717023427 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/wave/waveEnum.h0000664000175000017500000001045015232567717025367 0ustar alastairalastair/* * (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 waveEnum.h /// @brief Definition of the `wave` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `wave` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`waveName`) /// - the enumeration of supported wave-related variants (`WaveType`) /// - a compile-time typelist of all variants (`WaveList`) /// - 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 /// `waveEncoding.h` 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 `wave` 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 `wave` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view waveName{"wave"}; /// /// @brief Enumeration of all supported `wave` concept variants. /// /// Each enumerator represents a distinct wave-related representation /// or diagnostic 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 includes both spectral wave representations and /// derived period-based diagnostics. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class WaveType : std::size_t { Spectra = 0, Period, Default }; /// /// @brief Compile-time list of all `wave` 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 WaveList = ValueList; /// /// @brief Compile-time mapping from `WaveType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given wave variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Wave 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 waveTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view waveTypeName() { \ return NAME; \ } DEF(WaveType::Spectra, "spectra"); DEF(WaveType::Period, "period"); DEF(WaveType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/wave/waveEncoding.h0000664000175000017500000003040615232567717026214 0ustar alastairalastair/* * (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 waveEncoding.h /// @brief Implementation of the GRIB `wave` concept. /// /// This header defines the applicability rules and execution logic for the /// **wave concept** within the mars2grib backend. /// /// The `wave` concept is responsible for encoding GRIB keys related to /// wave spectral and wave period metadata, depending on: /// - the encoding stage, /// - the GRIB section, /// - the selected wave variant. /// /// The concept supports two distinct variants: /// - `WaveType::Spectra` /// - `WaveType::Period` /// /// Each variant is active at different stages of the encoding pipeline and /// operates on different subsets of GRIB keys, as dictated by the GRIB2 /// Product Definition Templates (PDTs). /// /// The implementation follows the standard mars2grib concept pattern: /// - Compile-time applicability via `waveApplicable` /// - Strict validation against expected PDTs /// - Variant- and stage-specific deductions /// - Deterministic encoding into the output dictionary /// - Context-rich error handling via concept exceptions /// /// @note /// The namespace name `concepts_` is intentionally used instead of `concepts` /// to avoid conflicts with the C++20 `concepts` language feature. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/wave/waveEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/periodItMax.h" #include "metkit/mars2grib/backend/deductions/periodItMin.h" #include "metkit/mars2grib/backend/deductions/waveDirectionGrid.h" #include "metkit/mars2grib/backend/deductions/waveDirectionNumber.h" #include "metkit/mars2grib/backend/deductions/waveFrequencyGrid.h" #include "metkit/mars2grib/backend/deductions/waveFrequencyNumber.h" // Tables #include "metkit/mars2grib/backend/tables/typeOfInterval.h" // Checks #include "metkit/mars2grib/backend/checks/matchProductDefinitionTemplateNumber.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 `wave` concept. /// /// This predicate determines whether the `wave` concept is applicable for a /// given combination of: /// - encoding stage, /// - GRIB section, /// - wave variant. /// /// The default applicability rules are: /// /// - **Spectral wave data** /// - `StageAllocate`, `SecProductDefinitionSection`, `WaveType::Spectra` /// - `StageRuntime`, `SecProductDefinitionSection`, `WaveType::Spectra` /// /// - **Wave period data** /// - `StagePreset`, `SecProductDefinitionSection`, `WaveType::Period` /// /// Any other combination is considered invalid and results in a runtime /// concept error if invoked. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Wave concept variant /// /// @return `true` if the concept is applicable, `false` otherwise. /// template constexpr bool waveApplicable() { bool condition1 = (Section == SecProductDefinitionSection && Stage == StageAllocate && Variant == WaveType::Spectra); bool condition2 = (Section == SecProductDefinitionSection && Stage == StagePreset && Variant == WaveType::Period); bool condition3 = (Section == SecProductDefinitionSection && Stage == StageRuntime && Variant == WaveType::Spectra); return condition1 || condition2 || condition3; } /// /// @brief Execute the `wave` concept operation. /// /// This function implements the runtime logic for encoding wave-related /// GRIB metadata. The behavior depends on both the wave variant and the /// encoding stage. /// /// --- /// ### Variant `WaveType::Spectra` /// /// #### StageAllocate /// - Validates that the Product Definition Template Number is one of `{99, 100}`. /// - Deduces and encodes: /// - Wave direction grid (number, scale factor, scaled values) /// - Wave frequency grid (number, scale factor, scaled values) /// /// #### StageRuntime /// - Deduces and encodes: /// - `waveDirectionNumber` /// - `waveFrequencyNumber` /// /// --- /// ### Variant `WaveType::Period` /// /// #### StagePreset /// - Validates that the Product Definition Template Number is one of `{103, 104}`. /// - Deduces optional lower and/or upper wave period bounds. /// - Encodes wave period interval metadata according to the availability /// of minimum and/or maximum bounds. /// /// --- /// ### Validation /// /// Each variant is validated against the expected GRIB Product Definition /// Template Number(s) before any encoding is performed. /// /// --- /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Wave 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 concept is invoked outside its applicability domain, /// - the Product Definition Template Number does not match expectations, /// - any wave grid or wave period deduction fails, /// - any encoding operation fails. /// /// @note /// For `WaveType::Period`, the code explicitly notes that some fields are /// already implicitly set by ecCodes via `paramId`. Overwriting these values /// may be redundant and should be reviewed once the final wave-period /// encoding policy is agreed. /// /// @warning /// If neither `periodItMin` nor `periodItMax` is present, no wave-period /// interval metadata is written. This is currently allowed but may require /// stricter validation in the future. /// /// @see waveApplicable /// @see deductions::resolve_WaveDirectionGrid_or_throw /// @see deductions::resolve_WaveFrequencyGrid_or_throw /// @see deductions::resolve_WaveDirectionNumber_or_throw /// @see deductions::resolve_WaveFrequencyNumber_or_throw /// @see deductions::resolve_PeriodItMin_opt /// @see deductions::resolve_PeriodItMax_opt /// template void WaveOp(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 (waveApplicable()) { try { MARS2GRIB_LOG_CONCEPT(wave); // Checks/Validation if constexpr (Variant == WaveType::Spectra) { validation::match_ProductDefinitionTemplateNumber_or_throw(opt, out, {99, 100}); } else if constexpr (Variant == WaveType::Period) { validation::match_ProductDefinitionTemplateNumber_or_throw(opt, out, {103, 104}); } if constexpr (Stage == StageAllocate) { if constexpr (Variant == WaveType::Spectra) { // Deductions deductions::WaveDirectionGrid directionGrid = deductions::resolve_WaveDirectionGrid_or_throw(mars, par, opt); deductions::WaveFrequencyGrid frequencyGrid = deductions::resolve_WaveFrequencyGrid_or_throw(mars, par, opt); // Encoding set_or_throw(out, "numberOfWaveDirections", directionGrid.numDirections); set_or_throw(out, "scaleFactorOfWaveDirections", directionGrid.scaleFactorDirections); set_or_throw>(out, "scaledValuesOfWaveDirections", directionGrid.scaledValuesDirections); set_or_throw(out, "numberOfWaveFrequencies", frequencyGrid.numFrequencies); set_or_throw(out, "scaleFactorOfWaveFrequencies", frequencyGrid.scaleFactorFrequencies); set_or_throw>(out, "scaledValuesOfWaveFrequencies", frequencyGrid.scaledValuesFrequencies); } } if constexpr (Stage == StagePreset) { if constexpr (Variant == WaveType::Period) { // Deductions std::optional itMin = deductions::resolve_PeriodItMin_opt(mars, par, opt); std::optional itMax = deductions::resolve_PeriodItMax_opt(mars, par, opt); // Encoding /// @note: /// - This information is set by eccodes as part of the paramId, not really /// sure it make sense to (over)write it here... if (itMin.has_value() && itMax.has_value()) { set_or_throw( out, "typeOfWavePeriodInterval", static_cast(tables::TypeOfInterval::BetweenFirstInclusiveSecondInclusive)); set_or_throw(out, "scaleFactorOfLowerWavePeriodLimit", 0L); set_or_throw(out, "scaledValueOfLowerWavePeriodLimit", itMin.value()); set_or_throw(out, "scaleFactorOfUpperWavePeriodLimit", 0L); set_or_throw(out, "scaledValueOfUpperWavePeriodLimit", itMax.value()); } else if (itMin.has_value() && !itMax.has_value()) { set_or_throw(out, "typeOfWavePeriodInterval", static_cast(tables::TypeOfInterval::GreaterThanFirstLimit)); set_or_throw(out, "scaleFactorOfLowerWavePeriodLimit", 0L); set_or_throw(out, "scaledValueOfLowerWavePeriodLimit", itMin.value()); } else if (!itMin.has_value() && itMax.has_value()) { set_or_throw(out, "typeOfWavePeriodInterval", static_cast(tables::TypeOfInterval::SmallerThanSecondLimit)); set_or_throw(out, "scaleFactorOfUpperWavePeriodLimit", 0L); set_or_throw(out, "scaledValueOfUpperWavePeriodLimit", itMax.value()); } } // if constexpr ( Variant == WaveType::Period ) } if constexpr (Stage == StageRuntime) { if constexpr (Variant == WaveType::Spectra) { // Deduction long marsDir = deductions::resolve_WaveDirectionNumber_or_throw(mars, par, opt); long marsFreq = deductions::resolve_WaveFrequencyNumber_or_throw(mars, par, opt); // Encoding set_or_throw(out, "waveDirectionNumber", marsDir); set_or_throw(out, "waveFrequencyNumber", marsFreq); } } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(wave, "Unable to set `wave` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(wave, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/wave/waveConceptDescriptor.h0000664000175000017500000001162615232567717030123 0ustar alastairalastair/* * (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 waveConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `wave` concept. /// /// This header defines `WaveConcept`, the **compile-time descriptor** /// that registers the GRIB `wave` 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/wave/waveEncoding.h" #include "metkit/mars2grib/backend/concepts/wave/waveEnum.h" #include "metkit/mars2grib/backend/concepts/wave/waveMatcher.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 `wave` concept. /// /// `WaveConcept` registers the GRIB `wave` concept into the /// compile-time registry engine. /// struct WaveConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return waveName; } /// /// @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 waveTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `wave` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (waveApplicable()) { return &WaveOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `wave` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &waveMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/wave/waveMatcher.h0000664000175000017500000000710315232567717026047 0ustar alastairalastair/* * (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 waveMatcher.h /// @brief Entry-level matcher for the GRIB `wave` concept. /// /// This header defines the runtime matcher used by the concept registry to /// identify wave-period and wave-spectra products. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "eckit/exception/Exceptions.h" #include "metkit/mars2grib/backend/concepts/wave/waveEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/paramMatcher.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `wave` concept variant. /// /// Wave-period parameters resolve to `WaveType::Period`. Wave spectra resolve /// to `WaveType::Spectra` and require both `frequency` and `direction` keys. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local wave variant index, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If wave spectra metadata is incomplete, the required parameter metadata /// cannot be read, or lower-level matcher evaluation fails. Lower-level /// exceptions are preserved through `std::throw_with_nested`. /// template std::size_t waveMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribMatcherException; const auto param = get_or_throw(mars, "param"); if (matchAny(param, range(140114, 140120))) { return static_cast(WaveType::Period); } if (matchAny(param, 140251)) { if (!has(mars, "frequency")) { throw Mars2GribMatcherException("Missing required mars keyword `frequency` for wave spectra", Here()); } if (!has(mars, "direction")) { throw Mars2GribMatcherException("Missing required mars keyword `direction` for wave spectra", Here()); } return static_cast(WaveType::Spectra); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested(utils::exceptions::Mars2GribMatcherException("Unable to match `wave` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/ensemble/0000775000175000017500000000000015232567717024257 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/ensemble/ensembleEnum.h0000664000175000017500000001124415232567717027051 0ustar alastairalastair/* * (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 ensembleEnum.h /// @brief Definition of the `ensemble` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `ensemble` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`ensembleName`) /// - the enumeration of supported ensemble variants (`EnsembleType`) /// - a compile-time typelist of all variants (`EnsembleList`) /// - 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 /// `ensembleEncoding.h` 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 `ensemble` 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 `ensemble` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view ensembleName{"ensemble"}; /// /// @brief Enumeration of all supported `ensemble` concept variants. /// /// Each enumerator represents a specific ensemble configuration or /// perturbation strategy 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 includes both deterministic and stochastic ensemble /// generation approaches. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class EnsembleType : std::size_t { Individual = 0, PerturbedParameters, RandomPatterns, ProbabilityLargeEnsemble }; /// /// @brief Compile-time list of all `ensemble` 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 EnsembleList = ValueList; /// /// @brief Compile-time mapping from `EnsembleType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given ensemble variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Ensemble 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 ensembleTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view ensembleTypeName() { \ return NAME; \ } DEF(EnsembleType::Individual, "individual"); DEF(EnsembleType::PerturbedParameters, "perturbedParameters"); DEF(EnsembleType::RandomPatterns, "randomPatterns"); DEF(EnsembleType::ProbabilityLargeEnsemble, "probabilityLargeEnsemble"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/ensemble/ensembleConceptDescriptor.h0000664000175000017500000001457215232567717031606 0ustar alastairalastair/* * (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 ensembleConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `ensemble` concept. /// /// This header defines `EnsembleConcept`, the **compile-time descriptor** /// that registers the GRIB `ensemble` 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/ensemble/ensembleEncoding.h" #include "metkit/mars2grib/backend/concepts/ensemble/ensembleEnum.h" #include "metkit/mars2grib/backend/concepts/ensemble/ensembleMatcher.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 `ensemble` concept. /// /// `EnsembleConcept` registers the GRIB `ensemble` 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 EnsembleConcept : 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 ensembleName; } /// /// @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 ensembleTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `ensemble` 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 (ensembleApplicable()) { return &EnsembleOp; } else { return nullptr; } } else { return nullptr; } // Avoid compiler warnings mars2gribUnreachable(); } /// /// @brief Variant-specific callbacks (not used for this concept). /// /// This hook is provided for completeness of the registry interface. /// The `ensemble` concept does not define variant-level callbacks, /// so this function always returns `nullptr`. /// /// @tparam Capability Encoding capability index /// @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 Always `nullptr` /// template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This callback is invoked to determine whether the `ensemble` /// concept should be activated for a given encoding request. /// /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Matcher function pointer /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &ensembleMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/ensemble/ensembleEncoding.h0000664000175000017500000002003615232567717027672 0ustar alastairalastair/* * (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 ensembleEncoding.h /// @brief Implementation of the GRIB `ensemble` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **ensemble concept** within the mars2grib backend. /// /// The ensemble concept is responsible for encoding GRIB keys related to /// ensemble forecasts in the *Product Definition Section*, including: /// /// - `typeOfEnsembleForecast` /// - `numberOfForecastsInEnsemble` /// - `perturbationNumber` /// /// The concept currently supports the **Individual ensemble variant**, which /// represents a single ensemble member within an ensemble forecast system. /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `ensembleApplicable` /// - Structural validation of the Product Definition Section /// - Delegation of semantic resolution to dedicated deduction functions /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/ensemble/ensembleEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/numberOfForecastsInEnsemble.h" #include "metkit/mars2grib/backend/deductions/perturbationNumber.h" #include "metkit/mars2grib/backend/deductions/typeOfEnsembleForecast.h" // Tables #include "metkit/mars2grib/backend/tables/typeOfEnsembleForecast.h" // checks #include "metkit/mars2grib/backend/checks/checkEnsembleProductDefinitionSection.h" #include "metkit/mars2grib/backend/checks/matchProductDefinitionTemplateNumber.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 `ensemble` concept. /// /// This predicate determines whether the ensemble concept is applicable /// for a given combination of: /// - encoding stage /// - GRIB section /// - ensemble variant /// /// Applicability is evaluated entirely at compile time and is used by the /// concept dispatcher to ensure that only valid concept instantiations occur. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Ensemble 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 == EnsembleType::Individual` /// - `Stage == StagePreset` /// - `Section == SecProductDefinitionSection` /// /// This reflects the current GRIB encoding rules for individual ensemble members. /// template constexpr bool ensembleApplicable() { // Confitions to apply concept return ((Variant == EnsembleType::Individual || Variant == EnsembleType::ProbabilityLargeEnsemble) && (Stage == StagePreset) && (Section == SecProductDefinitionSection)); } /// /// @brief Execute the ensemble concept operation. /// /// This function implements the runtime logic of the GRIB `ensemble` concept. /// When applicable, it: /// /// 1. Validates that the Product Definition Section is compatible with /// ensemble encoding. /// 2. Deduces ensemble-related metadata from MARS and parameter dictionaries. /// 3. Encodes the corresponding GRIB keys in the output dictionary. /// /// The concept currently supports the **Individual** and **ProbabilityLargeEnsemble** ensemble variants, which /// represent a single ensemble member. /// /// 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 Ensemble 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 Product Definition Section is not compatible with ensemble encoding /// - any deduction fails /// - any GRIB key cannot be set /// /// @note /// - All runtime errors are wrapped with full concept context /// (concept name, variant, stage, section). /// - This concept does not rely on any pre-existing GRIB header state. /// /// @see ensembleApplicable /// template void EnsembleOp(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 (ensembleApplicable()) { try { // Logging MARS2GRIB_LOG_CONCEPT(ensemble); // ============================================================= // Variant-specific logic // ============================================================= if constexpr (Variant == EnsembleType::Individual) { // Structural validation validation::check_EnsembleProductDefinitionSection_or_throw(opt, out); // Deductions tables::TypeOfEnsembleForecast typeOfEnsembleForecast = deductions::resolve_TypeOfEnsembleForecast_or_throw(mars, par, opt); long numberOfForecastsInEnsemble = deductions::resolve_NumberOfForecastsInEnsemble_or_throw(mars, par, opt); long marsNumber = deductions::resolve_PerturbationNumber_or_throw(mars, par, opt); // Encoding set_or_throw(out, "typeOfEnsembleForecast", static_cast(typeOfEnsembleForecast)); set_or_throw(out, "numberOfForecastsInEnsemble", numberOfForecastsInEnsemble); set_or_throw(out, "perturbationNumber", marsNumber); } if constexpr (Variant == EnsembleType::ProbabilityLargeEnsemble) { // Structural validation validation::match_ProductDefinitionTemplateNumber_or_throw(opt, out, {121L, 122L}); // Deductions std::cout << "TODO:: Resolving TypeOfEnsembleForecast for ProbabilityLargeEnsemble..." << std::endl; } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(ensemble, "Unable to set `ensemble` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(ensemble, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/ensemble/ensembleMatcher.h0000664000175000017500000001002515232567717027524 0ustar alastairalastair/* * (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 ensembleMatcher.h /// @brief Entry-level matcher for the GRIB `ensemble` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether ensemble-member metadata is active for a MARS request. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include #include // Utils #include "metkit/mars2grib/backend/concepts/ensemble/ensembleEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/paramMatcher.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `ensemble` concept variant. /// /// The concept is active as `EnsembleType::Individual` when the MARS request /// contains `number`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `EnsembleType::Individual`, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t ensembleMatcher(const MarsDict_t& mars, const OptDict_t& opt) { // Skip model-error products: in that case "number" identifies the // model-error realization, not an ensemble member. try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; long param = get_or_throw(mars, "param"); if (has(mars, "number") && !(has(mars, "type") && (get_or_throw(mars, "type") == "me" || get_or_throw(mars, "type") == "eme" || get_or_throw(mars, "type") == "efi" || get_or_throw(mars, "type") == "efic" || get_or_throw(mars, "type") == "sot"))) { return static_cast(EnsembleType::Individual); } else if (has(mars, "type") && (get_or_throw(mars, "type") == "ep")) { if (matchAny(param, 131060, 131061, 131062, 131063, 131064, 131065, 131066, 131067, 131068, 131069, 131070, 131071, 131072, 131073, range(131074, 131077), 131085, 131089, 131090, 131091, 131098, 131099, 131100, 133093, 133094, 133095, 133096, 133097, 133098)) { return static_cast(EnsembleType::ProbabilityLargeEnsemble); } else { return compile_time_registry_engine::MISSING; } } else { return compile_time_registry_engine::MISSING; } } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `ensemble` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/shape-of-the-earth/0000775000175000017500000000000015232567717026046 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthMatcher.h0000664000175000017500000000612215232567717032536 0ustar alastairalastair/* * (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 shapeOfTheEarthMatcher.h /// @brief Entry-level matcher for the GRIB `shapeOfTheEarth` concept. /// /// This header defines the runtime matcher used by the concept registry to /// decide whether shape-of-the-earth metadata should be encoded. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `shapeOfTheEarth` concept variant. /// /// The concept is active as `ShapeOfTheEarthType::Default` for grid-point /// fields. It is inactive for spherical harmonics, identified by `truncation`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `ShapeOfTheEarthType::Default`, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t shapeOfTheEarthMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::has; // This is used to fully delegate section3 setting to gridSpec if (metkit::mars2grib::utils::skipSection3(opt)) { return static_cast(ShapeOfTheEarthType::Dummy); } // NOTE: Spherical harmonics is encoded without shape of the earth if (has(mars, "truncation")) { return compile_time_registry_engine::MISSING; } return static_cast(ShapeOfTheEarthType::Default); } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `shapeOfTheEarth` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthEncoding.h0000664000175000017500000001370615232567717032707 0ustar alastairalastair/* * (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 shapeOfTheEarthEncoding.h /// @brief Implementation of the GRIB `shapeOfTheEarth` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **shapeOfTheEarth concept** within the mars2grib backend. /// /// The concept is responsible for populating the GRIB key `shapeOfTheEarth` /// in the *Grid Definition Section* (Section 3), describing the geometric /// reference system used for the Earth model (e.g. spherical Earth, /// oblate spheroid, custom reference system). /// /// The value is deduced from MARS and parameterization dictionaries and mapped /// to the corresponding GRIB code table via a strongly-typed enumeration. /// /// The implementation follows the standard mars2grib concept pattern: /// - Compile-time applicability via `shapeOfTheEarthApplicable` /// - Centralized deduction logic /// - Strict GRIB-level encoding /// - Context-rich error handling /// /// @note /// The namespace name `concepts_` is intentionally used instead of `concepts` /// to avoid conflicts with the C++20 `concepts` language feature. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/shapeOfTheEarth.h" // Tables #include "metkit/mars2grib/backend/tables/shapeOfTheReferenceSystem.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 `shapeOfTheEarth` concept. /// /// This predicate determines whether the `shapeOfTheEarth` concept /// is applicable for a given encoding stage, GRIB section, and concept variant. /// /// The concept is applied: /// - exclusively in the *Grid Definition Section* (Section 3) /// - during the preset stage /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Shape-of-the-Earth concept variant /// /// @return `true` if the concept is applicable, `false` otherwise. /// template constexpr bool shapeOfTheEarthApplicable() { // Disable the concept for the Dummy variant if constexpr (Variant == ShapeOfTheEarthType::Dummy) { return false; } return Section == SecGridDefinitionSection && Stage == StagePreset; } /// /// @brief Execute the `shapeOfTheEarth` concept operation. /// /// This function implements the runtime logic of the GRIB /// `shapeOfTheEarth` concept. /// /// When applicable, it: /// 1. Deduces the Earth reference system from MARS, and parametrization dictionaries. /// 2. Maps the result to a GRIB-compliant /// `ShapeOfTheReferenceSystem` enumeration. /// 3. Encodes the corresponding numeric value into the output GRIB /// dictionary. /// /// If the concept is invoked outside its applicability domain, /// a `Mars2GribConceptException` is thrown. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index /// @tparam Variant Shape-of-the-Earth 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 concept is called when not applicable /// - deduction of the Earth shape fails /// - encoding into the GRIB dictionary fails /// /// @note /// - This concept performs a **pure GRIB-level encoding**. /// - All semantic interpretation of geometry is delegated to the /// corresponding deduction. /// /// @see shapeOfTheEarthApplicable /// @see deductions::resolve_ShapeOfTheEarth_or_throw /// template void ShapeOfTheEarthOp(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 (shapeOfTheEarthApplicable()) { try { MARS2GRIB_LOG_CONCEPT(shapeOfTheEarth); // Deductions tables::ShapeOfTheReferenceSystem shapeOfTheEarth = deductions::resolve_ShapeOfTheEarth_or_throw(mars, par, opt); // Encoding set_or_throw(out, "shapeOfTheEarth", static_cast(shapeOfTheEarth)); } catch (...) { MARS2GRIB_CONCEPT_RETHROW(shapeOfTheEarth, "Unable to set `shapeOfTheEarth` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(shapeOfTheEarth, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ ././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthConceptDescriptor.hmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthConceptDescrip0000664000175000017500000001227515232567717033640 0ustar alastairalastair/* * (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 shapeOfTheEarthConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `shapeOfTheEarth` concept. /// /// This header defines `ShapeOfTheEarthConcept`, the **compile-time descriptor** /// that registers the GRIB `shapeOfTheEarth` 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/shape-of-the-earth/shapeOfTheEarthEncoding.h" #include "metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthEnum.h" #include "metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthMatcher.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 `shapeOfTheEarth` concept. /// /// `ShapeOfTheEarthConcept` registers the GRIB `shapeOfTheEarth` concept into the /// compile-time registry engine. /// struct ShapeOfTheEarthConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return shapeOfTheEarthName; } /// /// @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 shapeOfTheEarthTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `shapeOfTheEarth` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (shapeOfTheEarthApplicable()) { return &ShapeOfTheEarthOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `shapeOfTheEarth` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &shapeOfTheEarthMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/shape-of-the-earth/shapeOfTheEarthEnum.h0000664000175000017500000001116415232567717032061 0ustar alastairalastair/* * (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 shapeOfTheEarthEnum.h /// @brief Definition of the `shapeOfTheEarth` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `shapeOfTheEarth` /// concept used by the mars2grib backend. It contains: /// /// - the canonical concept name (`shapeOfTheEarthName`) /// - the enumeration of supported Earth-shape variants (`ShapeOfTheEarthType`) /// - a compile-time typelist of all variants (`ShapeOfTheEarthList`) /// - 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 /// `shapeOfTheEarthEncoding.h` 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 `shapeOfTheEarth` 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 `shapeOfTheEarth` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view shapeOfTheEarthName{"shapeOfTheEarth"}; /// /// @brief Enumeration of all supported `shapeOfTheEarth` concept variants. /// /// Each enumerator represents a specific Earth-shape model or assumption /// used when encoding geospatial information. /// /// 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 geodetic support evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class ShapeOfTheEarthType : std::size_t { Dummy = 0, // Used to skip section3 resolution and delegate to gridSpec Default }; /// /// @brief Compile-time list of all `shapeOfTheEarth` 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 ShapeOfTheEarthList = ValueList; /// /// @brief Compile-time mapping from `ShapeOfTheEarthType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given Earth-shape variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Shape-of-the-Earth 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 shapeOfTheEarthTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view shapeOfTheEarthTypeName() { \ return NAME; \ } DEF(ShapeOfTheEarthType::Dummy, "dummy"); DEF(ShapeOfTheEarthType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/point-in-time/0000775000175000017500000000000015232567717025156 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeConceptDescriptor.h0000664000175000017500000001212215232567717033137 0ustar alastairalastair/* * (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 pointInTimeConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `pointInTime` concept. /// /// This header defines `PointInTimeConcept`, the **compile-time descriptor** /// that registers the GRIB `pointInTime` 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/point-in-time/pointInTimeEncoding.h" #include "metkit/mars2grib/backend/concepts/point-in-time/pointInTimeEnum.h" #include "metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.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 `pointInTime` concept. /// /// `PointInTimeConcept` registers the GRIB `pointInTime` concept into the /// compile-time registry engine. /// struct PointInTimeConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return pointInTimeName; } /// /// @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 pointInTimeTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `pointInTime` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (pointInTimeApplicable()) { return &PointInTimeOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `pointInTime` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &pointInTimeMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h0000664000175000017500000001431715232567717031100 0ustar alastairalastair/* * (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 pointInTimeMatcher.h /// @brief Entry-level matcher for the GRIB `pointInTime` concept. /// /// This header defines the runtime matcher used by the concept registry to /// identify products that use point-in-time forecast semantics. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/point-in-time/pointInTimeEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/paramMatcher.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `pointInTime` concept variant. /// /// The matcher classifies known non-statistical, wave, satellite, and chemical /// parameters as `PointInTimeType::Default`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `PointInTimeType::Default`, or /// `compile_time_registry_engine::MISSING` when the concept is inactive. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If the required parameter metadata cannot be read or lower-level matcher /// evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t pointInTimeMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::util::param_matcher::matchAny; using metkit::mars2grib::util::param_matcher::range; using metkit::mars2grib::utils::dict_traits::get_or_throw; const auto param = get_or_throw(mars, "param"); if (matchAny(param, range(1, 3), 10, range(15, 18), range(21, 23), range(26, 43), 53, 54, 59, 60, 66, 67, range(74, 79), range(129, 139), 141, 148, 151, 152, range(155, 157), range(159, 168), 170, range(172, 174), 183, range(186, 188), 198, 203, 206, 207, range(229, 232), range(234, 236), 238, range(243, 248), 3020, 3031, 3067, range(3073, 3075), 129172, range(140098, 140105), 140112, 140113, range(140121, 140129), range(140131, 140134), range(140207, 140209), 140211, 140212, range(140214, 140239), range(140244, 140249), range(140252, 140254), 160198, range(162059, 162063), 162071, 162072, 162093, 174096, 200199, range(210186, 210191), range(210198, 210202), range(210260, 210264), range(213101, 213160), range(228001, 228003), range(228007, 228020), 228023, 228024, 228029, 228032, 228037, 228038, range(228044, 228048), 228050, 228052, range(228088, 228090), 228131, 228132, 228141, 228164, range(228217, 228221), range(228231, 228237), 229001, 229007, 260004, 260005, 260015, 260038, 260048, 260109, 260121, 260123, 260132, 260199, 260242, 260255, 260260, 260289, 260290, 260292, 260293, 260360, 260509, 260688, 261001, 261002, range(261014, 261016), 261018, 261023, range(262000, 262009), 262011, 262014, 262015, 262017, 262018, 262023, 262024, range(262100, 262106), range(262108, 262112), range(262113, 262116), range(262118, 262125), 262130, range(262139, 262141), 262143, 262144, range(262146, 262149), range(262500, 262502), range(262505, 262507), 262900, 262906, 262907)) { return static_cast(PointInTimeType::Default); } // Wave products if (matchAny(param, range(140114, 140120), 140251)) { return static_cast(PointInTimeType::Default); } // Satellite products if (matchAny(param, 194, range(260510, 260512))) { return static_cast(PointInTimeType::Default); } // Chemical products if (matchAny(param, range(228083, 228085), range(400000, 499999))) { return static_cast(PointInTimeType::Default); } // Strike-probability products if (matchAny(param, 131068, 131069, 131073, range(131074, 131077), 131089, 131090, 131091)) { return static_cast(PointInTimeType::Default); } // Probability products if (matchAny(param, 131020, 131021, 131022, 131023, 131024, 131025)) { return static_cast(PointInTimeType::Default); } // ECMWF covariance / analysis-uncertainty paramIds (254001..254017). // These are point-in-time products living on the abstractLevel // (typeOfFirstFixedSurface=254) and are used with MARS type=est // (individual ensemble member, PDT=1) as well as with non-ensemble // analyses (PDT=0). Without this mapping, PointInTimeConcept is left // inactive and Section 4 recipe selection fails with "No matching recipe". if (matchAny(param, range(254001, 254017))) { return static_cast(PointInTimeType::Default); } return compile_time_registry_engine::MISSING; } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `pointInTime` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeEnum.h0000664000175000017500000001057715232567717030425 0ustar alastairalastair/* * (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 pointInTimeEnum.h /// @brief Definition of the `pointInTime` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `pointInTime` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`pointInTimeName`) /// - the enumeration of supported point-in-time variants (`PointInTimeType`) /// - a compile-time typelist of all variants (`PointInTimeList`) /// - 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 /// `pointInTimeEncoding.h` 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 `pointInTime` 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 `pointInTime` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view pointInTimeName{"pointInTime"}; /// /// @brief Enumeration of all supported `pointInTime` concept variants. /// /// Each enumerator represents a specific temporal interpretation /// corresponding to a single valid time instant. /// /// 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 temporal handling evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class PointInTimeType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `pointInTime` 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 PointInTimeList = ValueList; /// /// @brief Compile-time mapping from `PointInTimeType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given point-in-time variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Point-in-time 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 pointInTimeTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view pointInTimeTypeName() { \ return NAME; \ } DEF(PointInTimeType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeEncoding.h0000664000175000017500000001777115232567717031252 0ustar alastairalastair/* * (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 pointInTimeEncoding.h /// @brief Implementation of the GRIB `pointInTime` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **pointInTime concept** within the mars2grib backend. /// /// The pointInTime concept is responsible for encoding GRIB keys that describe /// the temporal reference of a product expressed as a *point in time*, i.e. /// a forecast offset relative to the reference time. /// /// The concept operates across multiple encoding stages: /// - **StageAllocate**: prepares time-related keys and marks unused fields as missing /// - **StagePreset**: defines the unit of the time range (hours) /// - **StageRuntime**: sets the actual forecast time value /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `pointInTimeApplicable` /// - Runtime deduction of forecast time /// - Stage-specific encoding logic /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/point-in-time/pointInTimeEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Tables #include "metkit/mars2grib/backend/tables/timeUnits.h" // Deductions #include "metkit/mars2grib/backend/deductions/productTime.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 `pointInTime` concept. /// /// The default applicability enables this concept for the /// *Product Definition Section* at all encoding stages: /// - `StageAllocate` /// - `StagePreset` /// - `StageRuntime` /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Point-in-time concept variant /// /// @return `true` if the concept is applicable for the given parameters, /// `false` otherwise. /// /// @note /// The concept is stage-aware and performs different actions depending /// on the encoding stage. /// template constexpr bool pointInTimeApplicable() { bool condition1 = Stage == StageAllocate && Section == SecProductDefinitionSection; bool condition2 = Stage == StagePreset && Section == SecProductDefinitionSection; bool condition3 = Stage == StageRuntime && Section == SecProductDefinitionSection; return (condition1 || condition2 || condition3); } /// /// @brief Execute the `pointInTime` concept operation. /// /// This function implements the runtime logic of the GRIB `pointInTime` concept. /// When applicable, it: /// /// - Deduces the forecast time offset from the input dictionaries /// - Validates that the offset is an integer number of hours /// - Encodes time-related GRIB keys according to the current encoding stage /// /// Stage-specific behavior: /// - **StageAllocate** /// - Marks cutoff-related fields as missing /// - **StagePreset** /// - Sets the time unit to hours /// - **StageRuntime** /// - Sets the forecast time value in hours /// /// If the concept is invoked when not applicable, or if unsupported /// forecast offsets are encountered, a `Mars2GribConceptException` is thrown. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Point-in-time 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 concept is called when not applicable /// - the forecast time is not an integer number of hours /// - any deduction or encoding step fails /// /// @note /// - Currently, only full-hour forecast steps are supported. /// - Sub-hour resolutions must be handled by a different concept /// or by extending this implementation. /// /// @see pointInTimeApplicable /// template void PointInTimeOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { using metkit::mars2grib::backend::tables::TimeUnit; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::dict_traits::setMissing_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (pointInTimeApplicable()) { try { MARS2GRIB_LOG_CONCEPT(pointInTime); // Deductions: temporal data is sourced exclusively from the // resolved `ProductTime` (§15 of timeProducts.md). auto pt = deductions::resolve_ProductTime_or_throw(mars, par, opt); // For an instant product `windowStart == windowEnd`; the // forecast-time offset is `windowEnd - referenceDateTime` (in // seconds, returned by eckit::DateTime::operator-). const long marsStepInSeconds = static_cast(static_cast(pt.windowEnd - pt.referenceDateTime)); // Basic checks if (marsStepInSeconds % 3600 != 0) { throw Mars2GribConceptException( std::string(pointInTimeName), std::string(pointInTimeTypeName()), std::to_string(Stage), std::to_string(Section), "Only full hour steps are supported currently", Here()); } long marsStepInHours = marsStepInSeconds / 3600; // ============================================================= // Variant-specific logic // ============================================================= if constexpr (Stage == StageAllocate) { // Encoding setMissing_or_throw(out, "hoursAfterDataCutoff"); setMissing_or_throw(out, "minutesAfterDataCutoff"); } if constexpr (Stage == StagePreset) { // Encoding set_or_throw(out, "indicatorOfUnitOfTimeRange", static_cast(TimeUnit::Hour)); } if constexpr (Stage == StageRuntime) { // Encoding set_or_throw(out, "forecastTime", marsStepInHours); } } catch (...) { MARS2GRIB_CONCEPT_RETHROW(pointInTime, "Unable to set `pointInTime` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(pointInTime, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/representation/0000775000175000017500000000000015232567717025527 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/representation/representationMatcher.h0000664000175000017500000001020115232567717032240 0ustar alastairalastair/* * (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 representationMatcher.h /// @brief Entry-level matcher for the GRIB `representation` concept. /// /// This header defines the runtime matcher used by the concept registry to /// select the GRIB grid representation variant from MARS metadata. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include #include // Utils #include "eckit/geo/Grid.h" #include "eckit/spec/Custom.h" #include "metkit/mars2grib/backend/concepts/representation/representationEnum.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `representation` concept variant. /// /// Spherical harmonics are selected when `truncation` is present. Otherwise the /// matcher builds the eckit geometry from MARS `grid` and maps the resulting /// grid type onto the corresponding representation variant. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local representation variant index. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If the grid cannot be resolved, is unsupported, or lower-level matcher /// evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t representationMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; // This is used to fully delegate section3 setting to gridSpec if (metkit::mars2grib::utils::skipSection3(opt)) { return static_cast(RepresentationType::Dummy); } if (has(mars, "truncation")) { return static_cast(RepresentationType::SphericalHarmonics); } const auto marsGrid = get_or_throw(mars, "grid"); const auto gridType = std::unique_ptr( eckit::geo::GridFactory::build(eckit::spec::Custom{{"grid", marsGrid}})) ->type(); if (gridType == "regular_gg") { return static_cast(RepresentationType::RegularGaussian); } else if (gridType == "reduced_gg") { return static_cast(RepresentationType::ReducedGaussian); } else if (gridType == "regular_ll") { return static_cast(RepresentationType::Latlon); } else if (gridType == "ORCA") { return static_cast(RepresentationType::Orca); } else if (gridType == "HEALPix") { return static_cast(RepresentationType::Healpix); } throw utils::exceptions::Mars2GribMatcherException( "Cannot match grid \"" + marsGrid + "\" with grid type \"" + gridType + "\"! ", Here()); } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `representation` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/representation/representationEncoding.h0000664000175000017500000006133015232567717032414 0ustar alastairalastair/* * (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 representationEncoding.h /// @brief Implementation of the GRIB `representation` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **representation concept**, which is responsible for configuring the /// GRIB *Grid Definition Section (Section 3)*. /// /// The representation concept controls how the spatial grid of a field /// is described in GRIB, including: /// - grid type selection (lat/lon, Gaussian, spectral, HEALPix, …) /// - grid topology and resolution parameters /// - allocation and population of grid-related metadata /// /// The concept operates in multiple encoding stages and performs /// **variant-specific logic** depending on the selected /// `RepresentationType`. /// /// --- /// /// ## Encoding stages /// /// The concept is active in the following stages: /// /// - **StageAllocate** /// - Selection of the GRIB grid type /// - Structural validation against the Grid Definition Template /// - Allocation of representation-specific data (e.g. PL arrays) /// /// - **StagePreset** /// - Population of grid geometry parameters /// - Encoding of resolution, truncation, and coordinate metadata /// /// The exact operations performed depend on the selected representation /// variant. /// /// --- /// /// ## Supported representation variants /// /// The following `RepresentationType` variants are currently handled: /// /// - `Latlon` /// - `RegularGaussian` /// - `ReducedGaussian` /// - `SphericalHarmonics` /// - `Healpix` /// /// The following variants are recognized but **not implemented**: /// /// - `Orca` /// - `Fesom` /// /// Attempting to use unsupported variants results in a concept-level /// exception. /// /// --- /// /// ## Geometry handling /// /// Geometry parameters are in most cases retrieved from eckit::geo, based on the MARS key `grid`. /// For spherical harmonics, the geometry is based on the MARS key `truncation`. /// /// @warning /// This is a transitional design. /// /// A dedicated grid/geometry deduction layer does not exist yet. /// As a consequence: /// - The concept creates an eckit::geo::Grid object and retreives the relevant keys /// - Validation of geometry consistency is minimal /// - Responsibilities between geometry handling and encoding are not /// fully separated /// /// This will be refactored in a future iteration. /// /// --- /// /// ## Applicability model /// /// All representation variants are considered applicable in: /// /// - `StageAllocate` /// - `StagePreset` /// /// for `SecGridDefinitionSection`. /// /// Variant-specific behavior is implemented entirely inside the /// operation body using `if constexpr`. /// /// --- /// /// ## Error handling /// /// - Structural mismatches with GRIB templates are detected via /// validation helpers. /// - Unsupported or unknown representation variants result in /// concept-level exceptions. /// - All failures are rethrown with full concept context. /// /// --- /// /// @note /// The namespace name `concepts_` is intentionally used instead of /// `concepts` to avoid conflicts with the C++20 `concept` language /// feature. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include #include #include #include #include #include // Eckit::geo includes #include "eckit/geo/Grid.h" #include "eckit/geo/PointLonLat.h" #include "eckit/geo/grid/ORCA.h" #include "eckit/geo/grid/reduced/HEALPix.h" #include "eckit/geo/grid/reduced/ReducedGaussian.h" #include "eckit/geo/grid/regular/RegularGaussian.h" #include "eckit/geo/grid/regular/RegularLL.h" #include "eckit/spec/Custom.h" #include "metkit/mars2grib/utils/generalUtils.h" // Defintion of Span #include "metkit/codes/api/CodesTypes.h" // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/representation/representationEnum.h" // Checks #include "metkit/mars2grib/backend/checks/matchGridDefinitionTemplateNumber.h" // Deductions #include "metkit/mars2grib/backend/deductions/allowedReferenceValue.h" // Utils #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// This helper provides a function-local static buffer initialized with a constant value. /// The buffer starts with size zero and is grown on demand when the requested /// size exceeds the currently allocated size. /// /// If the buffer is already large enough, the existing storage is reused without /// reallocation. The returned span is restricted to the requested size, even if /// the underlying buffer is larger. /// /// The returned span is read-only and is intended to initialize encoded fields /// with the value resolved by the caller. The active span is refreshed with /// `std::transform` on each call. /// /// @param requiredSize Number of entries requested. /// @param value Constant value used to initialize every entry. /// /// @return Span containing exactly `requiredSize` entries set to `value`. /// static metkit::codes::Span constValueSpan(std::size_t requiredSize, double value) { static thread_local std::vector values; if (values.size() < requiredSize) { values.resize(requiredSize); } std::transform(values.begin(), values.begin() + requiredSize, values.begin(), [value](double) { return value; }); return metkit::codes::Span{values.data(), requiredSize}; } /// /// @brief Compile-time applicability predicate for the `representation` concept. /// /// This predicate determines whether the `representation` concept is /// instantiated for a given encoding stage and GRIB section. /// /// All representation variants are applicable during: /// - `StageAllocate` /// - `StagePreset` /// /// when operating on the *Grid Definition Section*. /// /// Variant-specific behavior is handled inside the concept operation. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Representation variant /// /// @return `true` if the concept is applicable, `false` otherwise. /// template constexpr bool representationApplicable() { // Disable the concept for the Dummy variant if constexpr (Variant == RepresentationType::Dummy) { return false; } bool condition1 = Stage == StageAllocate && Section == SecGridDefinitionSection; bool condition2 = Stage == StagePreset && Section == SecGridDefinitionSection; return condition1 || condition2; } /// /// @brief Execute the `representation` concept operation. /// /// This function implements the runtime logic of the GRIB `representation` /// concept. /// /// Depending on the encoding stage and the selected `RepresentationType`, /// it performs: /// /// - validation of the Grid Definition Template /// - selection of the GRIB grid type /// - extraction of geometry parameters from eckit::geo::Grid object /// - encoding of grid topology and resolution metadata /// /// The logic is entirely **variant-specific** and selected at compile time /// using `if constexpr`. /// /// @tparam Stage Encoding stage /// @tparam Section GRIB section index /// @tparam Variant Representation 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 (currently unused) /// @param[in] par Parameter dictionary (currently unused) /// @param[in] opt Options dictionary /// @param[out] out Output GRIB dictionary to be populated /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribConceptException /// If: /// - the concept is called when not applicable /// - the GRIB Grid Definition Template does not match expectations /// - required geometry information is missing or inconsistent /// - the representation variant is unsupported /// template void RepresentationOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::dict_traits::setMissing_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (representationApplicable()) { try { MARS2GRIB_LOG_CONCEPT(representation); // ============================================================= // Variant-specific logic // ============================================================= if constexpr (Stage == StageAllocate) { if constexpr (Variant == RepresentationType::Latlon) { // Checks/Validation validation::match_GridDefinitionTemplateNumber_or_throw(opt, out, {0}); // Encoding set_or_throw(out, "gridType", "regular_ll"); } else if constexpr (Variant == RepresentationType::RegularGaussian) { // Checks/Validation validation::match_GridDefinitionTemplateNumber_or_throw(opt, out, {40}); // Encoding set_or_throw(out, "gridType", "regular_gg"); } else if constexpr (Variant == RepresentationType::ReducedGaussian) { // Checks/Validation validation::match_GridDefinitionTemplateNumber_or_throw(opt, out, {40}); // Deductions const auto marsGrid = get_or_throw(mars, "grid"); const eckit::spec::Custom gridSpec = {{"grid", marsGrid}}; const std::unique_ptr genericGrid(eckit::geo::GridFactory::build(gridSpec)); const auto* grid = dynamic_cast(genericGrid.get()); const std::vector plArray = grid->pl(); const long numberOfParallelsBetweenAPoleAndTheEquator = grid->ny() / 2; // Encoding set_or_throw(out, "gridType", "reduced_gg"); set_or_throw(out, "interpretationOfNumberOfPoints", 1L); // Set already, because it is the size of the PL array! set_or_throw(out, "numberOfParallelsBetweenAPoleAndTheEquator", numberOfParallelsBetweenAPoleAndTheEquator); set_or_throw>(out, "pl", plArray); } else if constexpr (Variant == RepresentationType::SphericalHarmonics) { // Checks/Validation validation::match_GridDefinitionTemplateNumber_or_throw(opt, out, {50}); // Encoding set_or_throw(out, "gridType", "sh"); } else if constexpr (Variant == RepresentationType::Healpix) { // Checks/Validation validation::match_GridDefinitionTemplateNumber_or_throw(opt, out, {150}); // Encoding set_or_throw(out, "gridType", "healpix"); } else if constexpr (Variant == RepresentationType::Orca) { // Checks/Validation validation::match_GridDefinitionTemplateNumber_or_throw(opt, out, {101}); // Encoding set_or_throw(out, "gridType", "unstructured_grid"); } else if constexpr (Variant == RepresentationType::Fesom) { // Checks validation::match_GridDefinitionTemplateNumber_or_throw(opt, out, {101}); // Not implemented error MARS2GRIB_CONCEPT_THROW(representation, "Support for Fesom representation not implemented..."); } else { MARS2GRIB_CONCEPT_THROW(representation, "Unknown `representation` variant..."); } } // Preset data representation if constexpr (Stage == StagePreset) { // Resolve allowed reference value deduction double allowedReferenceValue = deductions::resolve_AllowedReferenceValue_or_throw(mars, par, opt); if constexpr (Variant == RepresentationType::Latlon) { // Deductions auto marsGrid = get_or_throw(mars, "grid"); const eckit::spec::Custom gridSpec = {{"grid", marsGrid}}; const std::unique_ptr genericGrid(eckit::geo::GridFactory::build(gridSpec)); const auto* grid = dynamic_cast(genericGrid.get()); const long Ni = grid->nlon(); const long Nj = grid->nlat(); const auto firstPoint = std::get(grid->first_point()); const auto lastPoint = std::get(grid->last_point()); const auto latitudeOfFirstGridPointInDegrees = firstPoint.lat(); const auto longitudeOfFirstGridPointInDegrees = firstPoint.lon(); const auto latitudeOfLastGridPointInDegrees = lastPoint.lat(); const auto longitudeOfLastGridPointInDegrees = lastPoint.lon(); const auto iDirectionIncrementInDegrees = std::abs(grid->dlon()); const auto jDirectionIncrementInDegrees = std::abs(grid->dlat()); // Encoding set_or_throw(out, "resolutionAndComponentFlags", 0); // Flag table 3.3 set_or_throw(out, "Ni", Ni); set_or_throw(out, "Nj", Nj); set_or_throw(out, "latitudeOfFirstGridPointInDegrees", latitudeOfFirstGridPointInDegrees); set_or_throw(out, "longitudeOfFirstGridPointInDegrees", longitudeOfFirstGridPointInDegrees); set_or_throw(out, "latitudeOfLastGridPointInDegrees", latitudeOfLastGridPointInDegrees); set_or_throw(out, "longitudeOfLastGridPointInDegrees", longitudeOfLastGridPointInDegrees); set_or_throw(out, "iDirectionIncrementInDegrees", iDirectionIncrementInDegrees); set_or_throw(out, "jDirectionIncrementInDegrees", jDirectionIncrementInDegrees); // Initialize values with the deduced reference value std::size_t numberOfCoefficients = grid->size(); set_or_throw(out, "values", constValueSpan(numberOfCoefficients, allowedReferenceValue)); } else if constexpr (Variant == RepresentationType::RegularGaussian) { // Deductions const auto marsGrid = get_or_throw(mars, "grid"); const eckit::spec::Custom gridSpec = {{"grid", marsGrid}}; const std::unique_ptr genericGrid(eckit::geo::GridFactory::build(gridSpec)); const auto* grid = dynamic_cast(genericGrid.get()); const auto firstPoint = std::get(grid->first_point()); const auto lastPoint = std::get(grid->last_point()); const auto latitudeOfFirstGridPointInDegrees = firstPoint.lat(); const auto longitudeOfFirstGridPointInDegrees = firstPoint.lon(); const auto latitudeOfLastGridPointInDegrees = lastPoint.lat(); const auto longitudeOfLastGridPointInDegrees = lastPoint.lon(); const auto iDirectionIncrementInDegrees = std::abs(grid->dx()); // TODO (GEOM): numberOfParallelsBetweenAPoleAndTheEquator, and numberOfPointsAlongAMeridian ? // Encoding set_or_throw(out, "resolutionAndComponentFlags", 0); // Flag table 3.3 set_or_throw(out, "latitudeOfFirstGridPointInDegrees", latitudeOfFirstGridPointInDegrees); set_or_throw(out, "longitudeOfFirstGridPointInDegrees", longitudeOfFirstGridPointInDegrees); set_or_throw(out, "latitudeOfLastGridPointInDegrees", latitudeOfLastGridPointInDegrees); set_or_throw(out, "longitudeOfLastGridPointInDegrees", longitudeOfLastGridPointInDegrees); set_or_throw(out, "iDirectionIncrementInDegrees", iDirectionIncrementInDegrees); // Initialize values with the deduced reference value std::size_t numberOfCoefficients = grid->size(); set_or_throw(out, "values", constValueSpan(numberOfCoefficients, allowedReferenceValue)); } else if constexpr (Variant == RepresentationType::ReducedGaussian) { // Deductions const auto marsGrid = get_or_throw(mars, "grid"); const eckit::spec::Custom gridSpec = {{"grid", marsGrid}}; const std::unique_ptr genericGrid(eckit::geo::GridFactory::build(gridSpec)); const auto* grid = dynamic_cast(genericGrid.get()); const auto& latitudes = grid->latitudes(); const auto& longitudes = grid->longitudes(grid->ny() / 2); // at the equator // NOTE: We actually need to describe the extreme latitudes and longitudes! // These 4 values have to be seen as independent, and not as two points. const auto latitudeOfFirstGridPointInDegrees = latitudes.front(); const auto longitudeOfFirstGridPointInDegrees = longitudes.front(); const auto latitudeOfLastGridPointInDegrees = latitudes.back(); const auto longitudeOfLastGridPointInDegrees = longitudes.back(); // TODO (GEOM): numberOfPointsAlongAMeridian ? // Encoding set_or_throw(out, "resolutionAndComponentFlags", 0); // Flag table 3.3 set_or_throw(out, "latitudeOfFirstGridPointInDegrees", latitudeOfFirstGridPointInDegrees); set_or_throw(out, "longitudeOfFirstGridPointInDegrees", longitudeOfFirstGridPointInDegrees); set_or_throw(out, "latitudeOfLastGridPointInDegrees", latitudeOfLastGridPointInDegrees); set_or_throw(out, "longitudeOfLastGridPointInDegrees", longitudeOfLastGridPointInDegrees); setMissing_or_throw(out, "iDirectionIncrement"); // Initialize values with the deduced reference value std::size_t numberOfCoefficients = grid->size(); set_or_throw(out, "values", constValueSpan(numberOfCoefficients, allowedReferenceValue)); } else if constexpr (Variant == RepresentationType::Healpix) { // Deductions const auto marsGrid = get_or_throw(mars, "grid"); const eckit::spec::Custom gridSpec = {{"grid", marsGrid}}; const std::unique_ptr genericGrid(eckit::geo::GridFactory::build(gridSpec)); const auto* grid = dynamic_cast(genericGrid.get()); const auto nside = static_cast(grid->Nside()); const auto orderingConvention = grid->order(); const auto longitudeOfFirstGridPointInDegrees = std::get(grid->first_point()).lon(); // Encoding set_or_throw(out, "resolutionAndComponentFlags", 0); // Flag table 3.3 set_or_throw(out, "Nside", nside); set_or_throw(out, "orderingConvention", orderingConvention); set_or_throw(out, "longitudeOfFirstGridPointInDegrees", longitudeOfFirstGridPointInDegrees); // Initialize values with the deduced reference value std::size_t numberOfCoefficients = grid->size(); set_or_throw(out, "values", constValueSpan(numberOfCoefficients, allowedReferenceValue)); } else if constexpr (Variant == RepresentationType::Orca) { // Deductions const auto marsGrid = get_or_throw(mars, "grid"); const eckit::spec::Custom gridSpec = {{"grid", marsGrid}}; const std::unique_ptr genericGrid(eckit::geo::GridFactory::build(gridSpec)); const auto* grid = dynamic_cast(genericGrid.get()); const auto gridType = grid->name(); const auto gridSubType = grid->arrangement(); const auto uuid = grid->uid(); // Encoding set_or_throw(out, "unstructuredGridType", gridType); set_or_throw(out, "unstructuredGridSubtype", gridSubType); set_or_throw(out, "uuidOfHGrid", uuid); // Initialize values with the deduced reference value std::size_t numberOfCoefficients = grid->size(); set_or_throw(out, "values", constValueSpan(numberOfCoefficients, allowedReferenceValue)); } else if constexpr (Variant == RepresentationType::Fesom) { MARS2GRIB_CONCEPT_THROW(representation, "Support for Fesom representation not implemented..."); } else if constexpr (Variant == RepresentationType::SphericalHarmonics) { // Deductions const auto marsTruncation = get_or_throw(mars, "truncation"); const auto pentagonalResolutionParameterJ = marsTruncation; const auto pentagonalResolutionParameterK = marsTruncation; const auto pentagonalResolutionParameterM = marsTruncation; // Encoding set_or_throw(out, "pentagonalResolutionParameterJ", pentagonalResolutionParameterJ); set_or_throw(out, "pentagonalResolutionParameterK", pentagonalResolutionParameterK); set_or_throw(out, "pentagonalResolutionParameterM", pentagonalResolutionParameterM); // Initialize values with the deduced reference value std::size_t numberOfCoefficients = (marsTruncation + 1) * (marsTruncation + 2); set_or_throw(out, "values", constValueSpan(numberOfCoefficients, allowedReferenceValue)); } else { MARS2GRIB_CONCEPT_THROW(representation, "Unknown `representation` variant..."); }; }; } catch (...) { MARS2GRIB_CONCEPT_RETHROW(representation, "Unable to set `representation` concept..."); } // Successful operation return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(representation, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/representation/representationEnum.h0000664000175000017500000001273315232567717031575 0ustar alastairalastair/* * (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 representationEnum.h /// @brief Definition of the `representation` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `representation` /// concept used by the mars2grib backend. It contains: /// /// - the canonical concept name (`representationName`) /// - the exhaustive enumeration of supported spatial representations (`RepresentationType`) /// - a compile-time typelist of all variants (`RepresentationList`) /// - 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 /// `representationEncoding.h` 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 `representation` 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 `representation` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view representationName{"representation"}; /// /// @brief Enumeration of all supported `representation` concept variants. /// /// Each enumerator represents a distinct spatial grid or spectral /// representation used to encode meteorological fields. /// /// 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 includes structured grids, spectral representations, /// and unstructured meshes used by atmosphere and ocean models. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class RepresentationType : std::size_t { Latlon = 0, RegularGaussian, ReducedGaussian, SphericalHarmonics, GeneralUnstructured, Healpix, Orca, Fesom, Dummy, // Used to skip section3 resolution and delegate to gridSpec Default }; /// /// @brief Compile-time list of all `representation` 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 RepresentationList = ValueList; /// /// @brief Compile-time mapping from `RepresentationType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given representation variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Representation 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 representationTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view representationTypeName() { \ return NAME; \ } DEF(RepresentationType::Latlon, "latlon"); DEF(RepresentationType::RegularGaussian, "regularGaussian"); DEF(RepresentationType::ReducedGaussian, "reducedGaussian"); DEF(RepresentationType::SphericalHarmonics, "sphericalHarmonics"); DEF(RepresentationType::GeneralUnstructured, "generalUnstructured"); DEF(RepresentationType::Healpix, "healpix"); DEF(RepresentationType::Orca, "orca"); DEF(RepresentationType::Fesom, "fesom"); DEF(RepresentationType::Dummy, "dummy"); DEF(RepresentationType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/representation/representationConceptDescriptor.h0000664000175000017500000001223215232567717034315 0ustar alastairalastair/* * (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 representationConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `representation` concept. /// /// This header defines `RepresentationConcept`, the **compile-time descriptor** /// that registers the GRIB `representation` 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/representation/representationEncoding.h" #include "metkit/mars2grib/backend/concepts/representation/representationEnum.h" #include "metkit/mars2grib/backend/concepts/representation/representationMatcher.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 `representation` concept. /// /// `RepresentationConcept` registers the GRIB `representation` concept into the /// compile-time registry engine. /// struct RepresentationConcept : RegisterEntryDescriptor { /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - registry identification /// - diagnostics and logging /// - debug and introspection utilities /// static constexpr std::string_view entryName() { return representationName; } /// /// @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 representationTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This hook is queried by the registry engine to obtain the function /// implementing the `representation` concept for a given: /// - capability /// - stage /// - GRIB section /// - variant /// /// The function returns either a valid function pointer or `nullptr` if the /// combination is not applicable. /// /// @tparam Capability Encoding capability index /// @tparam Stage Encoding stage /// @tparam Sec GRIB section /// @tparam Variant Concept variant /// template static constexpr Fn phaseCallbacks() { if constexpr (Capability == 0) { if constexpr (representationApplicable()) { return &RepresentationOp; } else { return nullptr; } } else { return nullptr; } mars2gribUnreachable(); } template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This hook is invoked to determine whether the `representation` concept should be /// activated for a given request. /// /// @tparam Capability Matching/encoding capability index /// @return Matcher function pointer, or `nullptr` if not participating. /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &representationMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/data-type/0000775000175000017500000000000015232567717024355 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/concepts/data-type/dataTypeEnum.h0000664000175000017500000001044515232567717027132 0ustar alastairalastair/* * (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 dataTypeEnum.h /// @brief Definition of the `dataType` concept variants and compile-time metadata. /// /// This header defines the **static description** of the GRIB `dataType` concept /// used by the mars2grib backend. It contains: /// /// - the canonical concept name (`dataTypeName`) /// - the enumeration of supported data type variants (`DataTypeType`) /// - a compile-time typelist of all variants (`DataTypeList`) /// - 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 /// `dataTypeEncoding.h` 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 `dataType` 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 `dataType` concept /// /// The value must remain stable across releases. /// inline constexpr std::string_view dataTypeName{"dataType"}; /// /// @brief Enumeration of all supported `dataType` concept variants. /// /// Each enumerator represents a specific data type or semantic interpretation /// 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 data type concept evolves. /// /// @warning /// Do not reorder existing enumerators, as they are used in compile-time /// tables and registries. /// enum class DataTypeType : std::size_t { Default = 0 }; /// /// @brief Compile-time list of all `dataType` 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 DataTypeList = ValueList; /// /// @brief Compile-time mapping from `DataTypeType` to human-readable name. /// /// This function returns the canonical string identifier associated /// with a given data type variant. /// /// The returned value is used for: /// - logging and debugging output /// - error reporting /// - concept registry diagnostics /// /// @tparam T Data type 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 dataTypeTypeName(); #define DEF(T, NAME) \ template <> \ constexpr std::string_view dataTypeTypeName() { \ return NAME; \ } DEF(DataTypeType::Default, "default"); #undef DEF } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/data-type/dataTypeConceptDescriptor.h0000664000175000017500000001457515232567717031670 0ustar alastairalastair/* * (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 dataTypeConceptDescriptor.h /// @brief Compile-time registry entry for the GRIB `dataType` concept. /// /// This header defines `DataTypeConcept`, the **compile-time descriptor** /// that registers the GRIB `dataType` 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/data-type/dataTypeEncoding.h" #include "metkit/mars2grib/backend/concepts/data-type/dataTypeEnum.h" #include "metkit/mars2grib/backend/concepts/data-type/dataTypeMatcher.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 `dataType` concept. /// /// `DataTypeConcept` registers the GRIB `dataType` 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 DataTypeConcept : 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 dataTypeName; } /// /// @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 dataTypeTypeName(); } /// /// @brief Return the callback associated with a specific encoding phase. /// /// This function is queried by the registry engine to obtain the /// callback implementing the `dataType` 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 (dataTypeApplicable()) { return &DataTypeOp; } else { return nullptr; } } else { return nullptr; } // Avoid compiler warnings mars2gribUnreachable(); } /// /// @brief Variant-specific callbacks (not used for this concept). /// /// This hook is provided for completeness of the registry interface. /// The `dataType` concept does not define variant-level callbacks, /// so this function always returns `nullptr`. /// /// @tparam Capability Encoding capability index /// @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 Always `nullptr` /// template static constexpr Fn variantCallbacks() { return nullptr; } /// /// @brief Entry-level matcher callback. /// /// This callback is invoked to determine whether the `dataType` /// concept should be activated for a given encoding request. /// /// @tparam MarsDict_t Type of MARS dictionary /// @tparam OptDict_t Type of options dictionary /// /// @return Matcher function pointer /// template static constexpr Fm entryCallbacks() { if constexpr (Capability == 0) { return &dataTypeMatcher; } else { return nullptr; } } }; } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/data-type/dataTypeMatcher.h0000664000175000017500000000442115232567717027606 0ustar alastairalastair/* * (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 dataTypeMatcher.h /// @brief Entry-level matcher for the GRIB `dataType` concept. /// /// This header defines the runtime matcher used by the concept registry to /// activate the default data-type concept variant. /// /// The matcher follows the standard mars2grib matching contract: /// - return a local concept variant index when the concept is active, /// - return `compile_time_registry_engine::MISSING` when it is not active, /// - wrap runtime failures as nested `Mars2GribMatcherException` instances. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System include #include #include // Utils #include "metkit/mars2grib/backend/concepts/data-type/dataTypeEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Match the `dataType` concept variant. /// /// The data-type concept is always active and resolves to /// `DataTypeType::Default`. /// /// @tparam MarsDict_t Type of the MARS input dictionary /// @tparam OptDict_t Type of the options dictionary /// /// @param[in] mars MARS input dictionary /// @param[in] opt Options dictionary /// /// @return Local variant index for `DataTypeType::Default`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribMatcherException /// If matcher evaluation fails. Lower-level exceptions are preserved through /// `std::throw_with_nested`. /// template std::size_t dataTypeMatcher(const MarsDict_t& mars, const OptDict_t& opt) { try { return static_cast(DataTypeType::Default); } catch (...) { std::throw_with_nested( utils::exceptions::Mars2GribMatcherException("Unable to match `dataType` concept", Here())); } } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/data-type/dataTypeEncoding.h0000664000175000017500000001604115232567717027752 0ustar alastairalastair/* * (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 dataTypeEncoding.h /// @brief Implementation of the GRIB `dataType` concept operation. /// /// This header defines the applicability rules and execution logic for the /// **dataType concept** within the mars2grib backend. /// /// The concept is responsible for populating GRIB keys in the /// *Identification Section* related to the classification of the processed /// data product, namely: /// /// - `typeOfProcessedData` /// - `productionStatusOfProcessedData` /// /// The concept itself does not implement semantic deduction logic. Instead, /// it delegates: /// - semantic resolution to dedicated deduction functions /// - value validation and encoding correctness to GRIB tables /// /// The implementation follows the standard mars2grib concept model: /// - Compile-time applicability via `data_typeApplicable` /// - Runtime deduction via backend deductions /// - 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 // System includes #include // Core concept includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/concepts/data-type/dataTypeEnum.h" #include "metkit/mars2grib/utils/generalUtils.h" // Deductions #include "metkit/mars2grib/backend/deductions/productionStatusOfProcessedData.h" #include "metkit/mars2grib/backend/deductions/typeOfProcessedData.h" // Tables #include "metkit/mars2grib/backend/tables/productionStatusOfProcessedData.h" #include "metkit/mars2grib/backend/tables/typeOfProcessedData.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 `dataType` concept. /// /// This predicate determines whether the `dataType` 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 ensure that only valid concept instantiations occur. /// /// @tparam Stage Encoding stage (compile-time constant) /// @tparam Section GRIB section index (compile-time constant) /// @tparam Variant Data type 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 == DataTypeType::Default` /// - `Stage == StagePreset` /// - `Section == SecIdentificationSection` /// /// Users may override or specialize this predicate to alter applicability. /// template constexpr bool dataTypeApplicable() { return ((Variant == DataTypeType::Default) && (Stage == StageOverride) && (Section == SecIdentificationSection)); } /// /// @brief Execute the `dataType` concept operation. /// /// This function implements the runtime logic of the GRIB `dataType` concept. /// When applicable, it: /// /// 1. Deduces the `typeOfProcessedData` from MARS and parameter dictionaries. /// 2. Deduces the `productionStatusOfProcessedData` from MARS and parameter dictionaries. /// 3. Encodes both values into the GRIB Identification Section. /// /// The concept acts as a **pure orchestration layer**: /// - All semantic logic is delegated to deduction functions. /// - All value correctness is guaranteed by table-backed enumerations. /// /// 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 Data type 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 concept is called when not applicable /// - a deduction fails /// - a GRIB key cannot be set /// /// @note /// - All runtime errors are wrapped with full concept context /// (concept name, variant, stage, section). /// - This concept does not perform validation beyond what is enforced /// by the deduction and table layers. /// /// @see dataTypeApplicable /// template void DataTypeOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; if constexpr (dataTypeApplicable()) { try { MARS2GRIB_LOG_CONCEPT(dataType); // Deductions tables::TypeOfProcessedData typeOfProcessedData = deductions::resolve_TypeOfProcessedData_or_throw(mars, par, opt); tables::ProductionStatusOfProcessedData productionStatusOfProcessedData = deductions::resolve_ProductionStatusOfProcessedData_or_throw(mars, par, opt); // Encoding // @todo -> set_or_throw(out, "typeOfProcessedData", // enum2name_TypeOfProcessedData_or_throw(typeOfProcessedData)); set_or_throw(out, "typeOfProcessedData", static_cast(typeOfProcessedData)); set_or_throw(out, "productionStatusOfProcessedData", static_cast(productionStatusOfProcessedData)); } catch (...) { MARS2GRIB_CONCEPT_RETHROW(dataType, "Unable to set `dataType` concept..."); } return; } // Concept invoked outside its applicability domain MARS2GRIB_CONCEPT_THROW(dataType, "Concept called when not applicable..."); // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::concepts_ metkit-1.19.2/src/metkit/mars2grib/backend/concepts/MatchingCallbacksRegistry.h0000664000175000017500000001732415232567717027730 0ustar alastairalastair/* * (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 MatchingCallbacksRegistry.h /// @brief Compile-time registry of entry-level matching callbacks. /// /// This header defines the **fully materialized matching dispatch table** /// used by the mars2grib backend to perform *semantic matching* of input /// dictionaries against the concept universe. /// /// ----------------------------------------------------------------------------- /// Conceptual model /// ----------------------------------------------------------------------------- /// /// Matching is modeled as a one-dimensional dispatch space: /// /// \code /// matchingCallbacks[conceptId] -> Fm | nullptr /// \endcode /// /// where: /// - `conceptId` is the compile-time concept identifier defined by /// `GeneralRegistry` / `EntryVariantRegistry`, /// - `Fm` is a dictionary-specialized matcher function, /// - `nullptr` indicates that the concept does not participate in matching /// for the selected capability. /// /// Each matcher is responsible for determining whether its corresponding /// concept is *active* for a given pair of input dictionaries. /// /// ----------------------------------------------------------------------------- /// Capability selection /// ----------------------------------------------------------------------------- /// /// This registry is parameterized by a compile-time **capability index** /// (here fixed to `0`). /// /// Capabilities allow the same concept universe to expose multiple independent /// matching planes (e.g. alternative semantics, validation modes, feature /// subsets) without duplicating registry infrastructure. /// /// ----------------------------------------------------------------------------- /// Scope and responsibility /// ----------------------------------------------------------------------------- /// /// This file is responsible only for: /// - binding the generic entry-level registry machinery /// (`makeEntryCallbacksRegistry`) to: /// - the complete concept universe (`AllConcepts`), /// - a specific capability index, /// - concrete dictionary types. /// /// It does **not**: /// - define any concept descriptors, /// - implement matching logic, /// - perform runtime dispatch or iteration, /// - introduce new compile-time algorithms. /// /// ----------------------------------------------------------------------------- /// Architectural role /// ----------------------------------------------------------------------------- /// /// `MatchingCallbacksRegistry` provides the **semantic entry point** to the /// resolution pipeline. /// /// Its output is typically consumed by: /// - active concept resolution, /// - semantic filtering of variants, /// - downstream structural resolution stages. /// /// ----------------------------------------------------------------------------- /// Design constraints /// ----------------------------------------------------------------------------- /// /// - Header-only /// - Fully constexpr /// - No runtime state /// - No dynamic allocation /// /// This header is safe to include transitively in performance-critical code. /// /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include // Project includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/compile-time-registry-engine/makeEntryCallbacksRegistry.h" #include "metkit/mars2grib/backend/concepts/AllConcepts.h" #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::concepts_ { /// /// @brief Compile-time registry exposing entry-level matching callbacks. /// /// This class template materializes a complete table of matcher functions, /// one per concept, specialized for a fixed pair of dictionary types. /// /// @tparam MarsDict_t /// Type of the MARS request dictionary. /// /// @tparam OptDict_t /// Type of the options dictionary. /// /// ----------------------------------------------------------------------------- /// Lifetime and usage /// ----------------------------------------------------------------------------- /// /// All members of this registry are: /// - `static`, /// - `constexpr`, /// - immutable. /// /// No instances of this class are ever constructed. /// /// The registry is intended to be accessed as: /// /// \code /// MatchingCallbacksRegistry<...>::matchingCallbacks /// \endcode /// /// ----------------------------------------------------------------------------- /// Structural guarantees /// ----------------------------------------------------------------------------- /// /// The ordering of the callback table is strictly defined by: /// - the order of concepts in `detail::AllConcepts`. /// /// The index into this table is therefore stable and consistent with: /// - concept identifiers returned by `GeneralRegistry::conceptId()`, /// - all downstream registry layers. /// /// Any change to the concept list or its ordering constitutes a breaking /// structural change. /// template struct MatchingCallbacksRegistry { /// /// @brief Number of concepts exposed by the registry. /// /// This constant anchors the dependency on `GeneralRegistry` and /// defines the expected size of the matching callback table. /// static constexpr std::size_t registry_size = GeneralRegistry::NConcepts; /// /// @brief Canonical encoding function pointer type. /// /// This alias exposes the exact function signature used for all encoding /// callbacks stored in the registry. /// /// It is provided primarily for: /// - readability, /// - consistency with higher-level abstractions, /// - avoiding repetition of long qualified names. /// using Fm_t = metkit::mars2grib::backend::compile_time_registry_engine::Fm; /// /// @brief Fully materialized matching dispatch table. /// /// This static data member contains the complete entry-level matching /// registry for: /// - all concepts, /// - a single capability index (`0`), /// - the dictionary types bound to this registry. /// /// The table is generated entirely at compile time by invoking /// `makeEntryCallbacksRegistry` with: /// - the full concept universe (`AllConcepts`), /// - capability index `0`, /// - the dictionary types bound to this registry. /// /// Each entry is either: /// - a valid matcher function (`Fm`), or /// - `nullptr` if the concept does not participate in matching. /// /// This table is typically consumed by the *semantic resolution* layer /// to determine which concepts and variants are active for a given /// input request. /// static constexpr auto matchingCallbacks = metkit::mars2grib::backend::compile_time_registry_engine::makeEntryCallbacksRegistry(); /// /// @brief Compile-time structural verification. /// /// Ensures that the generated callback table size matches the number /// of concepts defined by `GeneralRegistry`. /// static_assert(matchingCallbacks.size() == registry_size, "MatchingCallbacksRegistry: callback table size does not match GeneralRegistry"); }; } // namespace metkit::mars2grib::backend::concepts_metkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/0000775000175000017500000000000015232567717026344 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/makeEntryCallbacksRegistry.h0000664000175000017500000001716115232567717034013 0ustar alastairalastair/* * (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 EntryCallbacksRegistry.h /// @brief Compile-time global registry for entry-level callbacks. /// /// This header defines the machinery required to **materialize, at compile time, /// a dense lookup table of entry-level callbacks**, indexed by a *capability ID* /// and by *entry index*. /// /// ----------------------------------------------------------------------------- /// Conceptual model /// ----------------------------------------------------------------------------- /// /// Given: /// /// - a compile-time list of Entry descriptors (`EntriesList`) /// - a compile-time capability identifier (`Capability`) /// - concrete dictionary types (`MarsDict_t`, `OptDict_t`) /// /// this facility builds a constexpr table of the form: /// /// @code /// entryCallbacks[entryIndex] -> Fm | nullptr /// @endcode /// /// where: /// /// - `entryIndex` is the index of the Entry in `EntriesList` /// - `Fm` is a function pointer type /// - `nullptr` denotes “capability not supported by this entry” /// /// ----------------------------------------------------------------------------- /// Design goals /// ----------------------------------------------------------------------------- /// /// - **Pure compile-time construction** /// All tables are built via constexpr recursion and concatenation. /// /// - **Zero runtime branching** /// Capability selection is resolved entirely at compile time. /// /// - **Dense layout** /// One callback slot per entry, preserving EntryList order. /// /// - **Header-only** /// Safe for inclusion in multiple translation units. /// /// ----------------------------------------------------------------------------- /// Assumptions and invariants /// ----------------------------------------------------------------------------- /// /// Each Entry type in `EntriesList` is assumed to provide: /// /// @code /// template /// static constexpr Fm entryCallbacks(); /// @endcode /// /// returning either: /// - a valid function pointer implementing the capability, or /// - `nullptr` if the capability is not supported. /// /// Failure to meet this contract results in a compile-time error. /// #pragma once /// /// @file EntryCallbacksRegistry.h /// @brief Compile-time global registry for entry-level callbacks. /// /// Builds a compile-time table: /// /// entryCallbacks[entryIndex] -> Fm | nullptr /// /// Capability is selected via a compile-time std::size_t index. /// // System includes #include #include // Project includes #include "metkit/mars2grib/backend/compile-time-registry-engine/RegisterEntryDescriptor.h" #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/compile-time-registry-engine/utils.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::compile_time_registry_engine { /// /// @namespace detail /// @brief Internal implementation details of the entry callbacks registry. /// /// This namespace contains low-level templates used to: /// - instantiate per-entry callbacks /// - recursively concatenate them into a flat constexpr array /// /// All entities in this namespace are considered **implementation details** /// and must not be used directly outside this header. /// namespace detail { /// /// @brief Instantiate the callback for a single Entry and capability. /// /// This function delegates to the Entry’s static `entryCallbacks` interface /// and returns the function pointer corresponding to the requested capability. /// /// @tparam Entry Entry descriptor type /// @tparam Capability Compile-time capability identifier /// @tparam MarsDict_t MARS dictionary type /// @tparam OptDict_t Options dictionary type /// /// @return /// A function pointer of type `Fm`, or `nullptr` /// if the Entry does not implement the given capability. /// /// @note /// This function is `constexpr` and intended to be invoked only during /// compile-time table construction. /// template constexpr Fm makeEntryCallback() { return Entry::template entryCallbacks(); } /// /// @brief Compile-time builder for the entry callbacks table. /// /// This metafunction recursively traverses the `EntriesList` typelist and /// concatenates the callback corresponding to each Entry into a flat array. /// /// The resulting array has: /// - size equal to `EntriesList::size` /// - element `i` corresponding to the i-th Entry in the typelist /// /// @tparam EntriesList TypeList of Entry descriptors /// @tparam Capability Compile-time capability identifier /// @tparam MarsDict_t MARS dictionary type /// @tparam OptDict_t Options dictionary type /// template struct BuildEntryCallbacks; /// /// @brief Base case for an empty EntriesList. /// /// Produces an empty constexpr array. /// template struct BuildEntryCallbacks, Capability, MarsDict_t, OptDict_t> { static constexpr std::array, 0> value() { return {}; } }; /// /// @brief Recursive case: prepend callback for Head and recurse on Tail. /// /// This specialization: /// - builds a one-element array containing Head’s callback /// - recursively builds the tail array /// - concatenates the two using `concat` /// /// The order of Entries in the TypeList is strictly preserved. /// template struct BuildEntryCallbacks, Capability, MarsDict_t, OptDict_t> { static constexpr auto value() { constexpr auto head = std::array, 1>{makeEntryCallback()}; constexpr auto tail = BuildEntryCallbacks, Capability, MarsDict_t, OptDict_t>::value(); return concat(head, tail); } }; } // namespace detail /// /// @brief Construct the compile-time entry callbacks registry. /// /// This is the **public API** of the entry callbacks registry. /// /// It materializes, at compile time, a dense array of entry-level callbacks /// corresponding to the specified capability and dictionary types. /// /// @tparam EntriesList TypeList of Entry descriptors /// @tparam Capability Compile-time capability identifier /// @tparam MarsDict_t MARS dictionary type /// @tparam OptDict_t Options dictionary type /// /// @return /// A `constexpr std::array, N>` where: /// - `N == EntriesList::size` /// - element `i` is the callback for the i-th Entry /// /// @note /// The returned array is intended to be: /// - stored as a `static constexpr` object /// - indexed directly in hot paths without branching /// template constexpr auto makeEntryCallbacksRegistry() { return detail::BuildEntryCallbacks::value(); } } // namespace metkit::mars2grib::backend::compile_time_registry_engine metkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/common.h0000664000175000017500000001260415232567717030010 0ustar alastairalastair/* * (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 common.h /// @brief Canonical compile-time vocabulary shared by all concept dispatch registries. /// /// This header defines the **fundamental compile-time building blocks** used /// uniformly across the mars2grib dispatch infrastructure. /// /// Specifically, it provides: /// - fixed pipeline dimensions (`NUM_STAGES`, `NUM_SECTIONS`), /// - canonical numeric identifiers for encoding stages and GRIB sections, /// - sentinel value representing non-semantic or invalid states (`MISSING`), /// - canonical function pointer types (`Fn`, `Fm`) for dispatch interfaces, /// - minimal compile-time containers (`ValueList`, `TypeList`) used to model /// value and type universes. /// /// This header is intentionally **purely declarative**: /// - it contains no registry construction logic, /// - no compile-time recursion or algorithms, /// - and no concept-specific specialization. /// /// All higher-level registry engines rely on the definitions in this file /// as a **stable, shared vocabulary**. /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include #include #include #include // Project includes #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::compile_time_registry_engine { /// /// @name Sentinel value /// @{ /// /// This constant defines a **sentinel value** used throughout the mars2grib /// codebase to represent a special, non-semantic state. /// /// It is defined as an `inline constexpr` value rather than an enum to: /// - allow direct use in compile-time contexts, /// - avoid implicit conversions or scoped enum verbosity, /// - support use as array indices or placeholders. /// /// @note /// This value must remain outside the valid range of any real index, /// identifier, or enumerated domain it is compared against. /// inline constexpr std::size_t MISSING = 9999999; /// @} /// /// @name Encoding pipeline dimensions /// @{ /// /// These constants define the fixed dimensions of the mars2grib encoding pipeline. /// /// They are deliberately defined as `inline constexpr` values rather than enums, /// because they are iterated over at compile time using index sequences. /// /// Changing these values directly affects: /// - the size of all generated dispatch tables, /// - the number of compile-time instantiations. /// inline constexpr std::size_t NUM_STAGES = 4; inline constexpr std::size_t NUM_SECTIONS = 6; /// @} /// /// @name Encoding stages /// @{ /// /// Logical stages of the encoding pipeline. /// /// Each concept may participate in zero or more stages, as determined by its /// compile-time applicability predicate (encoded by returning nullptr in /// concept `encodingCallbacks()` entries). /// inline constexpr std::size_t StageAllocate = 0; ///< Structure allocation stage inline constexpr std::size_t StagePreset = 1; ///< Metadata preset stage inline constexpr std::size_t StageOverride = 2; ///< Metadata override stage inline constexpr std::size_t StageRuntime = 3; ///< Runtime-dependent encoding /// @} /// /// @name GRIB2 sections /// @{ /// /// Numeric identifiers for GRIB2 sections, aligned with the GRIB2 specification: /// https://codes.ecmwf.int/grib/format/grib2/sections/ /// /// These values are used as compile-time indices into dispatch tables. /// inline constexpr std::size_t SecIndicatorSection = 0; inline constexpr std::size_t SecIdentificationSection = 1; inline constexpr std::size_t SecLocalUseSection = 2; inline constexpr std::size_t SecGridDefinitionSection = 3; inline constexpr std::size_t SecProductDefinitionSection = 4; inline constexpr std::size_t SecDataRepresentationSection = 5; /// @} /// /// @brief Canonical function pointer type for concept encoding operations. /// /// Each dispatch table cell contains a pointer to a fully specialized concept operation /// for a fixed (stage, section, variant). Inapplicable combinations return nullptr. /// template using Fn = void (*)(const MarsDict_t&, const ParDict_t&, const OptDict_t&, OutDict_t&); /// /// @brief Canonical function pointer type for concept matcher operations. /// /// Matchers are dictionary-specialized (no stage/section/variant dimensions). /// template using Fm = std::size_t (*)(const MarsDict_t&, const OptDict_t&); /// /// @brief Compile-time list of values. /// /// A lightweight container for a pack of compile-time constants (typically enum values). /// Iteration is performed via template pack expansion (no runtime loops). /// template struct ValueList { static constexpr std::size_t size = sizeof...(Vals); }; /// /// @brief Compile-time list of types. /// /// A lightweight container for a pack of types. Used to represent the concept universe. /// template struct TypeList { static constexpr std::size_t size = sizeof...(Ts); }; } // namespace metkit::mars2grib::backend::compile_time_registry_engine metkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/EntryVariantRegistry.h0000664000175000017500000005626715232567717032714 0ustar alastairalastair/* * (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 EntryVariantRegistry.h /// @brief Compile-time registry engine for concept/variant indexing and metadata. /// /// This header implements the **core compile-time indexing machinery** used by /// mars2grib to map *concepts* and *their variants* onto stable, contiguous /// integer domains. /// /// The registry is entirely **header-only** and operates exclusively at /// compile time, producing constexpr tables that are later consumed by /// high-performance runtime code (dispatch tables, encoding plans, etc.). /// /// ----------------------------------------------------------------------------- /// Design overview /// ----------------------------------------------------------------------------- /// /// The fundamental abstraction is a **TypeList of Entry descriptors**, where /// each Entry represents: /// /// - one semantic concept (e.g. "levelType", "timeRange") /// - a fixed number of variants /// - an associated enum type (`Entry::Variant`) /// /// From this list, the registry derives: /// /// - global variant indices (flattened space) /// - per-concept local indices /// - concept identifiers /// - name lookup tables (concept names, variant names) /// /// All tables are: /// /// - constexpr /// - contiguous /// - allocation-free /// - index-stable /// /// ----------------------------------------------------------------------------- /// Two-stage model /// ----------------------------------------------------------------------------- /// /// The registry is conceptually split into two stages: /// /// - **Stage 1 (Index arithmetic)**: /// Computes offsets, indices, and relationships between concepts and /// variants. /// /// - **Stage 2 (Table materialization)**: /// Builds constexpr arrays that can be used directly at runtime. /// /// ----------------------------------------------------------------------------- /// Constraints and invariants /// ----------------------------------------------------------------------------- /// /// The following invariants are assumed and enforced: /// /// - Every Entry type provides: /// - `static constexpr std::size_t variant_count` /// - `using Variant = ` /// - `using VariantList = ValueList<...>` /// - `static constexpr std::string_view entryName()` /// - `template static constexpr std::string_view variantName()` /// /// - The order of Entries in the TypeList defines: /// - concept identifiers /// - block ordering in flattened tables /// /// - All variant enums are disjoint by type. /// /// Violation of these assumptions results in a compile-time error. /// #pragma once // system includes #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/compile-time-registry-engine/RegisterEntryDescriptor.h" #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/backend/compile-time-registry-engine/utils.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::compile_time_registry_engine { /// /// @namespace detail /// @brief Internal implementation details of the compile-time registry engine. /// /// This namespace contains **low-level meta-programming utilities** that are /// not part of the public API and must not be relied upon directly by users. /// /// All entities in this namespace are subject to change without notice. /// namespace detail { // ================================================================================================= // 0) Helper: dependent_false // ================================================================================================= /// /// @brief Helper metafunction to trigger dependent compile-time errors. /// /// `dependent_false` always evaluates to `false`, but is dependent on /// its template parameters. This allows `static_assert` to be placed in /// templates without causing immediate hard errors during parsing. /// /// Typical use: /// - provide meaningful diagnostics in impossible template branches /// template struct dependent_false : std::false_type {}; // ================================================================================================= // 1) TypeList utilities // ================================================================================================= /// /// @brief Compile-time index lookup of a type inside a TypeList. /// /// `IndexOf::value` yields the zero-based index of `T` within `List`. /// /// @tparam T Type to search for /// @tparam List TypeList to scan /// /// @note /// The behavior is undefined if `T` does not appear in `List`. /// Such misuse is considered a logic error in registry construction. /// template struct IndexOf; /// /// @brief Recursive case: head matches the searched type. /// template struct IndexOf> : std::integral_constant {}; /// /// @brief Recursive case: head does not match, continue scanning. /// template struct IndexOf> : std::integral_constant>::value> {}; /// /// @brief Compute the total number of variants across all entries. /// /// This metafunction folds over the TypeList and accumulates /// `Entry::variant_count` for each Entry. /// /// @tparam List TypeList of Entry descriptors /// template struct TotalVariantCount; template <> struct TotalVariantCount> : std::integral_constant {}; template struct TotalVariantCount> : std::integral_constant>::value> {}; // ================================================================================================= // 2) CSR-style offsets: EntryOffset // ================================================================================================= /// /// @brief Compute the global offset of the I-th Entry in the flattened variant space. /// /// The offset is defined as the sum of `variant_count` of all preceding Entries. /// /// This is analogous to CSR (Compressed Sparse Row) prefix sums. /// /// @tparam I Entry index /// @tparam List TypeList of Entries /// template struct EntryOffset; template struct EntryOffset<0, TypeList> : std::integral_constant {}; template struct EntryOffset> : std::integral_constant>::value> {}; // ================================================================================================= // 3) Map Variant enum type -> Entry by scanning the typelist // ================================================================================================= /// /// @brief Map a variant enum type to its owning Entry descriptor. /// /// This metafunction scans the Entries TypeList and selects the Entry whose /// `Entry::Variant` type exactly matches the given enum. /// /// @tparam Enum Variant enum type /// @tparam List Entries TypeList /// /// @note /// Failure to find a matching Entry results in a compile-time error. /// template struct EntryFromVariantEnum; template struct EntryFromVariantEnum, void> { static_assert(dependent_false::value, "Variant enum type not associated to any entry in EntriesList. " "Check: (a) entry is in the TypeList, (b) Entry::Variant matches exactly."); using type = void; }; template struct EntryFromVariantEnum, std::enable_if_t>> { using type = Head; }; template struct EntryFromVariantEnum, std::enable_if_t>> { using type = typename EntryFromVariantEnum>::type; }; // ================================================================================================= // 4) VariantList-driven local index (constexpr search) // ================================================================================================= /// /// @brief Compute the local index of a variant within its owning concept. /// /// This is a constexpr linear search over a ValueList. /// /// @param v Variant enum value /// @return Local index, or `-1` cast to `std::size_t` if not found /// /// @note /// Returning `-1` is intentional; callers translate this into the `missing` /// sentinel. /// template constexpr std::size_t value_index_impl(Enum, std::size_t, ValueList<>) { return static_cast(-1); } template constexpr std::size_t value_index_impl(Enum v, std::size_t pos, ValueList) { return (v == static_cast(Head)) ? pos : value_index_impl(v, pos + 1, ValueList{}); } template constexpr std::size_t value_index(Enum v, VariantListT) { return value_index_impl(v, 0, VariantListT{}); } // ================================================================================================= // 6) Per-entry blocks for Stage2 // (NOTE: ConceptsT is passed explicitly => no "Concepts" free name) // ================================================================================================= /// /// @brief Generate a block of repeated concept identifiers. /// /// Each concept contributes a contiguous block of size `variant_count`, /// where every element equals the concept identifier. /// /// This is used to build the flattened concept-id table. /// template constexpr std::array concept_id_block_impl(std::index_sequence) { return {{(static_cast(Is), ConceptIndex)...}}; } template constexpr std::array concept_id_block() { return concept_id_block_impl(std::make_index_sequence{}); } template constexpr std::array variant_id_block_impl(std::index_sequence) { return {{Is...}}; } template constexpr std::array variant_id_block() { return variant_id_block_impl(std::make_index_sequence{}); } template constexpr std::array concept_name_block_impl(std::index_sequence) { return {{(static_cast(Is), Concept::entryName())...}}; } template constexpr std::array concept_name_block() { return concept_name_block_impl(std::make_index_sequence{}); } template constexpr std::array variant_name_block_impl(ValueList) { return {{Concept::template variantName()...}}; } template constexpr auto variant_name_block() { return variant_name_block_impl(typename Concept::VariantList{}); } // ================================================================================================= // 7) Build full tables by recursion over ConceptsT // ================================================================================================= /// /// @brief Build full registry tables by recursion over the Entries TypeList. /// /// Each builder recursively concatenates per-entry blocks to form a /// flattened table aligned with the global variant index space. /// /// All builders terminate on `TypeList<>`. /// template struct BuildConceptIdTableWithBase; template struct BuildConceptIdTableWithBase, BaseIndex> { static constexpr std::array value() { return {}; } }; template struct BuildConceptIdTableWithBase, BaseIndex> { static constexpr auto value() { return concat(concept_id_block(), BuildConceptIdTableWithBase, BaseIndex + 1>::value()); } }; template struct BuildVariantIdTable; template <> struct BuildVariantIdTable> { static constexpr std::array value() { return {}; } }; template struct BuildVariantIdTable> { static constexpr auto value() { return concat(variant_id_block(), BuildVariantIdTable>::value()); } }; template struct BuildConceptNameTable; template <> struct BuildConceptNameTable> { static constexpr std::array value() { return {}; } }; template struct BuildConceptNameTable> { static constexpr auto value() { return concat(concept_name_block(), BuildConceptNameTable>::value()); } }; template struct BuildVariantNameTable; template <> struct BuildVariantNameTable> { static constexpr std::array value() { return {}; } }; template struct BuildVariantNameTable> { static constexpr auto value() { return concat(variant_name_block(), BuildVariantNameTable>::value()); } }; template struct BuildConceptNames; template <> struct BuildConceptNames> { static constexpr std::array value() { return {}; } }; template struct BuildConceptNames> { static constexpr auto value() { return concat(std::array{{Head::entryName()}}, BuildConceptNames>::value()); } }; template struct BuildConceptOffsetsTable; template <> struct BuildConceptOffsetsTable> { static constexpr std::array value() { return {{0}}; } }; template struct BuildConceptOffsetsTable> { static constexpr auto value() { constexpr auto tail = BuildConceptOffsetsTable>::value(); std::array result{}; result[0] = 0; for (std::size_t i = 0; i < tail.size(); ++i) { result[i + 1] = tail[i] + Head::variant_count; } return result; } }; } // namespace detail // ================================================================================================= // 8) EntryVariantRegistry // - Same public API names as your original VariantRegistry // - ConceptId is the derived concept number (IndexOf::value) // ================================================================================================= /// /// @brief Compile-time registry providing concept/variant indexing and metadata. /// /// `EntryVariantRegistry` exposes a **stable public API** for: /// /// - computing global and local variant indices /// - retrieving concept identifiers /// - mapping enums and strings to indices /// - accessing constexpr metadata tables /// /// @tparam EntriesListT TypeList of Entry descriptors /// /// This type is: /// - stateless /// - constexpr-friendly /// - safe to use in headers /// template struct EntryVariantRegistry { using Concepts = EntriesListT; /// /// @name Registry dimensions and sentinels /// @{ /// /// These constants define: /// - sentinel values for error handling /// - fixed structural dimensions of the encoding pipeline /// static constexpr std::size_t missing = MISSING; static constexpr std::size_t NSections = NUM_SECTIONS; static constexpr std::size_t NStages = NUM_STAGES; static constexpr std::size_t NConcepts = Concepts::size; static constexpr std::size_t NVariants = detail::TotalVariantCount::value; /// @} // ---- STAGE 1 ---- static constexpr std::size_t numberOfVariants() { return NVariants; } template static constexpr std::size_t offset() { return detail::EntryOffset::value, Concepts>::value; } template static constexpr std::size_t offset_from_enum_type() { using Concept = typename detail::EntryFromVariantEnum::type; return offset(); } template static constexpr std::size_t offset(Enum) { return offset_from_enum_type(); } template static constexpr std::size_t conceptId(Enum) { using Concept = typename detail::EntryFromVariantEnum::type; return detail::IndexOf::value; } template static constexpr std::size_t localIndex(Enum v) { using Concept = typename detail::EntryFromVariantEnum::type; constexpr auto list = typename Concept::VariantList{}; const std::size_t idx = detail::value_index(v, list); return (idx == static_cast(-1)) ? missing : idx; } template static constexpr std::size_t globalIndex(Enum v) { using Concept = typename detail::EntryFromVariantEnum::type; const std::size_t li = localIndex(v); return (li == missing) ? missing : (offset() + li); } // ---- STAGE 2 ---- /// /// @brief Precomputed constexpr lookup tables. /// /// These arrays are indexed by **global variant index** and provide /// O(1) access to: /// /// - concept identifiers (variantId -> conceptId) /// - local variant indices (variantId -> localIndex) /// - concept names (variantId -> conceptName) /// - variant names (variantId -> variantName) /// - concept name lookup (conceptId -> conceptName) /// /// They are safe to expose as `inline constexpr` objects. /// static constexpr auto conceptIdTable() { return detail::BuildConceptIdTableWithBase::value(); } static constexpr auto variantIdTable() { return detail::BuildVariantIdTable::value(); } static constexpr auto conceptNameTable() { return detail::BuildConceptNameTable::value(); } static constexpr auto variantNameTable() { return detail::BuildVariantNameTable::value(); } static constexpr auto conceptNamesTable() { return detail::BuildConceptNames::value(); } /// /// @brief Concept offset table (CSR-style). /// /// This array has size `NConcepts + 1` and defines half-open /// global variant index ranges for each concept: /// /// \code /// concept i → [ offsets[i], offsets[i+1] ) /// \endcode /// /// The last entry equals `NVariants`. /// static constexpr auto conceptOffsetsTable() { return detail::BuildConceptOffsetsTable::value(); } static inline constexpr auto conceptIdArr = conceptIdTable(); static inline constexpr auto variantIdArr = variantIdTable(); static inline constexpr auto conceptNameArr = conceptNameTable(); static inline constexpr auto variantNameArr = variantNameTable(); static inline constexpr auto conceptNames = conceptNamesTable(); static inline constexpr auto conceptOffsets = conceptOffsetsTable(); private: template static constexpr std::array make_id_array_from_concept_impl( std::index_sequence) { // VariantList order => contiguous [offset .. offset+variant_count) constexpr std::size_t off = offset(); return {{(static_cast(Is), off + Is)...}}; } public: template static constexpr std::array make_id_array_from_concept() { return make_id_array_from_concept_impl(std::make_index_sequence{}); } template static constexpr std::array make_id_array_from_variants() { using Enum = typename Concept::Variant; static_assert((std::is_same_v && ...), "All variant values must belong to Concept::Variant"); return {{globalIndex(static_cast(Vs))...}}; } // ---- STAGE 3 ---- /// /// @brief Runtime utilities for string-based lookup. /// /// These functions provide a bridge between dynamic inputs (strings) /// and the compile-time registry. /// /// They are intentionally linear-time and intended for: /// - diagnostics /// - configuration parsing /// - debugging paths /// /// They must not be used in hot paths. /// static std::size_t conceptId(const std::string& name) { for (std::size_t i = 0; i < NConcepts; ++i) { if (conceptNames[i] == name) { return i; } } return missing; } static std::size_t localIndex(const std::string& conceptName, const std::string& variantName) { const std::size_t cid = conceptId(conceptName); if (cid == missing) return missing; const std::size_t begin = offset(cid); const std::size_t end = (cid + 1 < NConcepts) ? offset(cid + 1) : NVariants; for (std::size_t i = begin; i < end; ++i) { if (variantNameArr[i] == variantName) { return variantIdArr[i]; // local index } } return missing; } static std::size_t globalIndex(const std::string& conceptName, const std::string& variantName) { const std::size_t cid = conceptId(conceptName); if (cid == missing) return missing; const std::size_t begin = offset(cid); const std::size_t end = (cid + 1 < NConcepts) ? offset(cid + 1) : NVariants; for (std::size_t i = begin; i < end; ++i) { if (variantNameArr[i] == variantName) { return i; } } return missing; } }; } // namespace metkit::mars2grib::backend::compile_time_registry_engine ././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootmetkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/makeVariantCallbacksRegistry.hmetkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/makeVariantCallbacksRegistry0000664000175000017500000002672415232567717034075 0ustar alastairalastair/* * (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 VariantCallbacksRegistry.h /// @brief Compile-time global registry for variant-level callbacks. /// /// This header defines the machinery required to **materialize, entirely at /// compile time, a dense dispatch table of variant-level callbacks**. /// /// ----------------------------------------------------------------------------- /// Conceptual result /// ----------------------------------------------------------------------------- /// /// The primary product of this header is a constexpr array with the following /// logical structure: /// /// @code /// variantCallbacks[globalVariantIndex] -> Fn | nullptr /// @endcode /// /// where: /// /// - `globalVariantIndex` is a **flattened index** spanning *all variants of all /// entries*, in a single contiguous index space. /// - `Fn` is a function pointer type implementing a concrete encoding operation. /// - `nullptr` denotes that the requested capability is **not supported** for /// that specific variant. /// /// ----------------------------------------------------------------------------- /// Definition of the global variant index space /// ----------------------------------------------------------------------------- /// /// The global variant index space is defined *structurally* and *deterministically* /// by two independent compile-time orderings: /// /// 1. **Entry ordering** /// The order of Entry descriptors in `EntriesList` (a `TypeList`). /// /// 2. **Variant ordering** /// For each Entry, the order of values in `Entry::VariantList` /// (a `ValueList`). /// /// The resulting index space is: /// /// @code /// [ Entry0::Variant0, /// Entry0::Variant1, /// ... /// Entry1::Variant0, /// Entry1::Variant1, /// ... /// ] /// @endcode /// /// This ordering is guaranteed to be: /// - contiguous /// - stable across translation units /// - independent of compilation order /// /// ----------------------------------------------------------------------------- /// Capability selection model /// ----------------------------------------------------------------------------- /// /// Each variant may expose zero or more *capabilities*. /// /// A capability is identified by a **compile-time `std::size_t` constant**. /// Capability selection is resolved entirely at compile time, producing a /// specialized dispatch table for each capability. /// /// ----------------------------------------------------------------------------- /// Design goals /// ----------------------------------------------------------------------------- /// /// - **Zero runtime branching** /// No conditionals are required when invoking variant callbacks. /// /// - **No dynamic allocation** /// All data structures are `constexpr std::array`. /// /// - **Header-only** /// Safe to include in any number of translation units. /// /// - **Dense layout** /// One slot per variant, indexed directly by global variant ID. /// /// ----------------------------------------------------------------------------- /// Assumptions and invariants /// ----------------------------------------------------------------------------- /// /// Each Entry type appearing in `EntriesList` is assumed to provide: /// /// @code /// template < /// std::size_t Capability, /// auto Variant, /// class MarsDict_t, /// class ParDict_t, /// class OptDict_t, /// class OutDict_t /// > /// static constexpr Fn /// variantCallbacks(); /// @endcode /// /// returning either: /// - a valid function pointer implementing the capability for that variant, or /// - `nullptr` if the capability is not supported. /// /// Failure to meet this contract results in a compile-time error. /// /// ----------------------------------------------------------------------------- /// Relationship to other registries /// ----------------------------------------------------------------------------- /// /// This registry operates at **variant granularity**. /// /// It is orthogonal to: /// - entry-level callback registries /// - concept/variant indexing registries /// /// Those registries define *index spaces*; this registry defines *behavior*. /// /// @ingroup mars2grib_backend_compile_time_registry_engine /// #pragma once // System includes #include #include #include // Project includes #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" namespace metkit::mars2grib::backend::compile_time_registry_engine { /// /// @namespace detail /// @brief Internal implementation details of the variant callbacks registry. /// /// This namespace contains the low-level template machinery required to: /// - instantiate per-variant callbacks /// - concatenate per-entry variant blocks /// - produce the final flattened dispatch table /// /// All entities in this namespace are **implementation details** and must not be /// used directly outside this header. /// namespace detail { /// /// @brief Alias for a variant-level callback function pointer. /// /// This alias represents the *callable unit* stored in the variant callbacks /// registry. /// /// The function signature is determined by the concrete dictionary types. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output dictionary /// template using VariantCallback = Fn; /// /// @brief Build the variant-level callback block for a single Entry. /// /// This function materializes a contiguous array of callbacks corresponding /// to **all variants of a single Entry**, in the order defined by /// `Entry::VariantList`. /// /// Each element of the returned array corresponds to exactly one variant. /// /// @tparam Entry Entry descriptor type /// @tparam Capability Compile-time capability identifier /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter dictionary type /// @tparam OptDict_t Options dictionary type /// @tparam OutDict_t Output dictionary type /// @tparam Variants Pack of variant enum values /// /// @param[in] ValueList /// Compile-time list defining variant order /// /// @return /// A `constexpr std::array` of size `sizeof...(Variants)` containing /// the callbacks for each variant. /// /// @note /// Each callback may be `nullptr` if the capability is not supported /// for the corresponding variant. /// template constexpr std::array, sizeof...(Variants)> makeEntryVariantCallbacks(ValueList) { return {{Entry::template variantCallbacks()...}}; } /// /// @brief Convenience wrapper to build the variant callback block for an Entry. /// /// This overload extracts `Entry::VariantList` automatically and forwards /// to the ValueList-based implementation. /// template constexpr auto makeEntryVariantCallbacks() { return makeEntryVariantCallbacks( typename Entry::VariantList{}); } /// /// @brief Compile-time builder for the full variant callbacks table. /// /// This metafunction recursively traverses the `EntriesList` typelist and /// concatenates the per-entry variant callback blocks into a single, /// flattened dispatch table. /// /// The resulting table: /// - has one slot per global variant /// - preserves Entry ordering /// - preserves VariantList ordering within each Entry /// /// @tparam EntriesList TypeList of Entry descriptors /// @tparam Capability Compile-time capability identifier /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter dictionary type /// @tparam OptDict_t Options dictionary type /// @tparam OutDict_t Output dictionary type /// template struct BuildVariantCallbacks; /// /// @brief Base case for an empty EntriesList. /// /// Produces an empty constexpr array. /// /// This specialization terminates the recursion. /// template struct BuildVariantCallbacks, Capability, MarsDict_t, ParDict_t, OptDict_t, OutDict_t> { static constexpr std::array, 0> value() { return {}; } }; /// /// @brief Recursive case: prepend Head’s variant block and recurse on Tail. /// /// This specialization: /// - builds the variant callback block for `Head` /// - recursively builds the table for `Tail...` /// - concatenates the two blocks using `concat` /// /// The order of the resulting array is strictly deterministic. /// template struct BuildVariantCallbacks, Capability, MarsDict_t, ParDict_t, OptDict_t, OutDict_t> { static constexpr auto value() { constexpr auto head = makeEntryVariantCallbacks(); constexpr auto tail = BuildVariantCallbacks, Capability, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>::value(); return concat(head, tail); } }; } // namespace detail /// /// @brief Construct the compile-time variant callbacks registry. /// /// This function is the **public API** of the variant callbacks registry. /// /// It materializes, at compile time, a dense array of variant-level callbacks /// corresponding to: /// - a fixed EntriesList /// - a fixed capability /// - concrete dictionary types /// /// @tparam EntriesList TypeList of Entry descriptors /// @tparam Capability Compile-time capability identifier /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter dictionary type /// @tparam OptDict_t Options dictionary type /// @tparam OutDict_t Output dictionary type /// /// @return /// A `constexpr std::array, N>` where: /// - `N` equals the total number of variants across all Entries /// - index `i` corresponds to global variant index `i` /// /// @note /// The returned array is intended to be: /// - stored as a `static constexpr` object /// - indexed directly in hot paths /// - never modified /// template constexpr auto makeVariantCallbacksRegistry() { return detail::BuildVariantCallbacks::value(); } } // namespace metkit::mars2grib::backend::compile_time_registry_engine metkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/compileTimeRegistryEngine.md0000664000175000017500000001653515232567717034026 0ustar alastairalastair# Compile-Time Registry Engine ## Overview The **compile-time registry engine** is the backbone of the mars2grib dispatch and encoding infrastructure. Its purpose is to transform *static knowledge about concepts, variants, stages, sections, and capabilities* into **fully materialized, constexpr dispatch tables** that can be used directly at runtime with: * no dynamic allocation, * no virtual dispatch, * no string-based lookup in hot paths, * no runtime branching. In short: > **All decisions are made at compile time. Runtime code only indexes arrays and calls function pointers.** This document explains the engine **from first principles**, starting with vocabulary and gradually building up to the full phase-level dispatch table. --- ## Design Goals The engine is designed to satisfy the following constraints: 1. **Zero runtime overhead** * All tables are `constexpr` and fully materialized at compile time. * Runtime code performs only indexed lookups and function calls. 2. **Deterministic structure** * All indices are stable across translation units. * Table layouts depend only on type lists and value lists. 3. **Header-only** * Safe to include in any number of translation units. * No ODR issues, no global state. 4. **Strict layering** * Each registry layer has a single responsibility. * Higher layers build on lower ones without circular dependencies. 5. **Explicit contracts** * Concept authors must implement a precise compile-time interface. * Violations fail at compile time. --- ## Core Vocabulary All registries share a common vocabulary defined in `callbacksRegistry_common.h`. ### Pipeline Dimensions The encoding pipeline is defined along two fixed axes: * **Stages** (`NUM_STAGES`) * **Sections** (`NUM_SECTIONS`) These values are compile-time constants and are used to dimension all dispatch tables. Stages represent *when* an operation occurs (e.g. allocation, preset, runtime), while sections represent *where* in the GRIB message the operation applies. ### Sentinel Values One sentinel constant is used throughout the engine: * `MISSING` It carries different semantic meaning depending on context. They are used to represent: * absent indices, * invalid lookups, * structurally inapplicable situations. ### Function Pointer Types Two canonical function pointer types are used: * `Fn<...>` — encoding / transformation callbacks * `Fm<...>` — matcher / predicate callbacks All dispatch tables store one of these two types (or `nullptr`). ### Compile-Time Containers Two lightweight compile-time containers are used everywhere: * `ValueList` — a list of compile-time values (usually enum values) * `TypeList` — a list of types They provide *ordering*, *cardinality*, and *structure*, but no runtime behavior. --- ## The Entry Descriptor Contract Every concept that participates in the registry engine must provide a **descriptor** conforming to `RegisterEntryDescriptor`. Conceptually, an *Entry* represents: * one semantic concept (e.g. level type, grid type), * a finite set of variants, * a family of dispatch functions at different granularities. An Entry descriptor specifies: 1. **Variant enum type** 2. **Ordered list of variants** (`VariantList`) 3. **Concept name** (`entryName()`) 4. **Variant names** (`variantName()`) 5. **Dispatch interfaces** The dispatch interfaces are layered: * entry-level (`entryCallbacks`) * variant-level (`variantCallbacks`) * phase-level (`phaseCallbacks`) Each interface is selected by: * a compile-time capability ID, * optional variant, stage, and section indices, * concrete dictionary types. Returning `nullptr` means *not supported*. --- ## Registry Layers (Bottom-Up) The compile-time registry engine is composed of several layers. Each layer builds on the previous one. --- ### 1. EntryVariantRegistry **Purpose:** define the *index space*. This registry computes: * the total number of concepts, * the total number of variants, * the mapping between: * concept → offset * variant → local index * variant → global index It also materializes constexpr tables: * `conceptIdArr[globalVariant]` * `variantIdArr[globalVariant]` * `conceptNameArr[globalVariant]` * `variantNameArr[globalVariant]` This layer contains **no behavior**. It is purely structural and arithmetic. Think of it as the *schema* of the system. --- ### 2. EntryCallbacksRegistry **Purpose:** entry-level behavior. This registry builds a table: ``` entryCallbacks[entryIndex] -> Fm | nullptr ``` Each entry contributes exactly one slot. This layer answers questions like: * “Does this concept support capability X?” * “Which matcher applies for this dictionary pair?” Granularity: **per concept**. --- ### 3. VariantCallbacksRegistry **Purpose:** variant-level behavior. This registry refines dispatch to the variant level: ``` variantCallbacks[globalVariant] -> Fn | nullptr ``` Each variant contributes exactly one slot. Granularity: **per variant**. This layer is typically used when: * behavior depends on the chosen variant, * but not on stage or section. --- ### 4. PhaseCallbacksRegistry (Final Layer) **Purpose:** full phase resolution. This registry builds the final, fully expanded dispatch table: ``` phaseCallbacks[globalVariant][stage][section] -> Fn | nullptr ``` This table answers the question: > “For this variant, at this stage, in this section — what do I do?” All dimensions are fixed at compile time. This is the **final product** consumed by the encoding pipeline. --- ## Capability Model A *capability* is represented by a compile-time `std::size_t` constant. Capabilities are not enums by design: * they are used as non-type template parameters, * they index families of dispatch tables, * they allow separate compilation of unrelated behaviors. Each capability produces a **distinct instantiation** of every registry layer. There is no runtime capability switching. --- ## How Everything Fits Together 1. Concepts define Entry descriptors. 2. EntryVariantRegistry defines the index space. 3. EntryCallbacksRegistry defines per-concept behavior. 4. VariantCallbacksRegistry defines per-variant behavior. 5. PhaseCallbacksRegistry defines full phase behavior. 6. Runtime code: * computes indices once, * indexes into constexpr tables, * calls function pointers. No maps. No conditionals. No polymorphism. --- ## Mental Model You can think of the engine as a **compile-time code generator** written in C++ templates. At runtime, the system behaves as if someone had manually written: * giant `switch` statements, * fully unrolled loops, * precomputed dispatch matrices. Except: * it is guaranteed correct by the type system, * it is impossible to forget a case silently, * and the compiler optimizes it aggressively. --- ## When to Use (and Not Use) This Engine Use it when: * the domain is finite and known at compile time, * performance matters, * behavior is highly structured. Do **not** use it when: * the domain is dynamic or user-extensible at runtime, * configurability matters more than performance, * compile times are a critical concern. --- ## Final Notes This engine trades: * longer compile times, * heavier template instantiations, for: * minimal runtime overhead, * maximal structural correctness, * explicit, auditable behavior. It is intentionally **not clever at runtime**. All the cleverness happens at compile time. metkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/RegisterEntryDescriptor.h0000664000175000017500000002240315232567717033363 0ustar alastairalastair/* * (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 callbacksRegistry_common.h /// @brief Common compile-time vocabulary for concept dispatch registries. /// /// This header defines the **shared compile-time interface and vocabulary** /// used by all dispatch registries in the mars2grib backend. /// /// ----------------------------------------------------------------------------- /// Scope and responsibility /// ----------------------------------------------------------------------------- /// /// This file is intentionally **minimal and declarative**. /// /// It provides: /// - a canonical *Entry descriptor interface*, /// - shared naming and typing conventions, /// - compile-time constants derived from structural metadata. /// /// It explicitly does **not**: /// - build dispatch tables, /// - perform compile-time recursion, /// - define registry logic, /// - contain any metaprogramming algorithms. /// /// Those responsibilities are delegated to higher-level registry headers /// (EntryVariantRegistry, EntryCallbacksRegistry, VariantCallbacksRegistry, /// PhaseCallbacksRegistry). /// /// ----------------------------------------------------------------------------- /// Architectural role /// ----------------------------------------------------------------------------- /// /// Conceptually, this header defines the **contract** that every concept /// participating in the encoding pipeline must satisfy. /// /// All registry engines assume that Entry descriptors conform exactly to /// the interface specified here. /// /// Any deviation from this contract results in: /// - compilation failure, or /// - silent misalignment of registry tables (undefined behavior). /// /// ----------------------------------------------------------------------------- /// Design constraints /// ----------------------------------------------------------------------------- /// /// - Header-only /// - No state /// - No runtime logic /// - No ownership /// - No dynamic allocation /// /// Everything defined here must be: /// - usable in constant expressions, /// - safe to include in any translation unit, /// - independent of include order (modulo dependent headers). /// /// @ingroup mars2grib_backend_concepts /// #pragma once // System includes #include #include #include #include // Project includes #include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::compile_time_registry_engine { /// /// @brief Descriptor interface for a single semantic concept entry. /// /// `RegisterEntryDescriptor` defines the **compile-time interface contract** /// that every concept entry must implement in order to participate in: /// /// - concept/variant indexing, /// - entry-level dispatch, /// - variant-level dispatch, /// - phase-level (stage × section) dispatch. /// /// This struct is **never instantiated**. /// It is used purely as a *compile-time interface specification*. /// /// ----------------------------------------------------------------------------- /// Template parameters /// ----------------------------------------------------------------------------- /// /// @tparam VariantEnum /// Enum type enumerating all variants of the concept. /// /// @tparam VariantListT /// A `ValueList<...>` containing all values of `VariantEnum` /// that represent valid variants. /// /// The order of values in `VariantListT` is **semantically significant** and /// defines: /// - local variant indices, /// - ordering in flattened variant tables. /// /// ----------------------------------------------------------------------------- /// Semantic meaning /// ----------------------------------------------------------------------------- /// /// Each specialization of `RegisterEntryDescriptor` represents exactly: /// /// - one *concept* (e.g. levelType, timeRange, gridType), /// - a finite, ordered set of *variants*, /// - a family of dispatch points parameterized by: /// - capability, /// - variant, /// - stage, /// - section, /// - dictionary types. /// /// ----------------------------------------------------------------------------- /// Required invariants /// ----------------------------------------------------------------------------- /// /// Implementations must guarantee: /// /// - `VariantEnum` is an enum type. /// - `VariantListT` lists *only* values of `VariantEnum`. /// - Every value in `VariantListT` is unique. /// - `VariantListT::size` accurately reflects the number of variants. /// /// Violating these invariants results in undefined behavior in registry engines. /// template struct RegisterEntryDescriptor { /// /// @brief Enum type representing the variants of this concept. /// using Variant = VariantEnum; /// /// @brief Compile-time list of all valid variant values. /// /// This list defines the *local ordering* of variants within the concept. /// using VariantList = VariantListT; /// /// @brief Return the canonical name of the concept. /// /// This name is used for: /// - diagnostics, /// - debugging output, /// - string-based lookup paths. /// /// @return /// A stable, null-terminated string view identifying the concept. /// /// @note /// The returned string must have static storage duration. /// static constexpr std::string_view entryName(); /// /// @brief Return the canonical name of a variant. /// /// This function maps a compile-time variant value to its /// human-readable identifier. /// /// @tparam V /// A value of `VariantEnum` identifying the variant. /// /// @return /// A stable, null-terminated string view identifying the variant. /// /// @note /// This function is used exclusively in constexpr contexts and /// in diagnostic utilities. /// template static constexpr std::string_view variantName(); /// /// @brief Number of variants supported by this concept. /// /// This value is derived directly from `VariantList`. /// /// @note /// This constant is consumed by registry engines to: /// - allocate compile-time tables, /// - compute offsets in flattened index spaces. /// static constexpr std::size_t variant_count = VariantList::size; /// /// @brief Phase-level dispatch interface. /// /// This function provides the most granular level of dispatch: /// it selects a callback based on: /// - capability, /// - stage, /// - section, /// - variant, /// - concrete dictionary types. /// /// @tparam Capability /// Compile-time capability identifier. /// /// @tparam Stage /// Encoding stage index. /// /// @tparam Section /// GRIB section index. /// /// @tparam Variant /// Variant value of this concept. /// /// @tparam MarsDict_t /// MARS dictionary type. /// /// @tparam ParDict_t /// Parameter dictionary type. /// /// @tparam OptDict_t /// Options dictionary type. /// /// @tparam OutDict_t /// Output dictionary type. /// /// @return /// A function pointer implementing the requested phase-level behavior, /// or `nullptr` if the combination is not supported. /// template static constexpr Fn phaseCallbacks(); /// /// @brief Variant-level dispatch interface. /// /// This function selects a callback based on: /// - capability, /// - variant, /// - concrete dictionary types. /// /// It is less granular than `phaseCallbacks` and is typically used /// in earlier or coarser dispatch layers. /// /// @return /// A function pointer implementing the variant-level behavior, /// or `nullptr` if unsupported. /// template static constexpr Fn variantCallbacks(); /// /// @brief Entry-level dispatch interface. /// /// This function selects a callback based only on: /// - capability, /// - dictionary types. /// /// It represents the coarsest dispatch granularity and is typically /// used for: /// - dictionary matching, /// - capability probing, /// - preflight checks. /// /// @return /// A function pointer implementing the entry-level behavior, /// or `nullptr` if unsupported. /// template static constexpr Fm entryCallbacks(); }; } // namespace metkit::mars2grib::backend::compile_time_registry_engine metkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/utils.h0000664000175000017500000001266115232567717027663 0ustar alastairalastair/* * (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 utils.h /// @brief Compile-time concatenation utilities for fixed-size arrays. /// /// This header provides a minimal, constexpr-capable utility to **concatenate /// two `std::array` objects into a single `std::array`**, entirely at compile time. /// /// ----------------------------------------------------------------------------- /// Scope and responsibility /// ----------------------------------------------------------------------------- /// /// This file is intentionally narrow in scope. It provides: /// /// - a constexpr implementation of array concatenation, /// - suitable for use in template metaprogramming contexts, /// - with no dynamic allocation and no runtime overhead. /// /// It does **not**: /// - perform bounds checking, /// - provide runtime utilities, /// - introduce any registry-specific semantics. /// /// ----------------------------------------------------------------------------- /// Architectural role /// ----------------------------------------------------------------------------- /// /// This utility is a foundational building block used by higher-level /// compile-time registry engines to: /// /// - assemble flattened lookup tables, /// - concatenate per-entry or per-variant blocks, /// - materialize large constexpr dispatch tables. /// /// It lives in the `detail` namespace because it is considered an /// **implementation primitive**, not part of the public API. /// /// ----------------------------------------------------------------------------- /// Design constraints /// ----------------------------------------------------------------------------- /// /// - Fully `constexpr` (usable in constant expressions) /// - Header-only /// - Allocation-free /// - Type- and size-safe /// - Compatible with C++17 /// /// ----------------------------------------------------------------------------- /// Complexity guarantees /// ----------------------------------------------------------------------------- /// /// - Compile-time complexity: O(N1 + N2) /// - Runtime complexity: zero (fully evaluated at compile time) /// /// ----------------------------------------------------------------------------- /// Safety and invariants /// ----------------------------------------------------------------------------- /// /// - Both input arrays must have the same element type `T`. /// - The resulting array has size `N1 + N2`. /// - Element order is strictly preserved: /// - all elements of `a` precede all elements of `b`. /// #pragma once // system includes #include #include #include #include // Project includes #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::compile_time_registry_engine::detail { /// /// @brief Implementation helper for compile-time array concatenation. /// /// This function performs the actual concatenation by expanding two /// `std::index_sequence`s corresponding to the indices of the input arrays. /// /// It is intentionally separated from the public interface to: /// - avoid exposing index sequence details, /// - allow clean forwarding from the simpler `concat` wrapper. /// /// @tparam T Element type of the arrays /// @tparam N1 Size of the first array /// @tparam N2 Size of the second array /// @tparam I1 Index pack for the first array /// @tparam I2 Index pack for the second array /// /// @param[in] a First input array /// @param[in] b Second input array /// @param[in] Index sequence for array `a` /// @param[in] Index sequence for array `b` /// /// @return /// A `std::array` containing: /// `{ a[0], a[1], ..., a[N1-1], b[0], b[1], ..., b[N2-1] }` /// /// @note /// This function is intended to be invoked only from `concat`. /// template constexpr std::array concat_impl(const std::array& a, const std::array& b, std::index_sequence, std::index_sequence) { return {{a[I1]..., b[I2]...}}; } /// /// @brief Concatenate two `std::array` objects at compile time. /// /// This function provides a simple, user-facing interface for array /// concatenation, hiding all index-sequence machinery. /// /// @tparam T Element type of the arrays /// @tparam N1 Size of the first array /// @tparam N2 Size of the second array /// /// @param[in] a First input array /// @param[in] b Second input array /// /// @return /// A `std::array` containing the elements of `a` /// followed by the elements of `b`. /// /// @note /// This function is `constexpr` and may be used in: /// - compile-time table construction, /// - static data member initialization, /// - other constant-expression contexts. /// template constexpr std::array concat(const std::array& a, const std::array& b) { return concat_impl(a, b, std::make_index_sequence{}, std::make_index_sequence{}); } } // namespace metkit::mars2grib::backend::compile_time_registry_engine::detailmetkit-1.19.2/src/metkit/mars2grib/backend/compile-time-registry-engine/makePhaseCallbacksRegistry.h0000664000175000017500000003043015232567717033744 0ustar alastairalastair /* * (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 PhaseCallbacksRegistry.h /// @brief Compile-time global registry for phase-level callbacks. /// /// This header defines the machinery required to **materialize, entirely at /// compile time, a three-dimensional dispatch table of phase-level callbacks**. /// /// ----------------------------------------------------------------------------- /// Conceptual result /// ----------------------------------------------------------------------------- /// /// The primary product of this header is a constexpr data structure with the /// following logical shape: /// /// @code /// phaseCallbacks[globalVariant][stage][section] -> Fn | nullptr /// @endcode /// /// where: /// /// - `globalVariant` is a flattened index over *all variants of all entries* /// (as defined by the compile-time registry engine). /// - `stage` is an encoding stage index in the range `[0, NUM_STAGES)`. /// - `section` is a GRIB section index in the range `[0, NUM_SECTIONS)`. /// - `Fn` is a function pointer implementing a concrete encoding action. /// - `nullptr` denotes that the requested capability is **not implemented** /// for the given `(variant, stage, section)` triple. /// /// ----------------------------------------------------------------------------- /// Structural definition of the index space /// ----------------------------------------------------------------------------- /// /// The three axes of the dispatch table are defined as follows: /// /// 1. **Global variant axis** /// Determined by: /// - the order of Entry descriptors in `EntriesList` (TypeList order), /// - the order of variant values in each `Entry::VariantList` (ValueList order). /// /// 2. **Stage axis** /// A fixed compile-time dimension of size `NUM_STAGES`, representing /// the sequential encoding phases of the GRIB header construction pipeline. /// /// 3. **Section axis** /// A fixed compile-time dimension of size `NUM_SECTIONS`, representing /// GRIB message sections. /// /// The resulting layout is: /// /// @code /// [ /// Variant0 -> [ Stage0 -> [Sec0, Sec1, ...], /// Stage1 -> [Sec0, Sec1, ...], /// ... ], /// Variant1 -> [ ... ], /// ... /// ] /// @endcode /// /// All dimensions are: /// - contiguous, /// - fixed at compile time, /// - deterministic, /// - identical across all translation units. /// /// ----------------------------------------------------------------------------- /// Capability selection model /// ----------------------------------------------------------------------------- /// /// Phase-level behavior is parameterized by a **compile-time capability /// identifier**, represented as a `std::size_t` template parameter. /// /// Each capability produces a *distinct* registry instantiation. /// Capability selection therefore incurs: /// - zero runtime cost, /// - zero branching, /// - zero dynamic dispatch. /// /// ----------------------------------------------------------------------------- /// Design goals /// ----------------------------------------------------------------------------- /// /// - **Zero runtime overhead** /// All decisions are resolved at compile time. /// /// - **Dense, indexable layout** /// Direct indexing without indirection or lookup structures. /// /// - **Strict structural alignment** /// Indices are guaranteed to match those produced by the variant and /// entry registries. /// /// - **Header-only, constexpr-only** /// Safe for inclusion in any translation unit. /// /// ----------------------------------------------------------------------------- /// Assumptions and invariants /// ----------------------------------------------------------------------------- /// /// Each Entry type appearing in `EntriesList` is assumed to provide: /// /// @code /// template < /// std::size_t Capability, /// auto Stage, /// auto Section, /// auto Variant, /// class MarsDict_t, /// class ParDict_t, /// class OptDict_t, /// class OutDict_t /// > /// static constexpr Fn /// phaseCallbacks(); /// @endcode /// /// returning either: /// - a valid function pointer implementing the phase-level operation, or /// - `nullptr` if the operation is not defined. /// /// Any deviation from this contract results in a compile-time error. /// /// ----------------------------------------------------------------------------- /// Relationship to other registries /// ----------------------------------------------------------------------------- /// /// This registry represents the **final and most granular dispatch layer**. /// /// It composes and refines: /// - EntryVariantRegistry (index space definition), /// - EntryCallbacksRegistry (entry-level behavior), /// - VariantCallbacksRegistry (variant-level behavior). /// /// This layer adds: /// - stage awareness, /// - section awareness, /// - full phase resolution. /// #pragma once // System includes #include #include #include // Project includes #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" namespace metkit::mars2grib::backend::compile_time_registry_engine { /// /// @namespace detail /// @brief Internal implementation details of the phase callbacks registry. /// /// This namespace contains low-level template machinery used to: /// - build per-section rows, /// - assemble per-stage planes, /// - concatenate all variant blocks across all entries. /// /// All entities in this namespace are **implementation details** and must not be /// referenced directly by user code. /// namespace detail { /// /// @brief One row of phase callbacks for a fixed stage. /// /// A PhaseRow represents all section-level callbacks for a single /// `(variant, stage)` pair. /// /// Layout: /// @code /// PhaseRow[section] -> Fn | nullptr /// @endcode /// template using PhaseRow = std::array, NUM_SECTIONS>; /// /// @brief One plane of phase callbacks for a fixed variant. /// /// A PhasePlane aggregates all stages and sections for a single variant. /// /// Layout: /// @code /// PhasePlane[stage][section] -> Fn | nullptr /// @endcode /// template using PhasePlane = std::array, NUM_STAGES>; /// /// @brief Build a single phase row for a fixed Entry, Variant, and Stage. /// /// This function instantiates all section-level callbacks corresponding to: /// - one Entry, /// - one Variant, /// - one Stage, /// across all sections. /// /// The resulting PhaseRow has exactly `NUM_SECTIONS` elements. /// template constexpr PhaseRow makePhaseRow(std::index_sequence) { return {{Entry::template phaseCallbacks()...}}; } /// /// @brief Build a full phase plane for a single variant. /// /// This function builds: /// - one PhaseRow per stage, /// - for all stages `[0, NUM_STAGES)`, /// producing a complete PhasePlane. /// template constexpr PhasePlane makePhasePlane(std::index_sequence) { return {{// For every stage in Stages..., build a full row of sections makePhaseRow( std::make_index_sequence{})...}}; } /// /// @brief Build phase planes for all variants of a single Entry. /// /// This function expands over `Entry::VariantList` and produces /// one PhasePlane per variant. /// /// The order of planes exactly matches the VariantList order. /// template constexpr std::array, sizeof...(Variants)> makeEntryPhaseCallbacks(ValueList) { return {{makePhasePlane( std::make_index_sequence{})...}}; } template constexpr auto makeEntryPhaseCallbacks() { return makeEntryPhaseCallbacks( typename Entry::VariantList{}); } /// /// @brief Compile-time builder for the full phase callbacks registry. /// /// This metafunction recursively traverses the EntriesList typelist and /// concatenates the per-entry variant phase blocks into a single, /// flattened registry indexed by global variant ID. /// /// The resulting array has: /// - one PhasePlane per global variant, /// - total size equal to the total number of variants across all entries. /// template struct BuildPhaseCallbacks; /// /// @brief Base case for an empty EntriesList. /// /// Terminates recursion and produces an empty registry. /// template struct BuildPhaseCallbacks, Capability, MarsDict_t, ParDict_t, OptDict_t, OutDict_t> { static constexpr std::array, 0> value() { return {}; } }; /// /// @brief Recursive case: prepend Head’s phase blocks and recurse on Tail. /// /// This preserves: /// - Entry ordering, /// - Variant ordering, /// - Stage ordering, /// - Section ordering. /// template struct BuildPhaseCallbacks, Capability, MarsDict_t, ParDict_t, OptDict_t, OutDict_t> { static constexpr auto value() { constexpr auto head = makeEntryPhaseCallbacks(); constexpr auto tail = BuildPhaseCallbacks, Capability, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>::value(); return concat(head, tail); } }; } // namespace detail /// /// @brief Construct the compile-time phase callbacks registry. /// /// This function is the **public API** of the phase-level registry. /// /// It materializes, at compile time, a fully expanded three-dimensional /// dispatch table indexed by: /// - global variant, /// - stage, /// - section. /// /// @tparam EntriesList TypeList of Entry descriptors /// @tparam Capability Compile-time capability identifier /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter dictionary type /// @tparam OptDict_t Options dictionary type /// @tparam OutDict_t Output dictionary type /// /// @return /// A `constexpr std::array, N>` where: /// - `N` is the total number of global variants, /// - element `i` corresponds to global variant index `i`. /// /// @note /// The returned object is intended to be: /// - stored as a `static constexpr` variable, /// - indexed directly in hot paths, /// - never modified. /// template constexpr auto makePhaseCallbacksRegistry() { return detail::BuildPhaseCallbacks::value(); } } // namespace metkit::mars2grib::backend::compile_time_registry_engine metkit-1.19.2/src/metkit/mars2grib/backend/sections/0000775000175000017500000000000015232567717022476 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/0000775000175000017500000000000015232567717025204 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/sectionInitializer2.h0000664000175000017500000001200215232567717031302 0ustar alastairalastair/* * (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 section2Initializer.h /// @brief Initializer for GRIB Section 2 (Local Use Section). /// /// This header defines a **section initializer** for GRIB **Section 2**, /// responsible for allocating and populating the *Local Use Section* /// based on the resolved template number. /// /// Section 2 is used to encode local or centre-specific extensions that /// are not part of the official GRIB specification. In the mars2grib /// backend, this initializer supports both: /// /// - standard local definition numbers /// - *virtual* template numbers used internally by the encoder /// /// In particular, this initializer contains special handling for /// DestinE-related virtual template numbers that are mapped onto /// valid local definition sections with additional metadata. /// /// This file performs controlled mutation of the output GRIB dictionary /// and includes structured exception handling to ensure consistent /// error propagation across section initialization boundaries. /// /// @ingroup mars2grib_backend_sections /// #pragma once #include "metkit/mars2grib/backend/sections/initializers/sectionInitializerCore.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::initializers { /// /// @brief Initializer for GRIB Section 2 (Local Use Section). /// /// This function allocates and configures GRIB Section 2 by: /// - enabling the local definition section /// - selecting the appropriate local definition number /// - optionally injecting additional metadata for special template numbers /// /// Two *virtual* template numbers are handled explicitly: /// - `1001`: DestinE / ClimateDT local definition /// - `1002`: DestinE / ExtremesDT local definition /// /// These template numbers are **not part of the official ecCodes tables**. /// They are used internally by the mars2grib encoder to emulate the /// behavior of standard templates while injecting additional semantics /// through Section 2. /// /// All dictionary mutations are performed via `set_or_throw` to ensure /// strict error checking. /// /// @tparam SectionNumber GRIB section number (expected to be 2) /// @tparam TemplateNumber GRIB template number or virtual template identifier /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// /// @param mars Read-only MARS dictionary /// @param par Read-only parameter dictionary /// @param opt Read-only options dictionary /// @param out Output GRIB dictionary to be populated /// /// @throws Mars2GribGenericException /// If any dictionary operation fails while preparing Section 2. /// /// @note /// Existing local definition content is not checked and may be overwritten. /// template void allocateTemplateNumber2(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { // Dictionary traits using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { // Enable local definition section (overwrite if already present) set_or_throw(out, "setLocalDefinition", 1); // Select local definition number based on template number // Special handling for DestinE virtual templates if constexpr (TemplateNumber == 1001) { // Minimal Section 2 set_or_throw(out, "localDefinitionNumber", 1L); // DestinE metadata set_or_throw(out, "productionStatusOfProcessedData", 12L); // ClimateDT dataset tag set_or_throw(out, "dataset", "climate-dt"); } else if constexpr (TemplateNumber == 1002) { // Minimal Section 2 set_or_throw(out, "localDefinitionNumber", 1L); // DestinE metadata set_or_throw(out, "productionStatusOfProcessedData", 12L); // ExtremesDT dataset tag set_or_throw(out, "dataset", "extremes-dt"); } else { set_or_throw(out, "localDefinitionNumber", TemplateNumber); } return; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Error preparing section 2 with template number", Here())); } // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::sections::initializers metkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/sectionInitializer3.h0000664000175000017500000001261715232567717031317 0ustar alastairalastair/* * (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 section3Initializer.h /// @brief Initializer for GRIB Section 3 (Grid Definition Section). /// /// This header defines a **section initializer** for GRIB **Section 3**, /// responsible for configuring the *Grid Definition Section* according to /// the selected grid definition template. /// /// The initializer supports: /// - standard grid definition templates, mapped directly from the template number /// - special-case handling for selected templates that require explicit /// preconditioning of the GRIB handle prior to encoding /// /// In particular, Template **50** (spectral representation) requires /// explicit initialization of several keys (values, truncation parameters, /// spectral mode, etc.) to satisfy ecCodes constraints and enable correct /// conversion to spherical harmonics. /// /// All dictionary mutations are performed via checked dictionary traits, /// and failures are wrapped in a mars2grib-specific exception with /// preserved exception nesting. /// /// @ingroup mars2grib_backend_sections /// #pragma once // System includes #include #include "metkit/mars2grib/backend/sections/initializers/sectionInitializerCore.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::sections::initializers { /// /// @brief Initializer for GRIB Section 3 (Grid Definition Section). /// /// This function prepares Section 3 by selecting and configuring the /// appropriate *Grid Definition Template*. /// /// Behaviour depends on the template number: /// - **Template 50** (spectral grid): requires explicit initialization of /// grid size, spectral truncation parameters, representation mode, and /// placeholder values, following ecCodes recommendations /// - **All other templates**: the grid definition template number is set /// directly with no additional preprocessing /// /// @tparam SectionNumber GRIB section number (expected to be 3) /// @tparam TemplateNumber Grid definition template number /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// /// @param mars Read-only MARS dictionary /// @param par Read-only parameter dictionary /// @param opt Read-only options dictionary /// @param out Output GRIB dictionary to be populated /// /// @throws Mars2GribGenericException /// If any dictionary operation fails while preparing Section 3. /// /// @note /// The special handling for Template 50 follows ecCodes guidance for /// spectral GRIB messages: /// https://confluence.ecmwf.int/display/ECC/ecCodes+developer+FAQ+-+GRIB#ecCodesdeveloperFAQGRIB-GRIB:HowcanIconvertthesampleGRIB2.tmpltosphericalharmonics? /// template void allocateTemplateNumber3(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { // Dictionary traits using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { // Special handling for spectral grids (Template 50) if constexpr (TemplateNumber == 50) { // Precondition GRIB handle for spectral representation set_or_throw(out, "numberOfDataPoints", 6L); set_or_throw(out, "numberOfValues", 6L); set_or_throw(out, "bitsPerValue", 16L); set_or_throw(out, "typeOfFirstFixedSurface", 105L); set_or_throw>(out, "values", std::vector{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}); set_or_throw(out, "scaleFactorOfFirstFixedSurface", 0L); set_or_throw(out, "scaledValueOfFirstFixedSurface", 0L); set_or_throw(out, "gridDefinitionTemplateNumber", 50L); set_or_throw(out, "J", 1L); set_or_throw(out, "K", 1L); set_or_throw(out, "M", 1L); set_or_throw(out, "spectralType", 1L); set_or_throw(out, "spectralMode", 1L); set_or_throw(out, "numberOfOctectsForNumberOfPoints", 0L); set_or_throw(out, "interpretationOfNumberOfPoints", 0L); set_or_throw(out, "dataRepresentationTemplateNumber", 51L); } if constexpr (TemplateNumber != 50 && TemplateNumber != 1000) { // Standard grid definition template long drt = static_cast(TemplateNumber); set_or_throw(out, "gridDefinitionTemplateNumber", drt); } return; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Error preparing section 3 with template number", Here())); } // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::sections::initializers metkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/sectionInitializerCore.h0000664000175000017500000000700115232567717032034 0ustar alastairalastair/* * (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 sectionInitializerTypes.h /// @brief Core type definitions for GRIB section initializer registries. /// /// This header defines the **fundamental type aliases** used by the /// mars2grib backend to describe section initializer registries. /// /// Section initializers are lightweight, stateless functions responsible for /// populating GRIB sections based on the resolved dictionaries produced by /// the frontend and concept layers. /// /// This file provides: /// - a canonical function pointer type (`Fn`) for section initializers /// - a registry entry type (`Entry`) pairing a template number with an initializer /// /// These types are used to build compile-time or static registries mapping /// GRIB template numbers to the corresponding initialization routines. /// /// This file contains **no logic**, **no state**, and **no runtime behavior**. /// It is purely a type-definition header. /// /// @ingroup mars2grib_backend_sections /// #pragma once // System includes #include #include #include "metkit/mars2grib/backend/concepts/EncodingCallbacksRegistry.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::initializers { /// /// @brief Function pointer type for GRIB section initializers. /// /// This alias defines the canonical signature for all section initializer /// functions used by the mars2grib backend. /// /// A section initializer: /// - consumes read-only frontend dictionaries (`Mars`, `Param`, `Options`) /// - mutates the output GRIB dictionary corresponding to a specific section /// /// Initializers are invoked by the encoder once the appropriate GRIB /// template has been selected. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// template using Fn = metkit::mars2grib::backend::compile_time_registry_engine::Fn; /// /// @brief Registry entry associating a GRIB template number with an initializer. /// /// An `Entry` represents a single row in a section initializer registry. /// It binds: /// - a GRIB template number (e.g. Product Definition Template, Grid Definition Template) /// - the corresponding section initializer function /// /// Registries composed of these entries are used to: /// - select the correct initializer based on the resolved template number /// - dispatch section initialization at runtime with zero dynamic allocation /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// template struct Entry { using Fn_t = Fn; std::size_t templateNumber; Fn_t callback; }; } // namespace metkit::mars2grib::backend::sections::initializersmetkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/sectionInitializer1.h0000664000175000017500000000600115232567717031303 0ustar alastairalastair/* * (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 section1Initializer.h /// @brief Placeholder initializer for GRIB Section 1. /// /// This header defines a **section initializer** associated with /// GRIB **Section 1** and **Template Number 1**. /// /// Section 1 in GRIB messages contains identification and origin /// metadata and, in the current mars2grib architecture, does not /// require any concept-driven or dictionary-based initialization. /// /// The initializer provided here is therefore a **no-op**, introduced /// to preserve uniformity in the section-initializer registry and /// dispatch infrastructure. /// /// This file contains **no state**, **no encoding logic**, and performs /// **no mutation** of the output dictionary. /// /// @note /// This initializer is expected to be removed or replaced once /// Section 1 handling is either specialized or excluded from the /// generic initialization pipeline. /// /// @ingroup mars2grib_backend_sections /// #pragma once #include "metkit/mars2grib/backend/sections/initializers/sectionInitializerCore.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::initializers { /// /// @brief No-op initializer for GRIB Section 1, Template 1. /// /// This function conforms to the standard section initializer /// signature but intentionally performs no operations. /// /// It allows Section 1 to participate in the same compile-time /// registry and dispatch mechanisms as other GRIB sections, /// despite requiring no initialization logic. /// /// @tparam SectionNumber GRIB section number (expected to be 1) /// @tparam TemplateNumber GRIB template number (expected to be 1) /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// /// @param mars Read-only MARS dictionary (unused) /// @param par Read-only parameter dictionary (unused) /// @param opt Read-only options dictionary (unused) /// @param out Output GRIB dictionary (unused) /// /// @note /// This function currently exists as a placeholder and may be /// removed or specialized once Section 1 initialization is /// handled explicitly. /// template void allocateTemplateNumber1(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { // No-op for Section 1 — placeholder initializer } } // namespace metkit::mars2grib::backend::sections::initializers metkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/sectionInitializer0.h0000664000175000017500000000572215232567717031313 0ustar alastairalastair/* * (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 section0Initializer.h /// @brief Placeholder initializer for GRIB Section 0. /// /// This header defines a **section initializer** associated with /// GRIB **Section 0** and **Template Number 0**. /// /// Section 0 in GRIB messages contains only metadata related to the /// GRIB edition and message structure and does not require any /// concept-driven encoding or dictionary-based initialization. /// /// As such, the initializer provided here is intentionally a **no-op**. /// It exists solely to satisfy the uniform section-initializer /// infrastructure used by the mars2grib backend. /// /// This file contains **no state**, **no logic**, and performs /// **no mutation** of the output dictionary. /// /// @note /// This initializer is temporary and marked for removal once /// Section 0 handling is fully excluded from the generic /// section initialization pipeline. /// /// @ingroup mars2grib_backend_sections /// #pragma once #include "metkit/mars2grib/backend/sections/initializers/sectionInitializerCore.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::initializers { /// /// @brief No-op initializer for GRIB Section 0, Template 0. /// /// This function conforms to the standard section initializer /// signature but intentionally performs no operations. /// /// It is provided to allow Section 0 to participate in the same /// compile-time registry and dispatch mechanisms as other GRIB /// sections, even though no initialization is required. /// /// @tparam SectionNumber GRIB section number (expected to be 0) /// @tparam TemplateNumber GRIB template number (expected to be 0) /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// /// @param mars Read-only MARS dictionary (unused) /// @param par Read-only parameter dictionary (unused) /// @param opt Read-only options dictionary (unused) /// @param out Output GRIB dictionary (unused) /// /// @note /// This function currently exists as a placeholder and is expected /// to be removed. /// template void allocateTemplateNumber0(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { // No-op for Section 0 — placeholder initializer } } // namespace metkit::mars2grib::backend::sections::initializers metkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/sectionInitializer4.h0000664000175000017500000000646615232567717031325 0ustar alastairalastair/* * (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 section4Initializer.h /// @brief Initializer for GRIB Section 4 (Product Definition Section). /// /// This header defines a **section initializer** for GRIB **Section 4**, /// responsible for selecting and setting the *Product Definition Template* /// (PDT) number. /// /// Section 4 describes the scientific meaning of the data (forecast, /// analysis, ensemble, statistics, etc.). In the mars2grib backend, /// the template number is resolved by the concept layer and passed /// unchanged to the GRIB output dictionary by this initializer. /// /// This initializer performs a single, well-defined mutation of the /// output dictionary and relies on strict error checking and structured /// exception propagation. /// /// @ingroup mars2grib_backend_sections /// #pragma once #include "metkit/mars2grib/backend/sections/initializers/sectionInitializerCore.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::sections::initializers { /// /// @brief Initializer for GRIB Section 4 (Product Definition Section). /// /// This function configures Section 4 by setting the /// **Product Definition Template Number** corresponding to the /// resolved product type. /// /// No additional preprocessing is performed at this stage; all /// concept-specific logic affecting Section 4 is handled elsewhere /// in the encoding pipeline. /// /// @tparam SectionNumber GRIB section number (expected to be 4) /// @tparam TemplateNumber Product Definition Template number /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// /// @param mars Read-only MARS dictionary /// @param par Read-only parameter dictionary /// @param opt Read-only options dictionary /// @param out Output GRIB dictionary to be populated /// /// @throws Mars2GribGenericException /// If setting the product definition template number fails. /// template void allocateTemplateNumber4(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { // Dictionary traits using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { long pdt = static_cast(TemplateNumber); set_or_throw(out, "productDefinitionTemplateNumber", pdt); return; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Error preparing section 4 with template number", Here())); } // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::sections::initializers metkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/sectionRegistry.h0000664000175000017500000002700015232567717030551 0ustar alastairalastair/* * (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 sectionInitializerRegistry.h /// @brief Static registries and dispatch logic for GRIB section initializers. /// /// This header defines the **section initializer registry layer** of the /// mars2grib backend. /// /// It provides: /// - compile-time registries mapping *(section, template number)* pairs /// to concrete section initializer functions /// - a generic lookup utility for registry tables /// - a unified dispatch function used by the encoder to resolve /// the correct initializer at runtime /// /// Each GRIB section exposes a sorted, constexpr registry associating /// template numbers with their corresponding initializer functions. /// These registries are intentionally static, allocation-free, and /// trivially inspectable. /// /// This file contains **no encoding logic** itself; it only orchestrates /// the selection of the appropriate initializer. /// /// @ingroup mars2grib_backend_sections /// #pragma once // System includes #include #include "metkit/mars2grib/backend/sections/initializers/sectionInitializer0.h" #include "metkit/mars2grib/backend/sections/initializers/sectionInitializer1.h" #include "metkit/mars2grib/backend/sections/initializers/sectionInitializer2.h" #include "metkit/mars2grib/backend/sections/initializers/sectionInitializer3.h" #include "metkit/mars2grib/backend/sections/initializers/sectionInitializer4.h" #include "metkit/mars2grib/backend/sections/initializers/sectionInitializer5.h" #include "metkit/mars2grib/backend/sections/initializers/sectionInitializerCore.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::sections::initializers { /// /// @brief Registry for GRIB Section 0 initializers. /// /// Section 0 does not require initialization logic; this registry /// contains a single placeholder entry. /// template inline constexpr Entry Sec0Reg[] = { {0, &allocateTemplateNumber0<0, 0, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}}; /// /// @brief Registry for GRIB Section 1 initializers. /// template inline constexpr Entry Sec1Reg[] = { {0, &allocateTemplateNumber1<1, 0, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}}; /// /// @brief Registry for GRIB Section 2 (Local Use Section) initializers. /// /// Includes both official and *virtual* template numbers used internally /// by the encoder (e.g. DestinE extensions). /// template inline constexpr Entry Sec2Reg[] = { {1, &allocateTemplateNumber2<2, 1, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {15, &allocateTemplateNumber2<2, 15, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {20, &allocateTemplateNumber2<2, 20, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {24, &allocateTemplateNumber2<2, 24, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {25, &allocateTemplateNumber2<2, 25, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {36, &allocateTemplateNumber2<2, 36, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {37, &allocateTemplateNumber2<2, 37, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {38, &allocateTemplateNumber2<2, 38, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {39, &allocateTemplateNumber2<2, 39, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {1000, &allocateTemplateNumber2<2, 1000, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {1001, &allocateTemplateNumber2<2, 1001, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {1002, &allocateTemplateNumber2<2, 1002, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {1004, &allocateTemplateNumber2<2, 1004, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}}; /// /// @brief Registry for GRIB Section 3 (Grid Definition Section) initializers. /// template inline constexpr Entry Sec3Reg[] = { {0, &allocateTemplateNumber3<3, 0, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {40, &allocateTemplateNumber3<3, 40, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {50, &allocateTemplateNumber3<3, 50, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {101, &allocateTemplateNumber3<3, 101, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {150, &allocateTemplateNumber3<3, 150, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {1000, &allocateTemplateNumber3<3, 1000, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}}; /// /// @brief Registry for GRIB Section 4 (Product Definition Section) initializers. /// template inline constexpr Entry Sec4Reg[] = { {0, &allocateTemplateNumber4<4, 0, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {1, &allocateTemplateNumber4<4, 1, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {2, &allocateTemplateNumber4<4, 2, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {8, &allocateTemplateNumber4<4, 8, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {11, &allocateTemplateNumber4<4, 11, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {12, &allocateTemplateNumber4<4, 12, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {32, &allocateTemplateNumber4<4, 32, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {33, &allocateTemplateNumber4<4, 33, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {40, &allocateTemplateNumber4<4, 40, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {41, &allocateTemplateNumber4<4, 41, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {42, &allocateTemplateNumber4<4, 42, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {43, &allocateTemplateNumber4<4, 43, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {50, &allocateTemplateNumber4<4, 50, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {45, &allocateTemplateNumber4<4, 45, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {46, &allocateTemplateNumber4<4, 46, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {85, &allocateTemplateNumber4<4, 85, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {48, &allocateTemplateNumber4<4, 48, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {49, &allocateTemplateNumber4<4, 49, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {60, &allocateTemplateNumber4<4, 60, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {61, &allocateTemplateNumber4<4, 61, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {76, &allocateTemplateNumber4<4, 76, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {77, &allocateTemplateNumber4<4, 77, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {78, &allocateTemplateNumber4<4, 78, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {79, &allocateTemplateNumber4<4, 79, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {99, &allocateTemplateNumber4<4, 99, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {100, &allocateTemplateNumber4<4, 100, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {103, &allocateTemplateNumber4<4, 103, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {104, &allocateTemplateNumber4<4, 104, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {107, &allocateTemplateNumber4<4, 107, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {121, &allocateTemplateNumber4<4, 121, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {122, &allocateTemplateNumber4<4, 122, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {142, &allocateTemplateNumber4<4, 142, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {143, &allocateTemplateNumber4<4, 143, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}}; /// /// @brief Registry for GRIB Section 5 (Data Representation Section) initializers. /// template inline constexpr Entry Sec5Reg[] = { {0, &allocateTemplateNumber5<5, 0, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {42, &allocateTemplateNumber5<5, 42, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}, {51, &allocateTemplateNumber5<5, 51, MarsDict_t, ParDict_t, OptDict_t, OutDict_t>}}; /// /// @brief Lookup a section initializer function by template number. /// /// Performs a linear search over a compile-time registry table and /// returns the corresponding initializer function pointer. /// /// @tparam EntryT Registry entry type /// @tparam N Registry size /// /// @param table Registry table /// @param templ Template number /// /// @return Initializer function pointer, or `nullptr` if not found. /// template constexpr auto lookup(const EntryT (&table)[N], std::size_t templ) -> decltype(table[0].callback) { for (std::size_t i = 0; i < N; ++i) { if (table[i].templateNumber == templ) return table[i].callback; } return nullptr; } /// /// @brief Resolve a section initializer function. /// /// Dispatches to the appropriate section registry based on the /// GRIB section number and resolves the initializer corresponding /// to the provided template number. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// /// @param section GRIB section number /// @param templ Template number /// /// @return Initializer function pointer, or `nullptr` if not found. /// /// @throws Mars2GribGenericException /// If an error occurs during dispatch. /// template Fn sectionRegistry(std::size_t section, std::size_t templ) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { switch (section) { case 0: return lookup(Sec0Reg, templ); case 1: return lookup(Sec1Reg, templ); case 2: return lookup(Sec2Reg, templ); case 3: return lookup(Sec3Reg, templ); case 4: return lookup(Sec4Reg, templ); case 5: return lookup(Sec5Reg, templ); default: return nullptr; } } catch (...) { std::throw_with_nested(Mars2GribGenericException("Error getting section initializer function for section " + std::to_string(section) + " template " + std::to_string(templ), Here())); } } } // namespace metkit::mars2grib::backend::sections::initializers metkit-1.19.2/src/metkit/mars2grib/backend/sections/initializers/sectionInitializer5.h0000664000175000017500000000656215232567717031323 0ustar alastairalastair/* * (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 section5Initializer.h /// @brief Initializer for GRIB Section 5 (Data Representation Section). /// /// This header defines a **section initializer** for GRIB **Section 5**, /// responsible for selecting the *Data Representation Template* (DRT) /// used to encode the data values. /// /// Section 5 controls how the field values are packed (e.g. simple packing, /// complex packing, JPEG, PNG, spectral, etc.). In the mars2grib backend, /// the concrete template number is resolved by the concept layer and passed /// directly to the GRIB output dictionary by this initializer. /// /// This initializer performs a single, well-defined mutation of the output /// dictionary and relies on strict error checking and structured exception /// propagation. /// /// @ingroup mars2grib_backend_sections /// #pragma once #include "metkit/mars2grib/backend/sections/initializers/sectionInitializerCore.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::sections::initializers { /// /// @brief Initializer for GRIB Section 5 (Data Representation Section). /// /// This function configures Section 5 by setting the /// **Data Representation Template Number** corresponding to the /// selected packing or representation method. /// /// No additional preprocessing is performed at this stage; all /// concept-specific logic affecting Section 5 is handled elsewhere /// in the encoding pipeline. /// /// @tparam SectionNumber GRIB section number (expected to be 5) /// @tparam TemplateNumber Data Representation Template number /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary /// @tparam OutDict_t Type of the output GRIB dictionary /// /// @param mars Read-only MARS dictionary /// @param par Read-only parameter dictionary /// @param opt Read-only options dictionary /// @param out Output GRIB dictionary to be populated /// /// @throws Mars2GribGenericException /// If setting the data representation template number fails. /// template void allocateTemplateNumber5(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { // Dictionary traits using metkit::mars2grib::utils::dict_traits::set_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { long pdt = static_cast(TemplateNumber); set_or_throw(out, "dataRepresentationTemplateNumber", pdt); return; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Error preparing section 5 with template number", Here())); } // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::sections::initializers metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/0000775000175000017500000000000015232567717024337 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/Recipe.h0000664000175000017500000002441515232567717025725 0ustar alastairalastair/* * (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 Recipe.h /// @brief Runtime representation of a fully expanded section recipe. /// /// This header defines `Recipe`, an **immutable runtime object** produced from /// the compile-time section-recipe DSL. /// /// A `Recipe` encapsulates: /// - A GRIB **template number** /// - A multidimensional selection space derived from `Select<>` grammar nodes /// - The total number of **valid variant combinations** /// /// Conceptually, a recipe represents the *Cartesian product* of a sequence of /// concept-variant selectors. Each point in this space corresponds to a /// concrete encoding configuration and can be materialized on demand as a /// `ResolvedTemplateData` instance. /// /// The class is designed for: /// - Fast lookup /// - Predictable iteration /// - Deterministic decoding via mixed-radix arithmetic /// /// Instances are immutable after construction and are intended to be stored /// and traversed in hot paths during encoding plan construction. /// /// Debug and introspection facilities are provided for diagnostics only and /// are not part of the performance-critical API. /// /// @ingroup mars2grib_backend_section_recipes /// #pragma once // System includes #include #include #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/sections/resolver/ResolvedTemplateData.h" #include "metkit/mars2grib/backend/sections/resolver/Select.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::resolver::dsl { /// /// @brief Runtime, immutable container defining a GRIB section template number. /// /// A `Recipe` represents the **runtime realization of a GRIB section template /// definition**. /// /// In the GRIB model, a *template number* is not defined by a single choice, /// but by an **ordered set of concepts** contributing to the same section. /// Each concept may participate in the definition of the template using /// **different variants**, and different combinations of variants may map /// to the same template number. /// /// As a consequence, the process of defining a template number is /// **inherently combinatorial**. /// /// The role of a `Recipe` is to: /// - Bind a specific GRIB template number /// - Describe the complete space of valid concept-variant combinations /// that realize that template /// /// The valid variants for each concept are expressed at compile time using /// the `Select<>` DSL object, which specifies: /// - Which concept participates /// - Which variants of that concept are admissible for the template /// /// At runtime, the recipe materializes this information as a multidimensional /// selection space, where: /// - Each dimension corresponds to one concept /// - Each dimension contains the list of allowed global variant identifiers /// /// Individual encoding configurations are obtained by enumerating this space /// using mixed-radix decoding, preserving the original concept order. /// /// The class is intentionally opaque and immutable: /// - No mutation after construction /// - No exposure of internal storage /// - Construction only via the `make_recipe` factory /// /// This design ensures deterministic behavior, efficient lookup, and /// suitability for hot-path execution during encoding plan construction. /// class Recipe { public: /// /// @brief Return the total number of valid variant combinations. /// std::size_t numberOfCombinations() const noexcept { return nCombinations_; } /// /// @brief Materialize a resolved recipe entry. /// /// This function decodes the given linear index into a concrete /// combination of concept variants and returns it as a /// `ResolvedTemplateData` payload. /// /// Mixed-radix decoding is used, preserving the original selector order. /// The rightmost selector varies fastest. /// /// @param[in] i Linear combination index /// /// @return Fully populated resolved template payload /// /// @throws std::out_of_range /// If @p i is greater than or equal to the number of combinations /// ResolvedTemplateData getEntry(std::size_t i) const { if (i >= nCombinations_) { throw std::out_of_range("Recipe::getEntry index out of range"); } ResolvedTemplateData entry; entry.templateNumber = templateNumber_; entry.count = variants_.size(); std::size_t remainder = i; // Mixed-radix decoding: // selector order preserved // rightmost selector varies fastest for (std::size_t d = variants_.size(); d-- > 0;) { const std::size_t radix = sizes_[d]; const std::size_t idx = remainder % radix; remainder /= radix; entry.variantIndices[d] = variants_[d][idx]; } return entry; } /// /// @brief Print a human-readable description of the recipe. /// /// @param[in] prefix Line prefix used for indentation /// @param[out] os Output stream /// void debug_print(const std::string& prefix, std::ostream& os) const { using metkit::mars2grib::backend::concepts_::GeneralRegistry; os << prefix << " :: Recipe\n"; os << prefix << " :: templateNumber : " << templateNumber_ << std::endl; os << prefix << " :: dimensions : " << variants_.size() << std::endl; os << prefix << " :: nCombinations : " << nCombinations_ << std::endl; for (std::size_t d = 0; d < variants_.size(); ++d) { os << prefix << " :: dimension[" << d << "]" << std::endl; os << prefix << " :: radix : " << sizes_[d] << std::endl; os << prefix << " :: variants glbId : [ "; const auto& dim = variants_[d]; for (std::size_t i = 0; i < dim.size(); ++i) { os << dim[i]; if (i + 1 < dim.size()) os << ", "; } os << " ] " << std::endl; os << prefix << " :: variants names : [ "; for (std::size_t i = 0; i < dim.size(); ++i) { std::size_t id = dim[i]; std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); os << "\"" << cname << "::" << vname << "\""; if (i + 1 < dim.size()) os << ", "; } os << " ]" << std::endl; } } /// /// @brief Convert the recipe to a JSON-like string. /// /// Intended exclusively for diagnostics and debugging. /// /// @return JSON-style string representation /// std::string debug_to_json() const { using metkit::mars2grib::backend::concepts_::GeneralRegistry; std::ostringstream oss; oss << "{ \"Recipe\":{"; oss << "\"templateNumber\":" << templateNumber_ << ", "; oss << "\"dimensions\":" << variants_.size() << ", "; oss << "\"nCombinations\":" << nCombinations_ << ", "; oss << "\"selectors\":["; for (std::size_t d = 0; d < variants_.size(); ++d) { oss << "{\"dimension\":" << d << ", "; oss << "\"radix\":" << sizes_[d] << ", "; oss << "\"variantIndices\": ["; const auto& dim = variants_[d]; for (std::size_t i = 0; i < dim.size(); ++i) { oss << dim[i]; if (i + 1 < dim.size()) oss << ", "; } oss << "], " << std::endl; oss << "\"variantNames\": ["; for (std::size_t i = 0; i < dim.size(); ++i) { std::size_t id = dim[i]; std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); oss << "\"" << cname << "::" << vname << "\""; if (i + 1 < dim.size()) oss << ", "; } oss << " ]}" << std::endl; if (d + 1 < variants_.size()) oss << ", "; } oss << "]}}"; return oss.str(); } private: using Dimension = std::vector; using Dimensions = std::vector; using Sizes = std::vector; const std::size_t templateNumber_; const Dimensions variants_; const Sizes sizes_; const std::size_t nCombinations_; private: Recipe(std::size_t tpl, Dimensions&& variants, Sizes&& sizes, std::size_t nComb) : templateNumber_(tpl), variants_(std::move(variants)), sizes_(std::move(sizes)), nCombinations_(nComb) {} template friend Recipe make_recipe(); }; /// /// @brief Factory function converting DSL grammar to a runtime recipe. /// /// This function erases the compile-time `Select<>` grammar and produces /// a fully materialized `Recipe` object suitable for runtime use. /// /// @tparam TemplateNumber GRIB template number /// @tparam Selects Sequence of selector grammar nodes /// /// @return Immutable runtime recipe instance /// template inline Recipe make_recipe() { constexpr std::size_t N = sizeof...(Selects); Recipe::Dimensions variants; Recipe::Sizes sizes; variants.reserve(N); sizes.reserve(N); // Erase Select<> grammar here ( [&] { variants.emplace_back(Selects::ids.begin(), Selects::ids.end()); sizes.push_back(Selects::ids.size()); }(), ...); std::size_t nComb = 1; for (std::size_t s : sizes) { nComb *= s; } return Recipe{TemplateNumber, std::move(variants), std::move(sizes), nComb}; } } // namespace metkit::mars2grib::backend::sections::resolver::dsl metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/Select.h0000664000175000017500000002007315232567717025731 0ustar alastairalastair/* * (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 Select.h /// @brief Compile-time selector for concept variants in section recipe definitions. /// /// This header defines the `Select` template, a **compile-time DSL building block** /// used by section recipes to specify *which variants of a given concept* are /// applicable for a recipe entry. /// /// The selector supports two usage modes: /// - **Explicit selection** of a fixed set of concept variants /// - **Wildcard selection** (`any`), meaning *all variants* of the concept /// /// The selection is resolved entirely at compile time and materialized as a /// constant array of *global variant identifiers*, as defined by the /// `GeneralRegistry`. /// /// This mechanism allows section recipes to: /// - Remain declarative and concise /// - Avoid runtime branching or lookups /// - Integrate seamlessly with the compile-time recipe expansion pipeline /// /// @note /// This type is part of the *section recipe DSL* and is not intended to be used /// directly by runtime encoding logic. /// /// @ingroup mars2grib_backend_section_recipes /// #pragma once // System includes #include #include #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::sections::resolver::dsl { /// /// @file Select.h /// @brief Compile-time selector defining admissible variants of a concept. /// /// This header defines `Select`, a **compile-time DSL primitive** used to /// express *which variants of a given concept* are admissible when defining /// a GRIB section template. /// /// A `Select` always refers to **exactly one concept** and defines a /// **subset of its variants**. This subset represents the variants of the /// concept that are allowed to participate in the definition of a specific /// template number. /// /// The selector supports two explicit modes: /// /// - **Explicit selection** /// When one or more variant tags are provided, only those variants are /// considered valid for the concept. /// /// - **Implicit full selection (wildcard)** /// When no variants are provided, *all variants* of the concept are /// implicitly selected. /// /// In other words: /// - `Select` selects **only** variants `V1` and `V2` /// - `Select` selects **all** variants of `Concept` /// /// `Select` objects are the fundamental building blocks used by every /// `Recipe` to define the **rules governing template-number selection**. /// /// A recipe is defined by an ordered list of `Select` objects, one per /// participating concept. Together, these selectors describe the full /// combinatorial space of admissible concept-variant combinations that /// realize a given GRIB template number. /// /// The selection is resolved entirely at compile time and materialized /// as a constant list of global variant identifiers, ensuring: /// - Zero runtime overhead /// - Deterministic behavior /// - Early validation of recipe definitions /// /// @note /// `Select` does not perform any runtime logic. It is a declarative, /// compile-time construct whose sole responsibility is to describe /// admissible variant subsets for a concept. /// /// @see Recipe /// @ingroup mars2grib_backend_section_resolver /// template struct Select { /// Concept associated with this selector using concept_type = Concept; /// True if the selector matches all variants of the concept static constexpr bool is_any = (sizeof...(Vs) == 0); /// Number of selected variants static constexpr std::size_t count = is_any ? Concept::variant_count : sizeof...(Vs); private: /// /// @brief Generate the array of global variant identifiers. /// /// This helper resolves the selection rule to a concrete array of /// variant IDs using the `GeneralRegistry`. /// /// The resolution is performed entirely at compile time. /// /// @return `std::array` containing global variant IDs /// static constexpr auto generate_ids() { using metkit::mars2grib::backend::concepts_::GeneralRegistry; if constexpr (is_any) { return GeneralRegistry::make_id_array_from_concept(); } else { return GeneralRegistry::make_id_array_from_variants(); } // Remove compiler warning mars2gribUnreachable(); } public: /// Compile-time array of selected global variant identifiers inline static constexpr auto ids = generate_ids(); /// /// @brief Print a human-readable description of the selector. /// /// This function emits a structured textual representation of the /// selector configuration, including: /// - Concept type /// - Wildcard status /// - Number of selected variants /// - Variant indices /// - Fully-qualified variant names /// /// @param[in] prefix Prefix string prepended to each output line /// @param[in,out] os Output stream /// static void debug_print(const std::string& prefix, std::ostream& os) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; os << prefix << " :: Select<" << typeid(Concept).name() << ">" << std::endl; os << prefix << " :: is_any : " << is_any << std::endl; os << prefix << " :: count : " << count << std::endl; os << prefix << " :: variantIndices : [ "; for (std::size_t i = 0; i < ids.size(); ++i) { os << ids[i]; if (i + 1 < ids.size()) os << ", "; } os << " ]" << std::endl; os << prefix << " :: variantNames : [ "; for (std::size_t i = 0; i < ids.size(); ++i) { std::size_t id = ids[i]; std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); os << "\"" << cname << "::" << vname << "\""; if (i + 1 < ids.size()) os << ", "; } os << " ]" << std::endl; } /// /// @brief Serialize the selector state to a JSON-like string. /// /// This method produces a compact JSON-style representation intended /// solely for debugging and diagnostics. /// /// @return String representation of the selector state /// /// @note /// The returned string is not guaranteed to be valid strict JSON and /// must not be used for machine parsing. /// static std::string debug_to_json() { using metkit::mars2grib::backend::concepts_::GeneralRegistry; std::ostringstream oss; oss << "{ \"Select\":{ \"typeId\": " << typeid(Concept).name(); oss << ", \"is_any\": " << is_any; oss << ", \"count\":" << count; oss << ", \"variantIndices\": ["; for (std::size_t i = 0; i < ids.size(); ++i) { oss << ids[i]; if (i + 1 < ids.size()) oss << ", "; } oss << "]"; oss << ", \"variantIndices\": ["; for (std::size_t i = 0; i < ids.size(); ++i) { std::size_t id = ids[i]; std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); oss << "\"" << cname << "::" << vname << "\""; if (i + 1 < ids.size()) oss << ", "; } oss << "]}}"; return oss.str(); } }; } // namespace metkit::mars2grib::backend::sections::resolver::dslmetkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/resolver.md0000664000175000017500000001755315232567717026535 0ustar alastairalastair@page mars2grib_section_resolver Section Resolver Subsystem @tableofcontents @section resolver_overview Overview The **Section Resolver** subsystem is responsible for determining the concrete layout of a GRIB section given: - A set of *declarative template rules* (recipes) - A *runtime description of active concepts* Its purpose is to select, in a deterministic and efficient way, a `SectionLayoutData` that fully defines how a GRIB section must be encoded. Conceptually, the resolver answers the question: @verbatim “Given the active concepts for this request, which section template applies?” @endverbatim The resolver is a **pure backend subsystem**. It contains no frontend orchestration logic and no configuration policy. It operates solely on: - Immutable configuration data - Immutable runtime state and produces deterministic results. -------------------------------------------------------------------------- @section resolver_big_picture Big picture Section resolution is defined by the interaction of three orthogonal elements: - **Declarative rules** describing which combinations of concept variants are admissible for a template - **Runtime state** describing which concept variants are active - **Resolution algorithms** that match the runtime state against the admissible rule space These responsibilities are intentionally separated to ensure: - Clear ownership of concerns - Predictable behavior - Efficient hot-path execution - Long-term maintainability -------------------------------------------------------------------------- @section resolver_namespace_structure Namespace organization The resolver subsystem is organized into a hierarchy of namespaces, each with a clearly defined role. @subsection resolver_ns_root backend::sections::resolver This is the **public API** of the resolver subsystem. It exposes only the types required to *use* the resolver: - `ActiveConceptsData` Runtime description of which concept variants are active - `SectionLayoutData` Result of section resolution; fully describes how a section must be encoded - `SectionTemplateSelector` The main algorithmic entry point performing section resolution Frontend code must depend **only** on this namespace. -------------------------------------------------------------------------- @subsection resolver_ns_dsl backend::sections::resolver::dsl This namespace defines the **declarative grammar** used to describe section template rules. It contains no algorithms and performs no resolution. Types in this namespace are used to *describe*: - Which concepts participate in defining a template - Which variants of each concept are admissible The main types are: - `Select` Compile-time selector defining a subset (possibly all) of variants for a single concept - `Recipe` Runtime representation of a single GRIB template number together with the full combinatorial space of admissible concept-variant combinations - `Recipes` Section-scoped container aggregating all `Recipe` instances defining all template numbers valid for a section The DSL namespace is intentionally expressive and declarative. It defines *what is allowed*, not *what is selected*. -------------------------------------------------------------------------- @subsection resolver_ns_detail backend::sections::resolver::detail This namespace contains **internal infrastructure** used by the resolver algorithms. It includes: - Key representations and compression utilities - Lookup tables and indices - Intermediate data structures - Performance-oriented helpers Types in this namespace are: - Not part of the public API - Free to change - Optimized for internal use No frontend code should depend on this namespace. -------------------------------------------------------------------------- @subsection resolver_ns_debug backend::sections::resolver::debug This namespace contains **debugging and introspection utilities**. Typical contents include: - Human-readable printers - JSON-like serialization helpers - Diagnostic formatting utilities Debug code is intentionally isolated to ensure that: - Hot-path code remains minimal - Debug facilities do not pollute public APIs - Debug functionality can be gated or compiled out if needed -------------------------------------------------------------------------- @section resolver_dsl The recipe DSL @subsection resolver_select Select: defining admissible variants A `Select` object defines a **subset of variants of a single concept**. It always refers to exactly one concept and expresses which of its variants are admissible when defining a GRIB template. Two modes are supported: - **Explicit selection** When one or more variants are specified, only those variants are admissible. - **Implicit full selection (wildcard)** When no variants are specified, *all variants* of the concept are assumed to be admissible. `Select` is a compile-time construct and introduces no runtime overhead. -------------------------------------------------------------------------- @subsection resolver_recipe Recipe: defining a template number A `Recipe` represents the runtime realization of a **single GRIB template number**. A template number is defined by: - An ordered set of concepts - For each concept, a set of admissible variants Because each concept may participate with multiple variants, the process of defining a template number is **inherently combinatorial**. A `Recipe` captures this combinatorial space and provides a deterministic way to enumerate all admissible combinations. -------------------------------------------------------------------------- @subsection resolver_recipes Recipes: section-scoped template definitions A `Recipes` object represents the **complete set of template definitions** valid for a single GRIB section. Each `Recipes` instance: - Is defined for exactly one section - Aggregates multiple `Recipe` objects - Represents all template numbers admissible for that section It provides a uniform expansion mechanism producing `ResolvedTemplateData` payloads. -------------------------------------------------------------------------- @section resolver_runtime Active concept state `ActiveConceptsData` represents the **runtime state** of concept activation for a specific encoding request. It answers questions such as: - Which concepts are active? - Which variant of a concept is active? - Is a concept missing or unspecified? This runtime state is matched against the declarative recipe space during resolution. -------------------------------------------------------------------------- @section resolver_algorithm Resolution algorithm The resolution process performed by `SectionTemplateSelector` can be summarized as: 1. Build a signature from the active concept state 2. Match this signature against the admissible recipe space 3. Select the corresponding section layout The algorithm is: - Deterministic - Stateless - Optimized for hot-path execution -------------------------------------------------------------------------- @section resolver_dependency_rules Dependency rules The following dependency rules are enforced by design: - Frontend code depends only on `backend::sections::resolver` - Resolver algorithms may depend on `dsl`, `detail`, and `debug` - DSL types do not depend on resolver algorithms - Debug utilities do not affect hot-path logic These rules ensure a clean layering and prevent accidental coupling. -------------------------------------------------------------------------- @section resolver_summary Summary The section resolver subsystem provides a compiler-like architecture for GRIB section template selection: - Declarative grammar (`dsl`) - Runtime state (`ActiveConceptsData`) - Deterministic resolution algorithms This design ensures correctness, performance, and long-term maintainability. metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/TemplateSignatureKey.h0000664000175000017500000001621215232567717030620 0ustar alastairalastair/* * (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 TemplateSignatureKey.h /// @brief Compact key representing an active concept-variant signature. /// /// This header defines `TemplateSignatureKey`, an **internal, fixed-size key** /// used by the section resolver to represent the *active concept-variant /// signature* of an encoding request. /// /// A signature key is constructed from the runtime active concept state and /// encodes, in a compact and ordered form, the set of **global variant /// identifiers** that characterize the request. /// /// The key is used for: /// - Efficient comparison /// - Ordered lookup /// - Hash-based indexing /// /// It is designed for hot-path usage and introduces no dynamic allocation. /// /// @note /// This type is an internal implementation detail of the resolver and is not /// part of the public API. /// /// @ingroup mars2grib_backend_section_resolver /// #pragma once // System includes #include #include #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::resolver::detail { /// /// @brief Fixed-size signature key for concept-variant combinations. /// /// `TemplateSignatureKey` represents an **ordered sequence of global /// variant identifiers** describing the active concept state. /// /// The key is: /// - Dense and contiguous /// - Order-sensitive /// - Free of dynamic allocation /// /// The `size` field indicates how many entries in @ref data are valid. /// /// Ordering and equality are defined lexicographically and are consistent /// with the semantics of concept-variant matching. /// struct TemplateSignatureKey { /// Registry providing global concept and variant identifiers using GeneralRegistry = metkit::mars2grib::backend::concepts_::GeneralRegistry; /// /// @brief Maximum number of variant identifiers that can be stored. /// /// This corresponds to the total number of registered variants. /// static constexpr std::size_t maxSize = GeneralRegistry::NVariants; /// /// @brief Ordered list of global variant identifiers. /// /// Only the first @ref size entries are valid. /// std::array data{}; /// /// @brief Number of active entries in @ref data. /// std::uint16_t size{0}; /// /// @brief Equality comparison. /// /// Two keys are equal if they have the same size and identical /// variant identifiers in the same order. /// bool operator==(const TemplateSignatureKey& other) const noexcept { if (size != other.size) { return false; } for (std::size_t i = 0; i < size; ++i) { if (data[i] != other.data[i]) { return false; } } return true; } /// /// @brief Strict weak ordering. /// /// Lexicographical comparison on the stored variant identifiers, /// with shorter keys ordered before longer ones when prefixes match. /// bool operator<(const TemplateSignatureKey& other) const noexcept { const std::size_t n = std::min(size, other.size); for (std::size_t i = 0; i < n; ++i) { if (data[i] < other.data[i]) { return true; } if (data[i] > other.data[i]) { return false; } } return size < other.size; } }; /// /// @brief Hash functor for `TemplateSignatureKey`. /// /// This hash combines the variant identifiers using a standard /// hash-mixing scheme suitable for unordered containers. /// struct TemplateSignatureKeyHash { std::size_t operator()(const TemplateSignatureKey& key) const noexcept { std::size_t h = 0; for (std::size_t i = 0; i < key.size; ++i) { h ^= key.data[i] + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2); } return h; } }; /// /// @namespace metkit::mars2grib::backend::sections::resolver::detail::debug /// /// @brief Debug and introspection utilities for template signature keys. /// /// These utilities are intended exclusively for diagnostics and debugging. /// They must not be used in performance-critical code paths. /// namespace debug { /// /// @brief Print a human-readable representation of a template signature key. /// /// @param[in] key Signature key /// @param[in] prefix Line prefix used for indentation /// @param[out] os Output stream /// inline void debug_print_Key(const TemplateSignatureKey& key, std::string_view prefix, std::ostream& os) { using GeneralRegistry = TemplateSignatureKey::GeneralRegistry; TemplateSignatureKeyHash hasher; os << prefix << " :: TemplateSignatureKey\n"; os << prefix << " :: maxSize : " << key.maxSize << "\n"; os << prefix << " :: size : " << key.size << "\n"; os << prefix << " :: hash : " << hasher(key) << "\n"; os << prefix << " :: variantIndices : [ "; for (std::size_t i = 0; i < key.size; ++i) { os << key.data[i]; if (i + 1 < key.size) { os << ", "; } } os << " ]\n"; os << prefix << " :: variantNames : [ "; for (std::size_t i = 0; i < key.size; ++i) { const std::size_t id = key.data[i]; os << "\"" << GeneralRegistry::conceptNameArr[id] << "::" << GeneralRegistry::variantNameArr[id] << "\""; if (i + 1 < key.size) { os << ", "; } } os << " ]\n"; } /// /// @brief Convert a template signature key to a JSON-like string. /// /// Intended exclusively for debugging and diagnostics. /// /// @param[in] key Signature key /// /// @return JSON-style string representation /// inline std::string debug_convert_Key_to_json(const TemplateSignatureKey& key) { using GeneralRegistry = TemplateSignatureKey::GeneralRegistry; TemplateSignatureKeyHash hasher; std::ostringstream oss; oss << "{ \"TemplateSignatureKey\": { " << "\"maxSize\": " << key.maxSize << ", " << "\"size\": " << key.size << ", " << "\"hash\": " << hasher(key) << ", " << "\"variantIndices\": [ "; for (std::size_t i = 0; i < key.size; ++i) { oss << key.data[i]; if (i + 1 < key.size) { oss << ", "; } } oss << " ], \"variantNames\": [ "; for (std::size_t i = 0; i < key.size; ++i) { const std::size_t id = key.data[i]; oss << "\"" << GeneralRegistry::conceptNameArr[id] << "::" << GeneralRegistry::variantNameArr[id] << "\""; if (i + 1 < key.size) { oss << ", "; } } oss << " ] } }"; return oss.str(); } } // namespace debug } // namespace metkit::mars2grib::backend::sections::resolver::detail metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/dsl.h0000664000175000017500000000037415232567717025276 0ustar alastairalastair#pragma once #include "metkit/mars2grib/backend/sections/resolver/Recipe.h" #include "metkit/mars2grib/backend/sections/resolver/Recipes.h" #include "metkit/mars2grib/backend/sections/resolver/Select.h" #include "metkit/mars2grib/utils/generalUtils.h"metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/ActiveConceptsData.h0000664000175000017500000001770715232567717030230 0ustar alastairalastair/* * (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 ActiveConceptsData.h /// @brief Runtime representation of active concept variants inferred from a MARS dictionary. /// /// This header defines `ActiveConceptsData`, a **runtime data structure** /// representing the set of concepts that are *semantically active* for a /// given encoding request. /// /// An instance of this structure is **directly inferred from a MARS input /// dictionary** during the normalization and sanitization phases of the /// encoding pipeline. /// /// It captures, in a compact and lookup-friendly form: /// - Which concepts are required to semantically describe the MARS request /// - Which variant of each required concept must be used /// /// The structure is designed to be consumed by the section resolver /// subsystem as input state for template resolution. /// /// @ingroup mars2grib_backend_section_resolver /// #pragma once // System includeds #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::resolver { /// /// @brief Runtime container describing active concepts and their variants. /// /// `ActiveConceptsData` is a **pure data carrier** that represents the /// semantic interpretation of a MARS dictionary in terms of concepts and /// concept variants. /// /// The structure answers two fundamental questions for each registered concept: /// /// 1. **Is this concept required** to semantically describe the MARS request? /// 2. **If required, which variant** of the concept must be used? /// /// This information is produced by analyzing the MARS dictionary and is /// consumed by the section resolver to select appropriate section templates. /// /// ------------------------------------------------------------------------ /// /// @section activeconcepts_representation Internal representation /// /// The data is stored using two complementary arrays: /// /// - `activeVariantIndices` /// A dense array indexed by **concept identifier** /// /// - `activeConceptsIndices` /// A sparse list containing only the identifiers of **active concepts** /// /// The `count` field specifies the number of active concepts. /// /// The two arrays are used together as follows: /// /// @code /// for (std::size_t i = 0; i < count; ++i) { /// std::size_t conceptId = activeConceptsIndices[i]; /// std::size_t globalVariantId = activeVariantIndices[conceptId]; /// } /// @endcode /// /// ------------------------------------------------------------------------ /// /// @section activeconcepts_semantics Semantics of activeVariantIndices /// /// Each entry in `activeVariantIndices` encodes **both presence and choice**: /// /// - If a concept is **not required** to describe the MARS dictionary, /// its corresponding entry is set to a special sentinel value /// exposed by the `GeneralRegistry` (typically referred to as `Missing`) /// /// - If a concept **is required**, the entry contains the **global variant /// identifier** corresponding to the variant that must be used /// /// This design allows: /// - O(1) access by concept identifier /// - Explicit representation of inactive concepts /// - Efficient iteration over only active concepts /// /// ------------------------------------------------------------------------ /// /// @section activeconcepts_design Design considerations /// /// - No dynamic allocation /// - Fixed-capacity storage /// - Trivially copyable /// - Suitable for hot-path usage /// /// The structure performs **no validation** and enforces **no policy**. /// It is assumed to be fully consistent when handed to the resolver. /// struct ActiveConceptsData { /// Registry providing global concept and variant identifiers using GeneralRegistry = metkit::mars2grib::backend::concepts_::GeneralRegistry; /// /// @brief Maximum number of concepts that can be represented. /// static constexpr std::size_t maxCapacity = GeneralRegistry::NConcepts; /// /// @brief Mapping from concept identifier to active variant identifier. /// /// Indexed by **concept identifier**. /// /// Semantics: /// - `Missing` value → concept not required /// - otherwise → global variant identifier to be used /// std::array activeVariantIndices{}; /// /// @brief Sparse list of active concept identifiers. /// /// Only the first @ref count entries are valid. /// std::array activeConceptsIndices{}; /// /// @brief Number of active concepts. /// std::size_t count{0}; }; /// /// @namespace metkit::mars2grib::backend::sections::resolver::debug /// /// @brief Internal utilities for diagnostics and introspection. /// /// This namespace contains debug-only helpers used to inspect /// `ActiveConceptsData` instances. These utilities are not part of the /// public resolver API and must not be used in performance-critical paths. /// namespace debug { /// /// @brief Print a human-readable representation of active concept data. /// /// The output explicitly reflects the canonical access pattern used by /// the resolver and highlights both: /// - which concepts are active /// - which variants are selected /// /// @param[in] data Active concept state /// @param[in] prefix Line prefix used for indentation /// @param[out] os Output stream /// inline void debug_print_ActiveConceptsData(const ActiveConceptsData& data, std::string_view prefix, std::ostream& os) { using GeneralRegistry = ActiveConceptsData::GeneralRegistry; os << prefix << " :: ActiveConceptsData\n"; os << prefix << " :: count : " << data.count << "\n"; for (std::size_t i = 0; i < data.count; ++i) { const std::size_t conceptId = data.activeConceptsIndices[i]; const std::size_t variantId = data.activeVariantIndices[conceptId]; if (variantId == GeneralRegistry::missing) { os << prefix << " :: concept[" << i << "] : Missing\n"; } else { os << prefix << " :: concept[" << i << "] : " << GeneralRegistry::conceptNameArr[variantId] << " -> " << GeneralRegistry::variantNameArr[variantId] << "\n"; } } os << std::flush; } /// /// @brief Convert active concept data to a JSON-like string. /// /// The resulting string is intended exclusively for debugging and /// diagnostics. It is not guaranteed to conform to strict JSON. /// /// @param[in] data Active concept state /// /// @return JSON-style string representation /// inline std::string debug_convert_ActiveConceptsData_to_json(const ActiveConceptsData& data) { using GeneralRegistry = ActiveConceptsData::GeneralRegistry; std::ostringstream oss; oss << "{ \"ActiveConceptsData\": { " << "\"count\": " << data.count << ", " << "\"concepts\": [ "; for (std::size_t i = 0; i < data.count; ++i) { const std::size_t conceptId = data.activeConceptsIndices[i]; const std::size_t variantId = data.activeVariantIndices[conceptId]; oss << "{ \"concept\": \"" << GeneralRegistry::conceptNameArr[variantId] << "\""; if (variantId == GeneralRegistry::missing) { oss << ", \"variant\": \"Missing\" }"; } else { oss << ", \"variant\": \"" << GeneralRegistry::variantNameArr[variantId] << "\" }"; } if (i + 1 < data.count) { oss << ", "; } } oss << " ] } }"; return oss.str(); } } // namespace debug } // namespace metkit::mars2grib::backend::sections::resolver metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/SectionTemplateSelector.h0000664000175000017500000004377315232567717031327 0ustar alastairalastair/* * (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 SectionTemplateSelector.h /// @brief Resolver responsible for selecting the template number of a GRIB section. /// /// This header defines `SectionTemplateSelector`, the **central algorithmic /// component** of the section resolver subsystem. /// /// A `SectionTemplateSelector`: /// - Is constructed once per section from declarative recipe definitions /// - Precomputes all data structures required for efficient lookup /// - Selects, at runtime, the correct section template number given the /// active concept state /// /// The selector is immutable after construction and optimized for hot-path /// usage during encoding. /// /// @ingroup mars2grib_backend_section_resolver /// #pragma once // System includes #include #include #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/sections/resolver/ActiveConceptsData.h" #include "metkit/mars2grib/backend/sections/resolver/CompressionMask.h" #include "metkit/mars2grib/backend/sections/resolver/Recipes.h" #include "metkit/mars2grib/backend/sections/resolver/SectionLayoutData.h" #include "metkit/mars2grib/backend/sections/resolver/TemplateSignatureKey.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::sections::resolver { // SectionTemplateSelector /// /// @brief Section-local resolver for GRIB template selection. /// /// `SectionTemplateSelector` encapsulates all logic required to select /// the appropriate GRIB template number for a **single section**, given /// the runtime active concept state. /// /// The selector operates in two phases: /// /// - **Construction phase (offline / once per section)** /// - Expand declarative recipes into a flat payload /// - Build a section-specific compression mask /// - Precompute lookup indices /// - Choose the optimal search strategy /// /// - **Resolution phase (runtime / hot path)** /// - Build a signature key from active concepts /// - Apply section-specific compression /// - Perform lookup using the preselected strategy /// - Produce a `SectionLayoutData` /// /// After construction, the selector is fully immutable. /// class SectionTemplateSelector { public: /// Runtime representation of active concepts using ActiveConcepts = ActiveConceptsData; /// Section-scoped recipe configuration using Recipes = metkit::mars2grib::backend::sections::resolver::dsl::Recipes; /// Section-scoped compression mask using CompressionMask = metkit::mars2grib::backend::sections::resolver::detail::CompressionMask; /// Expanded recipe entry produced by recipe expansion using ResolvedTemplateData = metkit::mars2grib::backend::sections::resolver::dsl::ResolvedTemplateData; /// Signature key type used for lookup using TemplateSignatureKey = metkit::mars2grib::backend::sections::resolver::detail::TemplateSignatureKey; /// Hash functor for signature keys using TemplateSignatureKeyHash = metkit::mars2grib::backend::sections::resolver::detail::TemplateSignatureKeyHash; /// /// @brief Select the section layout corresponding to the active concept state. /// /// @param[in] active Runtime active concept data /// /// @return Resolved section layout /// /// @throws Mars2GribGenericException /// If no matching template can be found /// const SectionLayoutData select_or_throw(const ActiveConcepts& active) const { using metkit::mars2grib::backend::sections::resolver::detail::make_SectionLayoutData_or_throw; const std::size_t id = searchFn_(*this, active); return make_SectionLayoutData_or_throw(sectionNumber_, payloads_[id]); } /// /// @brief Construct a selector from section recipes. /// /// This is the **only construction entry point**. /// /// @param[in] recipes Declarative recipe definitions for a section /// /// @return Fully constructed, immutable selector /// /// @throws Mars2GribGenericException /// If recipe expansion or index construction fails /// static SectionTemplateSelector make(const Recipes& recipes) { using metkit::mars2grib::backend::sections::resolver::detail::make_CompressionMask_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; // -------------------------------------------------------------------- // 1. Expand recipes → resolved payload // -------------------------------------------------------------------- std::vector payload = recipes.getPayload(); if (payload.empty()) { throw Mars2GribGenericException("SectionTemplateSelector: empty payload", Here()); } // -------------------------------------------------------------------- // 2. Build section-specific compression mask // -------------------------------------------------------------------- CompressionMask compressionMask = make_CompressionMask_or_throw(payload); // -------------------------------------------------------------------- // 3. Build (compressedKey, payloadIndex) lookup index // -------------------------------------------------------------------- std::vector> index; index.reserve(payload.size()); for (std::size_t i = 0; i < payload.size(); ++i) { const auto& entry = payload[i]; TemplateSignatureKey globalKey{}; globalKey.size = entry.count; for (std::size_t k = 0; k < entry.count; ++k) { globalKey.data[k] = entry.variantIndices[k]; } TemplateSignatureKey compressedKey = compressionMask.compressKey(globalKey); index.emplace_back(std::move(compressedKey), i); } std::sort(index.begin(), index.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); // -------------------------------------------------------------------- // 4. Reorder payload according to sorted index // -------------------------------------------------------------------- std::vector orderedPayload; orderedPayload.reserve(index.size()); for (const auto& [key, payloadIdx] : index) { orderedPayload.emplace_back(payload[payloadIdx]); } // -------------------------------------------------------------------- // 5. Choose lookup strategy // -------------------------------------------------------------------- const std::size_t N = index.size(); Index indexVariant; SearchFn searchFn = nullptr; if (N == 1) { indexVariant = SingleIndex{index.front().first, 0}; searchFn = &SectionTemplateSelector::search_single; } else if (N < 16) { ArrayIndex vec; vec.reserve(N); for (std::size_t i = 0; i < N; ++i) { vec.emplace_back(index[i].first, i); } indexVariant = std::move(vec); searchFn = &SectionTemplateSelector::search_linear; } else if (N < 256) { ArrayIndex vec; vec.reserve(N); for (std::size_t i = 0; i < N; ++i) { vec.emplace_back(index[i].first, i); } indexVariant = std::move(vec); searchFn = &SectionTemplateSelector::search_binary; } else { HashIndex map; map.reserve(N); for (std::size_t i = 0; i < N; ++i) { map.emplace(index[i].first, i); } indexVariant = std::move(map); searchFn = &SectionTemplateSelector::search_hash; } // -------------------------------------------------------------------- // 6. Construct immutable selector // -------------------------------------------------------------------- return SectionTemplateSelector{recipes.sectionId(), std::move(compressionMask), std::move(orderedPayload), std::move(indexVariant), searchFn}; } private: /// /// @brief Function pointer implementing the lookup strategy. /// /// This pointer selects the concrete search algorithm used at runtime /// to resolve a compressed signature key into a payload index. /// /// The function is chosen once during construction based on the /// number of template combinations and never changes afterward. /// /// All search functions: /// - Are pure /// - Are stateless /// - Throw if no matching template is found /// using SearchFn = std::size_t (*)(const SectionTemplateSelector&, const ActiveConcepts&); /// /// @brief Index optimized for a single template. /// /// Stores exactly one `(key, payloadIndex)` pair. /// Used when the section admits only one possible template. /// using SingleIndex = std::pair; /// /// @brief Linear / binary searchable index. /// /// Stores `(key, payloadIndex)` pairs in sorted order. /// Used for small to medium template spaces. /// using ArrayIndex = std::vector; /// /// @brief Hash-based index for large template spaces. /// /// Used when the number of admissible templates exceeds /// the threshold for efficient array-based search. /// using HashIndex = std::unordered_map; /// /// @brief Variant type holding the concrete index representation. /// /// Exactly one alternative is active at runtime, selected during /// construction and never changed afterward. /// using Index = std::variant; /// /// @brief GRIB section number handled by this selector. /// /// This value is immutable and propagated into the resulting /// `SectionLayoutData`. /// const std::size_t sectionNumber_; /// /// @brief Section-specific compression mask. /// /// Used to normalize signature keys before lookup by removing /// variants that never participate in this section. /// /// The mask is computed once during construction and reused /// for every lookup. /// const CompressionMask compressionMask_; /// /// @brief Ordered payload entries corresponding to resolved templates. /// /// The payload vector is reordered during construction so that /// its indices match those stored in the lookup index. /// /// The order of variants inside each payload entry is preserved /// and later used during encoding. /// const std::vector payloads_; /// /// @brief Lookup index mapping compressed keys to payload indices. /// /// The concrete representation is stored in @ref index_ and /// interpreted by the selected search function. /// const Index index_; /// /// @brief Pointer to the active search function. /// /// This pointer is guaranteed to be non-null after construction. /// const SearchFn searchFn_; /// /// @brief Resolve template index using a single-entry index. /// /// This search strategy is used when the section admits exactly /// one possible template. /// /// The active concept state is compressed and compared directly /// against the single stored key. /// /// @param[in] self Selector instance /// @param[in] active Runtime active concept data /// /// @return Index of the matching payload entry /// /// @throws Mars2GribGenericException /// If the compressed key does not match the stored key /// SectionTemplateSelector(std::size_t sectionNumber, CompressionMask&& mask, std::vector&& payloads, Index&& index, SearchFn fn) : sectionNumber_(sectionNumber), compressionMask_(std::move(mask)), payloads_(std::move(payloads)), index_(std::move(index)), searchFn_(fn) {} /// /// @brief Resolve template index using a single-entry index. /// /// This search strategy is used when the section admits exactly /// one possible template. /// /// The active concept state is compressed and compared directly /// against the single stored key. /// /// @param[in] self Selector instance /// @param[in] active Runtime active concept data /// /// @return Index of the matching payload entry /// /// @throws Mars2GribGenericException /// If the compressed key does not match the stored key /// static std::size_t search_single(const SectionTemplateSelector& self, const ActiveConcepts& active) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; const auto& [key, id] = std::get(self.index_); TemplateSignatureKey k = self.compressionMask_.compressKey(make_key(active)); if (k == key) { return id; } throw Mars2GribGenericException("No matching recipe", Here()); } /// /// @brief Resolve template index using linear search. /// /// This strategy performs a linear scan over a small number of /// `(key, payloadIndex)` pairs. /// /// It is selected when the template space is small enough that /// linear search is faster than binary or hash-based lookup. /// /// @param[in] self Selector instance /// @param[in] active Runtime active concept data /// /// @return Index of the matching payload entry /// /// @throws Mars2GribGenericException /// If no matching key is found /// static std::size_t search_linear(const SectionTemplateSelector& self, const ActiveConcepts& active) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; const auto& vec = std::get(self.index_); TemplateSignatureKey k = self.compressionMask_.compressKey(make_key(active)); for (const auto& [kk, id] : vec) { if (kk == k) { return id; } } throw Mars2GribGenericException("No matching recipe", Here()); } /// /// @brief Resolve template index using binary search. /// /// This strategy performs a binary search over a sorted array /// of `(key, payloadIndex)` pairs. /// /// It is selected for medium-sized template spaces where /// logarithmic lookup outperforms linear scanning. /// /// @param[in] self Selector instance /// @param[in] active Runtime active concept data /// /// @return Index of the matching payload entry /// /// @throws Mars2GribGenericException /// If no matching key is found /// static std::size_t search_binary(const SectionTemplateSelector& self, const ActiveConcepts& active) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; const auto& vec = std::get(self.index_); TemplateSignatureKey k = self.compressionMask_.compressKey(make_key(active)); auto it = std::lower_bound(vec.begin(), vec.end(), k, [](const auto& p, const TemplateSignatureKey& key) { return p.first < key; }); if (it != vec.end() && it->first == k) { return it->second; } throw Mars2GribGenericException("No matching recipe", Here()); } /// /// @brief Resolve template index using hash-based lookup. /// /// This strategy performs an average O(1) lookup using an /// unordered map keyed by compressed signature keys. /// /// It is selected for large template spaces where array-based /// search would be inefficient. /// /// @param[in] self Selector instance /// @param[in] active Runtime active concept data /// /// @return Index of the matching payload entry /// /// @throws Mars2GribGenericException /// If no matching key is found /// static std::size_t search_hash(const SectionTemplateSelector& self, const ActiveConcepts& active) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; const auto& map = std::get(self.index_); TemplateSignatureKey k = self.compressionMask_.compressKey(make_key(active)); auto it = map.find(k); if (it != map.end()) { return it->second; } throw Mars2GribGenericException("No matching recipe", Here()); } /// /// @brief Build a template signature key from active concept data. /// /// The key is constructed by iterating over the list of active /// concept identifiers and collecting the corresponding global /// variant identifiers. /// /// The resulting key: /// - Reflects the active semantic state /// - Preserves no ordering guarantees /// - Must be normalized using the section compression mask /// /// @param[in] active Runtime active concept data /// /// @return Uncompressed template signature key /// static TemplateSignatureKey make_key(const ActiveConcepts& active) { TemplateSignatureKey key{}; key.size = 0; for (std::size_t i = 0; i < active.count; ++i) { const std::size_t conceptId = active.activeConceptsIndices[i]; key.data[key.size++] = active.activeVariantIndices[conceptId]; } return key; } }; } // namespace metkit::mars2grib::backend::sections::resolver metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/Recipes.h0000664000175000017500000001344015232567717026104 0ustar alastairalastair/* * (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 Recipes.h /// @brief Runtime container for all template recipes of a GRIB section. /// /// This header defines `Recipes`, a **section-scoped runtime container** /// representing the complete set of GRIB template definitions applicable /// to a single GRIB section. /// /// A `Recipes` object is defined **per section** and aggregates all /// `Recipe` instances contributing to that section. Each `Recipe` /// defines one GRIB template number together with the full combinatorial /// space of concept-variant combinations that realize that template. /// /// Conceptually: /// - A GRIB section may admit multiple template numbers /// - Each template number is defined by an ordered set of concepts /// - Each concept may participate with multiple admissible variants /// /// The role of `Recipes` is to: /// - Collect all template definitions for a given section /// - Preserve their ordering /// - Provide a uniform way to expand them into a flat list of /// `ResolvedTemplateData` payloads /// /// The container is immutable after construction and is designed to be /// traversed in hot paths during section resolution and encoding plan /// construction. /// /// @ingroup mars2grib_backend_section_recipes /// #pragma once // System includes #include #include // Project includes #include "metkit/mars2grib/backend/sections/resolver/Recipe.h" #include "metkit/mars2grib/backend/sections/resolver/ResolvedTemplateData.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::resolver::dsl { /// /// @brief Runtime, immutable container for all recipes of a single GRIB section. /// /// A `Recipes` instance represents the **complete template-definition space** /// for one specific GRIB section. /// /// It owns: /// - The section identifier /// - An ordered list of `Recipe` objects, each corresponding to one /// GRIB template number valid for that section /// /// The container provides a single expansion operation that materializes /// all possible resolved templates (`ResolvedTemplateData`) for the section, /// by expanding each recipe and concatenating their combinatorial spaces. /// /// No mutation or filtering is performed at this level. /// class Recipes { public: /// /// @brief Construct a section-scoped recipe container. /// /// @param[in] sectionId GRIB section identifier /// @param[in] recipes Ordered list of recipe definitions for the section /// Recipes(std::size_t sectionId, std::vector&& recipes) : sectionId_(sectionId), recipes_(std::move(recipes)) {} /// /// @brief Return the GRIB section identifier. /// std::size_t sectionId() const noexcept { return sectionId_; } /// /// @brief Expand all recipes into resolved template payloads. /// /// This function materializes the full set of resolved templates /// defined for the section by: /// - Iterating over all recipes /// - Expanding each recipe’s combinatorial space /// - Concatenating the results in recipe order /// /// @return Vector of resolved template payloads /// std::vector getPayload() const { // ---- compute total size ---- std::size_t total = 0; for (const Recipe* r : recipes_) { total += r->numberOfCombinations(); } // ---- allocate payload ---- std::vector payload; payload.reserve(total); // ---- expand recipes in order ---- for (const Recipe* r : recipes_) { const std::size_t n = r->numberOfCombinations(); for (std::size_t i = 0; i < n; ++i) { payload.push_back(r->getEntry(i)); } } return payload; } /// /// @brief Print a human-readable description of the section recipes. /// /// This function prepends the section identifier and delegates the /// detailed output to the nested `Recipe` objects. /// /// @param[in] prefix Line prefix used for indentation /// @param[out] os Output stream /// void debug_print(const std::string& prefix, std::ostream& os) const { os << prefix << " :: Recipes\n"; os << prefix << " :: sectionId : " << sectionId_ << "\n"; os << prefix << " :: nRecipes : " << recipes_.size() << "\n"; for (std::size_t i = 0; i < recipes_.size(); ++i) { os << prefix << " :: recipe[" << i << "]\n"; recipes_[i]->debug_print(prefix + std::string(" :: "), os); } } /// /// @brief Convert the section recipes to a JSON-like string. /// /// The output includes the section identifier and the JSON /// representation of each nested recipe. /// /// @return JSON-style string representation /// std::string debug_to_json() const { std::ostringstream oss; oss << "{ \"Recipes\": { " << "\"sectionId\": " << sectionId_ << ", " << "\"recipes\": [ "; for (std::size_t i = 0; i < recipes_.size(); ++i) { oss << recipes_[i]->debug_to_json(); if (i + 1 < recipes_.size()) { oss << ", "; } } oss << " ] } }"; return oss.str(); } private: const std::size_t sectionId_; const std::vector recipes_; }; } // namespace metkit::mars2grib::backend::sections::resolver::dsl metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/ResolvedTemplateData.h0000664000175000017500000001421515232567717030564 0ustar alastairalastair/* * (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 ResolvedTemplateData.h /// @brief Runtime container for parsed and resolved section recipes. /// /// This header defines `ResolvedTemplateData`, the **primary in-memory container** /// used by the section-recipe resolution subsystem to store the outcome of /// recipe parsing and template selection. /// /// A `ResolvedTemplateData` instance represents a fully resolved recipe entry /// and encodes, in a compact and cache-friendly form: /// - The GRIB **template number** to be applied /// - The ordered list of **global concept-variant identifiers** that define /// the exact encoding logic for that template /// /// This structure is designed to be: /// - Traversed frequently /// - Compared and searched efficiently /// - Passed through hot execution paths during encoding /// /// For this reason, the type is intentionally minimal, flat, and free of /// ownership or dynamic allocation. /// /// Debug and introspection functionality is provided externally and granted /// access via explicit friendship, ensuring that: /// - The public interface remains minimal /// - No debug-related code or symbols interfere with the hot path /// /// @ingroup mars2grib_backend_section_recipes /// #pragma once // System includes #include #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::backend::sections::resolver::dsl { /// /// @brief Main container for parsed and resolved section recipes. /// /// This structure is the **central storage unit** produced by the recipe /// parsing and resolution pipeline. /// /// Each instance corresponds to a single resolved recipe and captures all /// information required to: /// - Identify the GRIB template to be used /// - Drive the ordered execution of concept operations during encoding /// /// The container is explicitly optimized for **hot-path usage**: /// - Fixed-capacity storage /// - No dynamic memory allocation /// - Trivial data layout /// /// Instances of this type are frequently accessed during recipe lookup /// and encoding plan construction. As a consequence, no runtime validation /// or defensive checks are performed inside the structure itself. /// /// Debug and introspection facilities are intentionally implemented as /// external friend functions to avoid polluting the public API and to /// ensure that debug-related code does not impact performance-critical /// execution paths. /// /// The layout and semantics of this structure are considered part of a /// **stable internal contract** for the section-recipe subsystem. /// struct ResolvedTemplateData { using GeneralRegistry = metkit::mars2grib::backend::concepts_::GeneralRegistry; /// /// @brief Maximum number of concept variants that can be stored. /// /// This corresponds to the total number of registered concepts and /// defines the fixed capacity of the container. /// static constexpr std::size_t maxCapacity = GeneralRegistry::NConcepts; /// /// @brief Ordered list of global concept-variant identifiers. /// /// Only the first @ref count entries are valid. /// std::array variantIndices{}; /// /// @brief Number of active entries in @ref variantIndices. /// std::size_t count{0}; /// /// @brief GRIB template number associated with this resolved recipe. /// std::size_t templateNumber{0}; }; namespace debug { inline void debug_print_ResolvedTemplateData(const ResolvedTemplateData& tdata, const std::string& prefix, std::ostream& os) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; os << prefix << " :: ResolvedTemplateData" << std::endl; os << prefix << " :: templateNumber : " << tdata.templateNumber << std::endl; os << prefix << " :: count : " << tdata.count << std::endl; os << prefix << " :: variantIndices : [ "; for (std::size_t i = 0; i < tdata.count; ++i) { os << tdata.variantIndices[i]; if (i + 1 < tdata.count) os << ", "; } os << " ]" << std::endl; os << prefix << " :: variantNames : [ "; for (std::size_t i = 0; i < tdata.count; ++i) { std::size_t id = tdata.variantIndices[i]; std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); os << "\"" << cname << "::" << vname << "\""; if (i + 1 < tdata.count) os << ", "; } os << " ]" << std::endl; } inline std::string debug_convert_ResolvedTemplateData_to_json(const ResolvedTemplateData& tdata) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; std::ostringstream oss; oss << "{ \"ResolvedTemplateData\":{" << "\"templateNumber\":" << tdata.templateNumber << ", " << "\"count\": " << tdata.count << ", " << "\"variantIndices\":["; for (std::size_t i = 0; i < tdata.count; ++i) { oss << tdata.variantIndices[i]; if (i + 1 < tdata.count) oss << ", "; } oss << "], "; oss << "\"variantNames\":[ "; for (std::size_t i = 0; i < tdata.count; ++i) { std::size_t id = tdata.variantIndices[i]; std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); oss << "\"" << cname << "::" << vname << "\""; if (i + 1 < tdata.count) oss << ", "; } oss << " ]}}"; return oss.str(); } } // namespace debug } // namespace metkit::mars2grib::backend::sections::resolver::dsl metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/SectionLayoutData.h0000664000175000017500000001746315232567717030117 0ustar alastairalastair/* * (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 SectionLayoutData.h /// @brief Runtime description of a resolved GRIB section layout. /// /// This header defines `SectionLayoutData`, the **final product** of the /// section resolver subsystem. /// /// A `SectionLayoutData` instance represents a fully resolved and /// deterministic description of how a GRIB section must be encoded. /// /// It is produced by: /// - Resolving declarative recipe definitions /// - Matching them against the active concept state /// - Selecting the appropriate template number /// /// The structure is subsequently consumed by the header encoder to /// drive the execution of concept operations for the section. /// /// @ingroup mars2grib_backend_section_resolver /// #pragma once // System includes #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/sections/resolver/ResolvedTemplateData.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::sections::resolver { /// /// @brief Final resolved layout for a GRIB section. /// /// `SectionLayoutData` is a **runtime data container** that fully describes /// the encoding layout of a GRIB section. /// /// It captures: /// - The GRIB section number /// - The selected GRIB template number /// - The ordered list of global concept-variant identifiers that must be /// applied when encoding the section /// /// The structure is: /// - Immutable once constructed /// - Flat and cache-friendly /// - Free of ownership and dynamic allocation /// /// Instances of this type represent the **terminal output** of the section /// resolution process. /// struct SectionLayoutData { /// Registry providing global concept and variant identifiers using GeneralRegistry = metkit::mars2grib::backend::concepts_::GeneralRegistry; /// /// @brief Maximum number of concept variants that can be stored. /// /// This corresponds to the total number of registered concepts. /// static constexpr std::size_t maxCapacity = GeneralRegistry::NConcepts; /// /// @brief Ordered list of global variant identifiers defining the layout. /// /// Only the first @ref count entries are valid. /// std::array variantIndices{}; /// /// @brief Number of active variants in @ref variantIndices. /// std::size_t count{0}; /// /// @brief Selected GRIB template number for the section. /// std::size_t templateNumber{0}; /// /// @brief GRIB section number this layout applies to. /// std::size_t sectionNumber{0}; }; /// /// @namespace metkit::mars2grib::backend::sections::resolver::detail /// /// @brief Internal helpers for constructing section layout data. /// /// This namespace contains non-public utilities used during section /// resolution. Functions here are not part of the public API and may /// change without notice. /// namespace detail { /// /// @brief Construct a `SectionLayoutData` from a resolved recipe entry. /// /// This function converts a `ResolvedTemplateData` payload into a /// `SectionLayoutData` instance by: /// - Copying the ordered list of variant identifiers /// - Assigning the selected template number /// - Binding the layout to a specific GRIB section /// /// @param[in] sectionNumber GRIB section number /// @param[in] recipeEntry Resolved recipe entry /// /// @return Fully populated section layout data /// /// @throws Mars2GribGenericException /// If construction fails for any reason /// inline SectionLayoutData make_SectionLayoutData_or_throw(std::size_t sectionNumber, const dsl::ResolvedTemplateData& recipeEntry) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { SectionLayoutData layoutData{}; layoutData.count = recipeEntry.count; for (std::size_t i = 0; i < recipeEntry.count; ++i) { layoutData.variantIndices[i] = recipeEntry.variantIndices[i]; } layoutData.templateNumber = recipeEntry.templateNumber; layoutData.sectionNumber = sectionNumber; return layoutData; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Unable to create SectionLayoutData", Here())); } mars2gribUnreachable(); } } // namespace detail /// /// @namespace metkit::mars2grib::backend::sections::resolver::debug /// /// @brief Debug and introspection utilities for section layout data. /// /// This namespace contains diagnostic helpers for inspecting /// `SectionLayoutData` instances. These utilities are not intended for /// performance-critical use. /// namespace debug { /// /// @brief Print a human-readable description of a section layout. /// /// @param[in] data Section layout data /// @param[in] prefix Line prefix used for indentation /// @param[out] os Output stream /// inline void debug_print_SectionLayoutData(const SectionLayoutData& data, std::string_view prefix, std::ostream& os) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; os << prefix << " :: SectionLayoutData" << std::endl; os << prefix << " :: sectionNumber : " << data.sectionNumber << std::endl; os << prefix << " :: templateNumber : " << data.templateNumber << std::endl; os << prefix << " :: count : " << data.count << std::endl; os << prefix << " :: variantIndices : [ "; for (std::size_t i = 0; i < data.count; ++i) { os << data.variantIndices[i]; if (i + 1 < data.count) { os << ", "; } } os << " ] " << std::endl; os << prefix << " :: variantNames : [ "; for (std::size_t i = 0; i < data.count; ++i) { std::size_t id = data.variantIndices[i]; std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); os << "\"" << cname << "::" << vname << "\""; if (i + 1 < data.count) { os << ", "; } } os << " ]\n"; } /// /// @brief Convert section layout data to a JSON-like string. /// /// Intended exclusively for debugging and diagnostics. /// /// @param[in] data Section layout data /// /// @return JSON-style string representation /// inline std::string debug_convert_SectionLayoutData_to_json(const SectionLayoutData& data) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; std::ostringstream oss; oss << "{ \"SectionLayoutData\": { " << "\"sectionNumber\": " << data.sectionNumber << ", " << "\"templateNumber\": " << data.templateNumber << ", " << "\"count\": " << data.count << ", " << "\"variantIndices\": [ "; for (std::size_t i = 0; i < data.count; ++i) { oss << data.variantIndices[i]; if (i + 1 < data.count) { oss << ", "; } } oss << " ], \"variantNames\": [ "; for (std::size_t i = 0; i < data.count; ++i) { const std::size_t id = data.variantIndices[i]; oss << "\"" << GeneralRegistry::conceptNameArr[id] << "::" << GeneralRegistry::variantNameArr[id] << "\""; if (i + 1 < data.count) { oss << ", "; } } oss << " ] } }"; return oss.str(); } } // namespace debug } // namespace metkit::mars2grib::backend::sections::resolver metkit-1.19.2/src/metkit/mars2grib/backend/sections/resolver/CompressionMask.h0000664000175000017500000003240515232567717027631 0ustar alastairalastair/* * (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 CompressionMask.h /// @brief Section-specific mask used to normalize template signature keys. /// /// This header defines `CompressionMask`, an **internal, immutable semantic /// object** used by the section resolver to *filter and normalize* /// `TemplateSignatureKey` instances prior to lookup. /// /// -------------------------------------------------------------------------- /// /// @section compressionmask_what What is a compression mask? /// /// A compression mask is a **section-specific filter** applied to a /// `TemplateSignatureKey` in order to remove concept variants that are /// *irrelevant* for a given GRIB section. /// /// Not all concepts participate in all sections. If irrelevant variants /// were left in the key, every comparison against section-specific template /// definitions would fail, and the combinatorial space would explode. /// /// For example: /// - When resolving **Section 4**, concepts that never participate in /// Section 4 must be ignored. /// - Otherwise, template lookup would implicitly require considering /// combinations across *different sections*, which is semantically wrong. /// /// The compression mask ensures that only the variants that *can actually /// appear* in a given section contribute to key comparison. /// /// -------------------------------------------------------------------------- /// /// @section compressionmask_why Why is it needed? /// /// A `TemplateSignatureKey` represents the **full active concept state**. /// However: /// /// - Section template definitions are **section-local** /// - Key comparison must therefore be **section-local** /// /// The compression mask: /// - Removes variants that never appear in any recipe for the section /// - Normalizes keys so that lookup depends only on relevant information /// /// This step is **mandatory** for correct and efficient section resolution. /// /// -------------------------------------------------------------------------- /// /// @section compressionmask_how How is it computed? /// /// The compression mask is computed *once per section* from the section /// recipe payload, using a two-phase process. /// /// @subsection compressionmask_phase1 Phase 1: variant collection /// /// All resolved template entries for the section are scanned, and the set /// of **all variant identifiers that ever participate** in the section is /// collected. /// /// Variants that never appear in any recipe entry are marked as invalid/missing. /// /// @subsection compressionmask_phase2 Phase 2: index assignment /// /// The collected variants are assigned **dense compressed indices**. /// /// This step finalizes the mask by mapping: /// - irrelevant variants → `missing` /// - relevant variants → dense indices `[0, compressedSize)` /// /// The resulting mask is immutable and section-specific. /// /// -------------------------------------------------------------------------- /// /// @section compressionmask_sorting Order normalization vs encoding order /// /// A crucial design point is the distinction between: /// /// - **Variant identity** (used for template lookup) /// - **Variant order** (used for encoding) /// /// The compression mask supports two compression modes: /// /// - `compressKey` /// Produces a **sorted** compressed key. /// Sorting ensures that template lookup depends *only on which variants /// are present*, not on their order. /// /// - `compressUnsortedKey` /// Produces an **order-preserving** compressed key. /// This is useful when order must be retained. /// /// Importantly: /// - **Order is intentionally ignored during lookup** /// - **Order is still preserved in the payload** /// /// Encoding relies on the ordered variant list stored in /// `ResolvedTemplateData`, not on the compressed key. /// /// This asymmetry is deliberate and fundamental to the resolver design. /// /// -------------------------------------------------------------------------- /// /// @section compressionmask_relationship Relationship with Select and Recipes /// /// - `Select` defines: /// - Which variants are admissible /// - The order in which concepts must be encoded /// /// - `Recipe` and `ResolvedTemplateData` preserve this order /// /// - `CompressionMask`: /// - Ignores order /// - Retains only variant identity /// - Operates strictly at the lookup level /// /// Together, these components allow: /// - Declarative, ordered encoding /// - Order-independent, efficient template matching /// /// @note /// This type is an internal implementation detail of the resolver and is not /// part of the public API. /// /// @ingroup mars2grib_backend_section_resolver /// #pragma once // System includes #include #include #include #include #include // Project includes #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/sections/resolver/ResolvedTemplateData.h" #include "metkit/mars2grib/backend/sections/resolver/TemplateSignatureKey.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::sections::resolver::detail { /// /// @brief Section-specific compression mask for template signature keys. /// /// `CompressionMask` is an immutable object that filters and normalizes /// `TemplateSignatureKey` instances so that they can be compared and /// matched against section-local template definitions. /// /// The mask is: /// - Computed once per section /// - Derived solely from the section recipe payload /// - Independent of the active concept state /// class CompressionMask { public: /// Registry providing global variant identifiers using GeneralRegistry = metkit::mars2grib::backend::concepts_::GeneralRegistry; /// /// @brief Number of variants retained after compression. /// std::size_t compressedSize() const noexcept { return compressedSize_; } /// /// @brief Compress a signature key while preserving variant order. /// /// Irrelevant variants are removed, but the relative order of the /// remaining variants is preserved. /// /// @param[in] in Input signature key /// /// @return Compressed key with preserved order /// TemplateSignatureKey compressUnsortedKey(const TemplateSignatureKey& in) const noexcept { TemplateSignatureKey out{}; out.size = 0; for (std::size_t i = 0; i < in.size; ++i) { const std::size_t v = in.data[i]; if (mask_[v] != GeneralRegistry::missing) { out.data[out.size++] = v; } } return out; } /// /// @brief Compress a signature key and normalize its order. /// /// Irrelevant variants are removed and the remaining variants are /// inserted into the output key in sorted order. /// /// This guarantees that key comparison depends only on *which variants /// are present*, not on their order. /// /// @param[in] in Input signature key /// /// @return Sorted, compressed key /// TemplateSignatureKey compressKey(const TemplateSignatureKey& in) const noexcept { TemplateSignatureKey out{}; out.size = 0; for (std::size_t i = 0; i < in.size; ++i) { const std::size_t v = in.data[i]; if (mask_[v] == GeneralRegistry::missing) { continue; } std::size_t j = out.size; while (j > 0 && out.data[j - 1] > v) { out.data[j] = out.data[j - 1]; --j; } out.data[j] = v; ++out.size; } return out; } /// /// @brief Print a human-readable description of the compression mask. /// /// @param[in] prefix Line prefix used for indentation /// @param[out] os Output stream /// void debug_print(const std::string& prefix, std::ostream& os) const { using metkit::mars2grib::backend::concepts_::GeneralRegistry; os << prefix << " :: Compressed size: " << compressedSize_ << "\n"; os << prefix << " :: Compression mask indices: [ "; for (std::size_t v = 0; v < mask_.size(); ++v) { os << mask_[v]; if (v + 1 < mask_.size()) { os << ", "; } } os << " ]\n"; os << prefix << " :: Compression mask names: [ "; for (std::size_t v = 0; v < mask_.size(); ++v) { std::size_t id = mask_[v]; if (id != GeneralRegistry::missing) { std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); os << "\"" << cname << "::" << vname << "\""; } else { os << "\"invalid\""; } if (v + 1 < mask_.size()) { os << ", "; } } os << " ]" << std::endl; } /// /// @brief Convert the compression mask to a JSON-like string. /// /// This method produces a diagnostic, human-readable representation of the /// compression mask. The output is intended exclusively for debugging and /// introspection and is not guaranteed to be valid JSON. /// /// The representation exposes: /// - the compressed size /// - the full variant-to-compressed-index mapping /// /// Variants mapped to `missing` explicitly indicate concepts that never /// participate in the section and are therefore removed during key compression. /// /// @return JSON-style string describing the compression mask /// std::string debug_to_json() const { using metkit::mars2grib::backend::concepts_::GeneralRegistry; std::ostringstream oss; oss << "{ \"CompressionMask\": { " << "\"compressedSize\": " << compressedSize_ << ", " << "\"maskIndices\": [ "; for (std::size_t v = 0; v < mask_.size(); ++v) { oss << mask_[v]; if (v + 1 < mask_.size()) { oss << ", "; } } oss << " ], "; oss << "\"maskNames\": [ "; for (std::size_t v = 0; v < mask_.size(); ++v) { std::size_t id = mask_[v]; if (id != GeneralRegistry::missing) { std::string cname = std::string(GeneralRegistry::conceptNameArr[id]); std::string vname = std::string(GeneralRegistry::variantNameArr[id]); oss << "\"" << cname << "::" << vname << "\""; } else { oss << "\"invalid\""; } if (v + 1 < mask_.size()) { oss << ", "; } } oss << " ] } }"; return oss.str(); } private: /// Mapping from global variant identifier to compressed index or invalid const std::array mask_; /// Number of retained variants const std::size_t compressedSize_; CompressionMask(std::array&& mask, std::size_t compressedSize) : mask_(std::move(mask)), compressedSize_(compressedSize) {} friend CompressionMask make_CompressionMask_or_throw( const std::vector&); }; /// /// @brief Build a compression mask from section recipe payload. /// /// This factory computes a section-specific compression mask by scanning /// all resolved template entries and collecting the set of variants that /// ever participate in the section. /// /// @param[in] payload Resolved template payload for the section /// /// @return Fully constructed compression mask /// /// @throws Mars2GribGenericException /// If the payload is empty or inconsistent /// inline CompressionMask make_CompressionMask_or_throw( const std::vector& payload) { using metkit::mars2grib::backend::concepts_::GeneralRegistry; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; if (payload.empty()) { throw Mars2GribGenericException("CompressionMask: empty payload", Here()); } std::array mask{}; mask.fill(0); for (const auto& entry : payload) { for (std::size_t i = 0; i < entry.count; ++i) { const std::size_t v = entry.variantIndices[i]; if (v >= GeneralRegistry::NVariants) { throw Mars2GribGenericException("CompressionMask: variant index out of range", Here()); } ++mask[v]; } } std::size_t cnt = 0; for (std::size_t v = 0; v < GeneralRegistry::NVariants; ++v) { if (mask[v] == 0) { mask[v] = GeneralRegistry::missing; } else { mask[v] = cnt++; } } return CompressionMask{std::move(mask), cnt}; } } // namespace metkit::mars2grib::backend::sections::resolver::detail metkit-1.19.2/src/metkit/mars2grib/backend/tables/0000775000175000017500000000000015232567717022121 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/tables/timeUnits.h0000664000175000017500000001216115232567717024254 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief GRIB time units (Code Table 4.4). /// /// This enumeration represents the GRIB code values associated with /// time units as defined in GRIB2 Code Table 4.4. /// /// The numeric values map **directly** to the official GRIB code table /// and must not be changed manually. /// /// @section Source of truth /// GRIB2 Code Table 4.4: /// Units of time range /// /// @note /// This enum is a pure GRIB table representation. /// No semantic interpretation or policy decisions are encoded here. /// /// @todo [owner: mds,dgov][scope: tables][reason: correctness][prio: critical] /// - Generate this enum automatically from ecCodes GRIB tables /// to guarantee alignment with the runtime ecCodes version. /// enum class TimeUnit : long { Minute = 0, Hour = 1, Day = 2, Month = 3, Year = 4, Decade = 5, // 10 years Normal = 6, // 30 years Century = 7, // 100 years Hours3 = 10, Hours6 = 11, Hours12 = 12, Second = 13, Missing = 255 }; /// /// @brief Convert a symbolic time-unit name to a GRIB `TimeUnit`. /// /// Performs a strict mapping from a string identifier to the corresponding /// GRIB time unit code. /// /// Supported names: /// - "minute" /// - "hour" /// - "day" /// - "month" /// - "year" /// - "decade" /// - "normal" /// - "century" /// - "3h" /// - "6h" /// - "12h" /// - "second" /// - "missing" /// /// @param[in] name Symbolic name of the time unit /// /// @return Corresponding `TimeUnit` enumeration value /// /// @throws Mars2GribTableException /// If the name is not a supported GRIB time unit /// /// @note /// - Mapping is case-sensitive by design. /// - No normalization or aliasing is performed. /// inline TimeUnit name2enum_TimeUnit_or_throw(const std::string& name) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (name == "minute") return TimeUnit::Minute; if (name == "hour") return TimeUnit::Hour; if (name == "day") return TimeUnit::Day; if (name == "month") return TimeUnit::Month; if (name == "year") return TimeUnit::Year; if (name == "decade") return TimeUnit::Decade; if (name == "normal") return TimeUnit::Normal; if (name == "century") return TimeUnit::Century; if (name == "3h") return TimeUnit::Hours3; if (name == "6h") return TimeUnit::Hours6; if (name == "12h") return TimeUnit::Hours12; if (name == "second") return TimeUnit::Second; if (name == "missing") return TimeUnit::Missing; std::string err = "Invalid TimeUnit name: actual='" + name + "', expected={minute,hour,day,month,year,decade,normal,century," "3h,6h,12h,second,missing}"; throw Mars2GribTableException(err, Here()); mars2gribUnreachable(); } /// /// @brief Convert a GRIB `TimeUnit` enumeration to its symbolic name. /// /// Performs a strict mapping from a GRIB time unit code to its /// canonical string representation. /// /// @param[in] value GRIB `TimeUnit` enumeration value /// /// @return Canonical symbolic name of the time unit /// /// @throws Mars2GribTableException /// If the enum value is not supported /// /// @note /// - Returned strings are stable and suitable for logging, YAML, /// diagnostics, and round-tripping via `name2enum_TimeUnit_or_throw`. /// inline std::string enum2name_TimeUnit_or_throw(TimeUnit value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case TimeUnit::Minute: return "minute"; case TimeUnit::Hour: return "hour"; case TimeUnit::Day: return "day"; case TimeUnit::Month: return "month"; case TimeUnit::Year: return "year"; case TimeUnit::Decade: return "decade"; case TimeUnit::Normal: return "normal"; case TimeUnit::Century: return "century"; case TimeUnit::Hours3: return "3h"; case TimeUnit::Hours6: return "6h"; case TimeUnit::Hours12: return "12h"; case TimeUnit::Second: return "second"; case TimeUnit::Missing: return "missing"; } std::string err = "Invalid TimeUnit enum value: actual='" + std::to_string(static_cast(value)) + "'"; throw Mars2GribTableException(err, Here()); mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::tablesmetkit-1.19.2/src/metkit/mars2grib/backend/tables/backgroundProcess.h0000664000175000017500000002573315232567717025762 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief GRIB background process classification. /// /// This enumeration represents the GRIB code values associated with the /// `backgroundProcess` key in the Product Definition Section. /// /// Each enumerator corresponds to a distinct model configuration or /// post-processing workflow as defined by ECMWF conventions and encoded /// in GRIB local concepts. /// /// The numeric values of the enumerators map **directly** to the GRIB /// code table values and must not be changed manually. /// /// @note /// The value `255` usually corresponds to the GRIB *missing* value. /// In this specific case, it is semantically interpreted as the /// deterministic IFS workflow (`ifs`) for compatibility with existing /// production data. /// /// @important /// This enum is a **GRIB-level representation**, not a policy decision. /// All semantic validation, defaulting, and resolution logic must be /// implemented in the corresponding deduction functions. /// /// @section Source of truth /// The authoritative definition of supported background processes and /// their GRIB encodings is maintained in: /// /// `definitions/grib2/localConcepts/ecmf/modelNameConcept.def` /// /// This enumeration must remain consistent with that definition. /// /// @todo [owner: mds,dgov][scope: deduction][reason: correctness][prio: critical] /// - Replace this manually maintained enumeration with code generated /// automatically from ecCodes GRIB code tables / definitions. /// - The generation should occur at build or configure time (e.g. via a /// Python code-generation step) to prevent silent divergence between /// the encoder and the ecCodes library. /// enum class BackgroundProcess : long { aifs_single = 1, aifs_ens = 2, aifs_single_mse = 3, aifs_ens_crps = 4, aifs_ens_diff = 5, aifs_compo_single = 6, aifs_compo_ens = 7, aifs_compo_single_mse = 8, aifs_compo_ens_crps = 9, aifs_subs = 10, aifs_subs_crps = 11, ifs = 255 }; /// /// @brief Map a MARS model identifier to a GRIB `BackgroundProcess` enumeration. /// /// This function converts a string-based model identifier, typically obtained /// from the MARS key `mars::model`, into the corresponding GRIB /// `BackgroundProcess` enumeration value. /// /// The mapping is explicit and strict. Only the following identifiers are /// supported: /// /// - `"aifs-single"` → `BackgroundProcess::aifs_single` // 1 /// - `"aifs-ens"` → `BackgroundProcess::aifs_ens` // 2 /// - `"aifs-single-mse"` → `BackgroundProcess::aifs_single_mse` // 3 /// - `"aifs-ens-crps"` → `BackgroundProcess::aifs_ens_crps` // 4 /// - `"aifs-ens-diff"` → `BackgroundProcess::aifs_ens_diff` // 5 /// - `"aifs-compo-single"` → `BackgroundProcess::aifs_compo_single` // 6 /// - `"aifs-compo-ens"` → `BackgroundProcess::aifs_compo_ens` // 7 /// - `"aifs-compo-single-mse"` → `BackgroundProcess::aifs_compo_single_mse` // 8 /// - `"aifs-compo-ens-crps"` → `BackgroundProcess::aifs_compo_ens_crps` // 9 /// - `"aifs-subs"` → `BackgroundProcess::aifs_subs` // 10 /// - `"aifs-subs-crps"` → `BackgroundProcess::aifs_subs_crps` // 11 /// - `"IFS"` → `BackgroundProcess::ifs` // 255 /// /// Any other value is considered invalid and results in a deduction error. /// /// @param[in] value String value of the MARS `model` key to be mapped /// /// @return The corresponding `BackgroundProcess` enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the provided string does not correspond to a supported /// MARS model identifier. /// /// @note /// - This function performs a **pure mapping** from MARS semantics to /// GRIB background process codes. /// - No implicit normalization, fallback, or defaulting is performed. /// /// @important /// The authoritative definition of valid model identifiers and their /// mapping to GRIB background process codes is maintained in: /// /// `definitions/grib2/localConcepts/ecmf/modelNameConcept.def` /// /// This function must remain consistent with that definition. /// /// @todo [owner: mds,dgov][scope: deduction][reason: correctness][prio: medium] /// - Replace this hard-coded mapping with code generated directly from /// ecCodes definitions to prevent divergence between software stacks. /// inline BackgroundProcess name2enum_BackgroundProcess_or_throw(const std::string& value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (value == "aifs-single") { return BackgroundProcess::aifs_single; } else if (value == "aifs-ens") { return BackgroundProcess::aifs_ens; } else if (value == "aifs-single-mse") { return BackgroundProcess::aifs_single_mse; } else if (value == "aifs-ens-crps") { return BackgroundProcess::aifs_ens_crps; } else if (value == "aifs-ens-diff") { return BackgroundProcess::aifs_ens_diff; } else if (value == "aifs-compo-single") { return BackgroundProcess::aifs_compo_single; } else if (value == "aifs-compo-ens") { return BackgroundProcess::aifs_compo_ens; } else if (value == "aifs-compo-single-mse") { return BackgroundProcess::aifs_compo_single_mse; } else if (value == "aifs-compo-ens-crps") { return BackgroundProcess::aifs_compo_ens_crps; } else if (value == "aifs-subs") { return BackgroundProcess::aifs_subs; } else if (value == "aifs-subs-crps") { return BackgroundProcess::aifs_subs_crps; } else if (value == "IFS") { return BackgroundProcess::ifs; } else { std::string errMsg = "Invalid BackgroundProcess value: "; errMsg += "actual='" + value + "', "; errMsg += "expected={'aifs-single', 'aifs-ens', 'aifs-single-mse', 'aifs-ens-crps', 'aifs-ens-diff', " "'aifs-compo-single', 'aifs-compo-ens', 'aifs-compo-single-mse', 'aifs-compo-ens-crps', " "'aifs-subs', 'aifs-subs-crps', 'IFS'}"; throw Mars2GribTableException(errMsg, Here()); } // Remove compiler warning mars2gribUnreachable(); } /// /// @brief Map a GRIB `BackgroundProcess` enumeration to its canonical MARS model identifier. /// /// This function converts a GRIB-level `BackgroundProcess` enumeration value /// into the corresponding canonical string identifier used by MARS /// (e.g. `mars::model`). /// /// The mapping is explicit and strict. Only officially supported enumeration /// values are accepted. /// /// Supported mappings: /// /// - `BackgroundProcess::aifs_single` → `"aifs-single"` // 1 /// - `BackgroundProcess::aifs_ens` → `"aifs-ens"` // 2 /// - `BackgroundProcess::aifs_single_mse` → `"aifs-single-mse"` // 3 /// - `BackgroundProcess::aifs_ens_crps` → `"aifs-ens-crps"` // 4 /// - `BackgroundProcess::aifs_ens_diff` → `"aifs-ens-diff"` // 5 /// - `BackgroundProcess::aifs_compo_single` → `"aifs-compo-single"` // 6 /// - `BackgroundProcess::aifs_compo_ens` → `"aifs-compo-ens"` // 7 /// - `BackgroundProcess::aifs_compo_single_mse` → `"aifs-compo-single-mse"` // 8 /// - `BackgroundProcess::aifs_compo_ens_crps` → `"aifs-compo-ens-crps"` // 9 /// - `BackgroundProcess::aifs_subs` → `"aifs-subs"` // 10 /// - `BackgroundProcess::aifs_subs_crps` → `"aifs-subs-crps"` // 11 /// - `BackgroundProcess::ifs` → `"IFS"` // 255 /// /// Any other enumeration value is considered invalid and results in a /// table-mapping error. /// /// @param[in] value GRIB `BackgroundProcess` enumeration value /// /// @return Canonical string representation corresponding to the MARS /// `model` identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enumeration value does not correspond to a supported /// background process. /// /// @note /// - This function performs a **pure reverse mapping** of /// `mapto_BackgroundProcess_or_throw(const std::string&)`. /// - No implicit normalization, fallback, or defaulting is performed. /// /// @important /// The authoritative definition of background process identifiers and their /// string representations is maintained in: /// /// `definitions/grib2/localConcepts/ecmf/modelNameConcept.def` /// /// This function must remain strictly consistent with that definition. /// /// @todo [owner: mds,dgov][scope: tables][reason: correctness][prio: medium] /// - Replace this hard-coded mapping with code generated directly from /// ecCodes definitions to guarantee bidirectional consistency. /// inline std::string enum2name_BackgroundProcess_or_throw(BackgroundProcess value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case BackgroundProcess::aifs_single: return "aifs-single"; // 1 case BackgroundProcess::aifs_ens: return "aifs-ens"; // 2 case BackgroundProcess::aifs_single_mse: return "aifs-single-mse"; // 3 case BackgroundProcess::aifs_ens_crps: return "aifs-ens-crps"; // 4 case BackgroundProcess::aifs_ens_diff: return "aifs-ens-diff"; // 5 case BackgroundProcess::aifs_compo_single: return "aifs-compo-single"; // 6 case BackgroundProcess::aifs_compo_ens: return "aifs-compo-ens"; // 7 case BackgroundProcess::aifs_compo_single_mse: return "aifs-compo-single-mse"; // 8 case BackgroundProcess::aifs_compo_ens_crps: return "aifs-compo-ens-crps"; // 9 case BackgroundProcess::aifs_subs: return "aifs-subs"; // 10 case BackgroundProcess::aifs_subs_crps: return "aifs-subs-crps"; // 11 case BackgroundProcess::ifs: return "IFS"; // 255 default: std::string errMsg = "Invalid BackgroundProcess enum value: "; errMsg += std::to_string(static_cast(value)); throw Mars2GribTableException(errMsg, Here()); } // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::tables metkit-1.19.2/src/metkit/mars2grib/backend/tables/typeOfGeneratingProcess.h0000664000175000017500000002326715232567717027115 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief GRIB Code Table 4.3 – Type of generating process. /// /// This enumeration represents the GRIB2 *Type of Generating Process* /// as defined in Section 4, Code Table 3. /// /// The numeric values map **directly** to the official GRIB code table /// and must not be changed manually. /// /// @section Source of truth /// GRIB2 Code Table 4.3: /// Type of generating process /// /// @note /// This enum is a pure GRIB table representation. /// No semantic interpretation or policy decisions are encoded here. /// /// @todo [owner: mds,dgov][scope: tables][reason: correctness][prio: critical] /// - Generate this enum automatically from ecCodes GRIB tables /// to guarantee alignment with the runtime ecCodes version. /// enum class TypeOfGeneratingProcess : long { Analysis = 0, Initialization = 1, Forecast = 2, BiasCorrectedForecast = 3, EnsembleForecast = 4, ProbabilityForecast = 5, ForecastError = 6, AnalysisError = 7, Observation = 8, Climatological = 9, ProbabilityWeightedForecast = 10, BiasCorrectedEnsembleForecast = 11, PostProcessedAnalysis = 12, PostProcessedForecast = 13, Nowcast = 14, Hindcast = 15, PhysicalRetrieval = 16, RegressionAnalysis = 17, DifferenceBetweenTwoForecasts = 18, FirstGuess = 19, AnalysisIncrement = 20, InitializationIncrementForAnalysis = 21, BlendedForecast = 22, Missing = 255 }; /// /// @brief Map a canonical string identifier to a GRIB `TypeOfGeneratingProcess`. /// /// This function converts a string-based identifier into the corresponding /// GRIB `TypeOfGeneratingProcess` enumeration value. /// /// The mapping is explicit and strict. Only officially supported identifiers /// are accepted. /// /// @param[in] value Canonical string identifier /// /// @return Corresponding `TypeOfGeneratingProcess` enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the provided string does not correspond to a supported /// generating process type. /// /// @note /// - This is a **pure table mapping**. /// - No normalization, fallback, or defaulting is performed. /// /// @todo [owner: mds,dgov][scope: tables][reason: correctness][prio: medium] /// - Replace this hard-coded mapping with code generated directly from /// ecCodes GRIB code tables. /// inline TypeOfGeneratingProcess name2enum_TypeOfGeneratingProcess_or_throw(const std::string& value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (value == "analysis") return TypeOfGeneratingProcess::Analysis; else if (value == "initialization") return TypeOfGeneratingProcess::Initialization; else if (value == "forecast") return TypeOfGeneratingProcess::Forecast; else if (value == "biasCorrectedForecast") return TypeOfGeneratingProcess::BiasCorrectedForecast; else if (value == "ensembleForecast") return TypeOfGeneratingProcess::EnsembleForecast; else if (value == "probabilityForecast") return TypeOfGeneratingProcess::ProbabilityForecast; else if (value == "forecastError") return TypeOfGeneratingProcess::ForecastError; else if (value == "analysisError") return TypeOfGeneratingProcess::AnalysisError; else if (value == "observation") return TypeOfGeneratingProcess::Observation; else if (value == "climatological") return TypeOfGeneratingProcess::Climatological; else if (value == "probabilityWeightedForecast") return TypeOfGeneratingProcess::ProbabilityWeightedForecast; else if (value == "biasCorrectedEnsembleForecast") return TypeOfGeneratingProcess::BiasCorrectedEnsembleForecast; else if (value == "postProcessedAnalysis") return TypeOfGeneratingProcess::PostProcessedAnalysis; else if (value == "postProcessedForecast") return TypeOfGeneratingProcess::PostProcessedForecast; else if (value == "nowcast") return TypeOfGeneratingProcess::Nowcast; else if (value == "hindcast") return TypeOfGeneratingProcess::Hindcast; else if (value == "physicalRetrieval") return TypeOfGeneratingProcess::PhysicalRetrieval; else if (value == "regressionAnalysis") return TypeOfGeneratingProcess::RegressionAnalysis; else if (value == "differenceBetweenTwoForecasts") return TypeOfGeneratingProcess::DifferenceBetweenTwoForecasts; else if (value == "firstGuess") return TypeOfGeneratingProcess::FirstGuess; else if (value == "analysisIncrement") return TypeOfGeneratingProcess::AnalysisIncrement; else if (value == "initializationIncrementForAnalysis") return TypeOfGeneratingProcess::InitializationIncrementForAnalysis; else if (value == "blendedForecast") return TypeOfGeneratingProcess::BlendedForecast; else if (value == "missing") return TypeOfGeneratingProcess::Missing; else { std::string errMsg = "Invalid TypeOfGeneratingProcess value: "; errMsg += "actual='" + value + "'"; throw Mars2GribTableException(errMsg, Here()); } mars2gribUnreachable(); } /// /// @brief Map a GRIB `TypeOfGeneratingProcess` enumeration to its canonical string identifier. /// /// This function converts a GRIB-level `TypeOfGeneratingProcess` enumeration /// value into its canonical string representation. /// /// @param[in] value GRIB `TypeOfGeneratingProcess` enumeration value /// /// @return Canonical string identifier corresponding to the enumeration /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enumeration value is not supported. /// /// @note /// - This function is the strict inverse of /// `mapto_TypeOfGeneratingProcess_or_throw(const std::string&)`. /// - No fallback or defaulting is performed. /// /// @todo [owner: mds,dgov][scope: tables][reason: correctness][prio: medium] /// - Replace this hard-coded mapping with code generated directly from /// ecCodes GRIB code tables. /// inline std::string enum2name_TypeOfGeneratingProcess_or_throw(TypeOfGeneratingProcess value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case TypeOfGeneratingProcess::Analysis: return "analysis"; case TypeOfGeneratingProcess::Initialization: return "initialization"; case TypeOfGeneratingProcess::Forecast: return "forecast"; case TypeOfGeneratingProcess::BiasCorrectedForecast: return "biasCorrectedForecast"; case TypeOfGeneratingProcess::EnsembleForecast: return "ensembleForecast"; case TypeOfGeneratingProcess::ProbabilityForecast: return "probabilityForecast"; case TypeOfGeneratingProcess::ForecastError: return "forecastError"; case TypeOfGeneratingProcess::AnalysisError: return "analysisError"; case TypeOfGeneratingProcess::Observation: return "observation"; case TypeOfGeneratingProcess::Climatological: return "climatological"; case TypeOfGeneratingProcess::ProbabilityWeightedForecast: return "probabilityWeightedForecast"; case TypeOfGeneratingProcess::BiasCorrectedEnsembleForecast: return "biasCorrectedEnsembleForecast"; case TypeOfGeneratingProcess::PostProcessedAnalysis: return "postProcessedAnalysis"; case TypeOfGeneratingProcess::PostProcessedForecast: return "postProcessedForecast"; case TypeOfGeneratingProcess::Nowcast: return "nowcast"; case TypeOfGeneratingProcess::Hindcast: return "hindcast"; case TypeOfGeneratingProcess::PhysicalRetrieval: return "physicalRetrieval"; case TypeOfGeneratingProcess::RegressionAnalysis: return "regressionAnalysis"; case TypeOfGeneratingProcess::DifferenceBetweenTwoForecasts: return "differenceBetweenTwoForecasts"; case TypeOfGeneratingProcess::FirstGuess: return "firstGuess"; case TypeOfGeneratingProcess::AnalysisIncrement: return "analysisIncrement"; case TypeOfGeneratingProcess::InitializationIncrementForAnalysis: return "initializationIncrementForAnalysis"; case TypeOfGeneratingProcess::BlendedForecast: return "blendedForecast"; case TypeOfGeneratingProcess::Missing: return "missing"; default: std::string errMsg = "Invalid TypeOfGeneratingProcess enum value: "; errMsg += std::to_string(static_cast(value)); throw Mars2GribTableException(errMsg, Here()); } mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::tablesmetkit-1.19.2/src/metkit/mars2grib/backend/tables/typeOfInterval.h0000664000175000017500000001663615232567717025261 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief Type of interval. /// /// This enumeration represents GRIB code values defining how an interval /// is interpreted with respect to its first and second limits. /// /// The numeric values map **directly** to ecCodes GRIB table 4.91 /// and must not be changed manually. /// /// @note /// The value `255` corresponds to the GRIB *missing* value. /// /// @important /// This enum is a **GRIB-table representation only**. /// No policy, defaulting, or deduction logic belongs here. /// /// @todo [owner: mival,dgov][scope: tables][reason: correctness][prio: medium] /// - Generate this enum and all conversion helpers automatically from /// ecCodes definitions at build time. /// enum class TypeOfInterval : long { SmallerThanFirstLimit = 0, GreaterThanSecondLimit = 1, BetweenFirstInclusiveSecondExclusive = 2, GreaterThanFirstLimit = 3, SmallerThanSecondLimit = 4, SmallerOrEqualFirstLimit = 5, GreaterOrEqualSecondLimit = 6, BetweenFirstInclusiveSecondInclusive = 7, GreaterOrEqualFirstLimit = 8, SmallerOrEqualSecondLimit = 9, BetweenFirstExclusiveSecondInclusive = 10, EqualFirstLimit = 11, Missing = 255 }; /// /// @brief Convert `TypeOfInterval` to its canonical name. /// /// @param[in] value Enumeration value /// /// @return Canonical string name /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enumeration value is invalid. /// inline std::string enum2name_TypeOfInterval_or_throw(TypeOfInterval value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case TypeOfInterval::SmallerThanFirstLimit: return "smaller-than-first-limit"; case TypeOfInterval::GreaterThanSecondLimit: return "greater-than-second-limit"; case TypeOfInterval::BetweenFirstInclusiveSecondExclusive: return "between-first-inclusive-second-exclusive"; case TypeOfInterval::GreaterThanFirstLimit: return "greater-than-first-limit"; case TypeOfInterval::SmallerThanSecondLimit: return "smaller-than-second-limit"; case TypeOfInterval::SmallerOrEqualFirstLimit: return "smaller-or-equal-first-limit"; case TypeOfInterval::GreaterOrEqualSecondLimit: return "greater-or-equal-second-limit"; case TypeOfInterval::BetweenFirstInclusiveSecondInclusive: return "between-first-inclusive-second-inclusive"; case TypeOfInterval::GreaterOrEqualFirstLimit: return "greater-or-equal-first-limit"; case TypeOfInterval::SmallerOrEqualSecondLimit: return "smaller-or-equal-second-limit"; case TypeOfInterval::BetweenFirstExclusiveSecondInclusive: return "between-first-exclusive-second-inclusive"; case TypeOfInterval::EqualFirstLimit: return "equal-first-limit"; case TypeOfInterval::Missing: return "missing"; default: throw Mars2GribTableException("Invalid TypeOfInterval enum value", Here()); } } /// /// @brief Convert a canonical name to `TypeOfInterval`. /// /// @param[in] name Canonical string name /// /// @return Corresponding enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the name is not recognized. /// inline TypeOfInterval name2enum_TypeOfInterval_or_throw(const std::string& name) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (name == "smaller-than-first-limit") return TypeOfInterval::SmallerThanFirstLimit; else if (name == "greater-than-second-limit") return TypeOfInterval::GreaterThanSecondLimit; else if (name == "between-first-inclusive-second-exclusive") return TypeOfInterval::BetweenFirstInclusiveSecondExclusive; else if (name == "greater-than-first-limit") return TypeOfInterval::GreaterThanFirstLimit; else if (name == "smaller-than-second-limit") return TypeOfInterval::SmallerThanSecondLimit; else if (name == "smaller-or-equal-first-limit") return TypeOfInterval::SmallerOrEqualFirstLimit; else if (name == "greater-or-equal-second-limit") return TypeOfInterval::GreaterOrEqualSecondLimit; else if (name == "between-first-inclusive-second-inclusive") return TypeOfInterval::BetweenFirstInclusiveSecondInclusive; else if (name == "greater-or-equal-first-limit") return TypeOfInterval::GreaterOrEqualFirstLimit; else if (name == "smaller-or-equal-second-limit") return TypeOfInterval::SmallerOrEqualSecondLimit; else if (name == "between-first-exclusive-second-inclusive") return TypeOfInterval::BetweenFirstExclusiveSecondInclusive; else if (name == "equal-first-limit") return TypeOfInterval::EqualFirstLimit; else if (name == "missing") return TypeOfInterval::Missing; else throw Mars2GribTableException("Invalid TypeOfInterval name: '" + name + "'", Here()); } /// /// @brief Convert a numeric GRIB code to `TypeOfInterval`. /// /// @param[in] value Numeric GRIB code /// /// @return Corresponding enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the value is not defined in the GRIB table. /// inline TypeOfInterval long2enum_TypeOfInterval_or_throw(long value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case 0: return TypeOfInterval::SmallerThanFirstLimit; case 1: return TypeOfInterval::GreaterThanSecondLimit; case 2: return TypeOfInterval::BetweenFirstInclusiveSecondExclusive; case 3: return TypeOfInterval::GreaterThanFirstLimit; case 4: return TypeOfInterval::SmallerThanSecondLimit; case 5: return TypeOfInterval::SmallerOrEqualFirstLimit; case 6: return TypeOfInterval::GreaterOrEqualSecondLimit; case 7: return TypeOfInterval::BetweenFirstInclusiveSecondInclusive; case 8: return TypeOfInterval::GreaterOrEqualFirstLimit; case 9: return TypeOfInterval::SmallerOrEqualSecondLimit; case 10: return TypeOfInterval::BetweenFirstExclusiveSecondInclusive; case 11: return TypeOfInterval::EqualFirstLimit; case 255: return TypeOfInterval::Missing; default: throw Mars2GribTableException( "Invalid TypeOfInterval numeric value: actual='" + std::to_string(value) + "', expected={0..11,255}", Here()); } } } // namespace metkit::mars2grib::backend::tables metkit-1.19.2/src/metkit/mars2grib/backend/tables/typeOfEnsembleForecast.h0000664000175000017500000002111515232567717026702 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief GRIB Type of Ensemble Forecast. /// /// This enumeration represents the GRIB code values defined in /// **GRIB2 Code Table 4.6 – Type of ensemble forecast**. /// /// The values describe how a forecast member relates to the ensemble /// generation strategy (control, perturbation type, multi-model, etc.). /// /// The numeric values of the enumerators map **directly** to the GRIB /// code table and must not be modified manually. /// /// @note /// The value `255` corresponds to the GRIB *missing* value. /// /// @important /// This enum is a **GRIB-level representation** only. /// No policy, deduction, or defaulting logic must be embedded here. /// All semantic decisions must be implemented in the corresponding /// deduction functions. /// /// @section Source of truth /// The authoritative definition of this table is: /// /// GRIB2 Code Table 4.6 – Type of ensemble forecast /// /// as implemented in ecCodes. /// /// @todo [owner: mival,dgov][scope: tables][reason: correctness][prio: medium] /// - Generate this enum and its conversion helpers automatically from /// ecCodes GRIB code tables at build or configure time to avoid drift. /// enum class TypeOfEnsembleForecast : long { UnperturbedHighResControl = 0, UnperturbedLowResControl = 1, NegativelyPerturbed = 2, PositivelyPerturbed = 3, MultiModel = 4, Unperturbed = 5, Perturbed = 6, InitialConditionsPerturbations = 7, ModelPhysicsPerturbations = 8, InitialAndModelPhysicsPerturbations = 9, Missing = 255 }; /// /// @brief Convert `TypeOfEnsembleForecast` to its canonical string name. /// /// The returned string corresponds to the symbolic GRIB meaning of the /// enumerator and is intended for logging, diagnostics, and validation. /// /// @param[in] value Enumeration value /// /// @return Canonical string representation /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enum value is not recognized. /// inline std::string enum2name_TypeOfEnsembleForecast_or_throw(TypeOfEnsembleForecast value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case TypeOfEnsembleForecast::UnperturbedHighResControl: return "unperturbed-high-res-control"; case TypeOfEnsembleForecast::UnperturbedLowResControl: return "unperturbed-low-res-control"; case TypeOfEnsembleForecast::NegativelyPerturbed: return "negatively-perturbed"; case TypeOfEnsembleForecast::PositivelyPerturbed: return "positively-perturbed"; case TypeOfEnsembleForecast::MultiModel: return "multi-model"; case TypeOfEnsembleForecast::Unperturbed: return "unperturbed"; case TypeOfEnsembleForecast::Perturbed: return "perturbed"; case TypeOfEnsembleForecast::InitialConditionsPerturbations: return "initial-conditions-perturbations"; case TypeOfEnsembleForecast::ModelPhysicsPerturbations: return "model-physics-perturbations"; case TypeOfEnsembleForecast::InitialAndModelPhysicsPerturbations: return "initial-and-model-physics-perturbations"; case TypeOfEnsembleForecast::Missing: return "missing"; default: throw Mars2GribTableException("Invalid TypeOfEnsembleForecast enum value", Here()); } } /// /// @brief Convert a canonical string name to `TypeOfEnsembleForecast`. /// /// This function performs a strict mapping from a string identifier /// to the corresponding GRIB enumeration value. /// /// @param[in] name Canonical string name /// /// @return Corresponding `TypeOfEnsembleForecast` value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the provided name is not supported. /// inline TypeOfEnsembleForecast name2enum_TypeOfEnsembleForecast_or_throw(const std::string& name) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (name == "unperturbed-high-res-control") { return TypeOfEnsembleForecast::UnperturbedHighResControl; } else if (name == "unperturbed-low-res-control") { return TypeOfEnsembleForecast::UnperturbedLowResControl; } else if (name == "negatively-perturbed") { return TypeOfEnsembleForecast::NegativelyPerturbed; } else if (name == "positively-perturbed") { return TypeOfEnsembleForecast::PositivelyPerturbed; } else if (name == "multi-model") { return TypeOfEnsembleForecast::MultiModel; } else if (name == "unperturbed") { return TypeOfEnsembleForecast::Unperturbed; } else if (name == "perturbed") { return TypeOfEnsembleForecast::Perturbed; } else if (name == "initial-conditions-perturbations") { return TypeOfEnsembleForecast::InitialConditionsPerturbations; } else if (name == "model-physics-perturbations") { return TypeOfEnsembleForecast::ModelPhysicsPerturbations; } else if (name == "initial-and-model-physics-perturbations") { return TypeOfEnsembleForecast::InitialAndModelPhysicsPerturbations; } else if (name == "missing") { return TypeOfEnsembleForecast::Missing; } else { throw Mars2GribTableException("Invalid TypeOfEnsembleForecast name: '" + name + "'", Here()); } } /// /// @brief Convert a numeric GRIB code to `TypeOfEnsembleForecast`. /// /// This function validates and converts a raw numeric value associated /// with **GRIB2 Code Table 4.6 – Type of ensemble forecast** into the /// corresponding `TypeOfEnsembleForecast` enumeration. /// /// Only officially defined GRIB values are accepted. Any other value /// is considered invalid and results in an exception. /// /// @param[in] value Numeric GRIB code /// /// @return Corresponding `TypeOfEnsembleForecast` enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the provided value does not correspond to a valid /// GRIB Code Table 4.6 entry. /// /// @note /// - The value `255` corresponds to the GRIB *missing* value. /// - No implicit normalization, fallback, or defaulting is performed. /// /// @important /// This function performs **pure table validation**. /// Any semantic interpretation must be handled by the calling deduction. /// /// @todo [owner: mival,dgov][scope: tables][reason: correctness][prio: medium] /// - Replace this hard-coded mapping with code generated directly from /// ecCodes GRIB code tables. /// inline TypeOfEnsembleForecast long2enum_TypeOfEnsembleForecast_or_throw(long value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case 0: return TypeOfEnsembleForecast::UnperturbedHighResControl; case 1: return TypeOfEnsembleForecast::UnperturbedLowResControl; case 2: return TypeOfEnsembleForecast::NegativelyPerturbed; case 3: return TypeOfEnsembleForecast::PositivelyPerturbed; case 4: return TypeOfEnsembleForecast::MultiModel; case 5: return TypeOfEnsembleForecast::Unperturbed; case 6: return TypeOfEnsembleForecast::Perturbed; case 7: return TypeOfEnsembleForecast::InitialConditionsPerturbations; case 8: return TypeOfEnsembleForecast::ModelPhysicsPerturbations; case 9: return TypeOfEnsembleForecast::InitialAndModelPhysicsPerturbations; case 255: return TypeOfEnsembleForecast::Missing; default: throw Mars2GribTableException("Invalid TypeOfEnsembleForecast numeric value: actual='" + std::to_string(value) + "', expected={0..9,255}", Here()); } } } // namespace metkit::mars2grib::backend::tablesmetkit-1.19.2/src/metkit/mars2grib/backend/tables/shapeOfTheReferenceSystem.h0000664000175000017500000001774015232567717027355 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief Shape of the reference system. /// /// This enumeration represents GRIB code values defining the geometric /// model of the Earth (or Sun) and the associated reference system. /// /// The numeric values map **directly** to ecCodes GRIB table 3.2 /// and must not be changed manually. /// /// @note /// The value `255` corresponds to the GRIB *missing* value. /// /// @important /// This enum is a **GRIB-table representation only**. /// No policy, defaulting, or deduction logic belongs here. /// /// @todo [owner: mival,dgov][scope: tables][reason: correctness][prio: medium] /// - Generate this enum and all conversion helpers automatically from /// ecCodes definitions at build time. /// enum class ShapeOfTheReferenceSystem : long { EarthSphericalRadius6367470 = 0, EarthSphericalRadiusSpecified = 1, EarthOblateIAU1965 = 2, EarthOblateAxesKmSpecified = 3, EarthOblateIAGGRS80 = 4, EarthWGS84 = 5, EarthSphericalRadius6371229 = 6, EarthOblateAxesMetersSpecified = 7, EarthSphericalRadius6371200WGS84Datum = 8, EarthOSGB1936Airy1830 = 9, EarthWGS84CorrectedGeomagnetic = 10, SunSphericalStonyhurst = 11, Missing = 255 }; /// /// @brief Convert `ShapeOfTheReferenceSystem` to its canonical name. /// /// @param[in] value Enumeration value /// /// @return Canonical string name /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enumeration value is invalid. /// inline std::string enum2name_ShapeOfTheReferenceSystem_or_throw(ShapeOfTheReferenceSystem value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case ShapeOfTheReferenceSystem::EarthSphericalRadius6367470: return "earth-spherical-radius-6367470"; case ShapeOfTheReferenceSystem::EarthSphericalRadiusSpecified: return "earth-spherical-radius-specified"; case ShapeOfTheReferenceSystem::EarthOblateIAU1965: return "earth-oblate-iau-1965"; case ShapeOfTheReferenceSystem::EarthOblateAxesKmSpecified: return "earth-oblate-axes-km-specified"; case ShapeOfTheReferenceSystem::EarthOblateIAGGRS80: return "earth-oblate-iag-grs80"; case ShapeOfTheReferenceSystem::EarthWGS84: return "earth-wgs84"; case ShapeOfTheReferenceSystem::EarthSphericalRadius6371229: return "earth-spherical-radius-6371229"; case ShapeOfTheReferenceSystem::EarthOblateAxesMetersSpecified: return "earth-oblate-axes-m-specified"; case ShapeOfTheReferenceSystem::EarthSphericalRadius6371200WGS84Datum: return "earth-spherical-radius-6371200-wgs84-datum"; case ShapeOfTheReferenceSystem::EarthOSGB1936Airy1830: return "earth-osgb1936-airy1830"; case ShapeOfTheReferenceSystem::EarthWGS84CorrectedGeomagnetic: return "earth-wgs84-corrected-geomagnetic"; case ShapeOfTheReferenceSystem::SunSphericalStonyhurst: return "sun-spherical-stonyhurst"; case ShapeOfTheReferenceSystem::Missing: return "missing"; default: throw Mars2GribTableException("Invalid ShapeOfTheReferenceSystem enum value", Here()); } } /// /// @brief Convert a canonical name to `ShapeOfTheReferenceSystem`. /// /// @param[in] name Canonical string name /// /// @return Corresponding enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the name is not recognized. /// inline ShapeOfTheReferenceSystem name2enum_ShapeOfTheReferenceSystem_or_throw(const std::string& name) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (name == "earth-spherical-radius-6367470") return ShapeOfTheReferenceSystem::EarthSphericalRadius6367470; else if (name == "earth-spherical-radius-specified") return ShapeOfTheReferenceSystem::EarthSphericalRadiusSpecified; else if (name == "earth-oblate-iau-1965") return ShapeOfTheReferenceSystem::EarthOblateIAU1965; else if (name == "earth-oblate-axes-km-specified") return ShapeOfTheReferenceSystem::EarthOblateAxesKmSpecified; else if (name == "earth-oblate-iag-grs80") return ShapeOfTheReferenceSystem::EarthOblateIAGGRS80; else if (name == "earth-wgs84") return ShapeOfTheReferenceSystem::EarthWGS84; else if (name == "earth-spherical-radius-6371229") return ShapeOfTheReferenceSystem::EarthSphericalRadius6371229; else if (name == "earth-oblate-axes-m-specified") return ShapeOfTheReferenceSystem::EarthOblateAxesMetersSpecified; else if (name == "earth-spherical-radius-6371200-wgs84-datum") return ShapeOfTheReferenceSystem::EarthSphericalRadius6371200WGS84Datum; else if (name == "earth-osgb1936-airy1830") return ShapeOfTheReferenceSystem::EarthOSGB1936Airy1830; else if (name == "earth-wgs84-corrected-geomagnetic") return ShapeOfTheReferenceSystem::EarthWGS84CorrectedGeomagnetic; else if (name == "sun-spherical-stonyhurst") return ShapeOfTheReferenceSystem::SunSphericalStonyhurst; else if (name == "missing") return ShapeOfTheReferenceSystem::Missing; else throw Mars2GribTableException("Invalid ShapeOfTheReferenceSystem name: '" + name + "'", Here()); } /// /// @brief Convert a numeric GRIB code to `ShapeOfTheReferenceSystem`. /// /// @param[in] value Numeric GRIB code /// /// @return Corresponding enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the value is not defined in the GRIB table. /// inline ShapeOfTheReferenceSystem long2enum_ShapeOfTheReferenceSystem_or_throw(long value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case 0: return ShapeOfTheReferenceSystem::EarthSphericalRadius6367470; case 1: return ShapeOfTheReferenceSystem::EarthSphericalRadiusSpecified; case 2: return ShapeOfTheReferenceSystem::EarthOblateIAU1965; case 3: return ShapeOfTheReferenceSystem::EarthOblateAxesKmSpecified; case 4: return ShapeOfTheReferenceSystem::EarthOblateIAGGRS80; case 5: return ShapeOfTheReferenceSystem::EarthWGS84; case 6: return ShapeOfTheReferenceSystem::EarthSphericalRadius6371229; case 7: return ShapeOfTheReferenceSystem::EarthOblateAxesMetersSpecified; case 8: return ShapeOfTheReferenceSystem::EarthSphericalRadius6371200WGS84Datum; case 9: return ShapeOfTheReferenceSystem::EarthOSGB1936Airy1830; case 10: return ShapeOfTheReferenceSystem::EarthWGS84CorrectedGeomagnetic; case 11: return ShapeOfTheReferenceSystem::SunSphericalStonyhurst; case 255: return ShapeOfTheReferenceSystem::Missing; default: throw Mars2GribTableException("Invalid ShapeOfTheReferenceSystem numeric value: actual='" + std::to_string(value) + "', expected={0..11,255}", Here()); } } } // namespace metkit::mars2grib::backend::tables metkit-1.19.2/src/metkit/mars2grib/backend/tables/significanceOfReferenceTime.h0000664000175000017500000001457715232567717027655 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief GRIB significance of reference time. /// /// This enumeration represents the GRIB code table values associated with /// the `significanceOfReferenceTime` key in the Product Definition Section. /// /// The significance describes the semantic meaning of the reference time /// used in the GRIB message (e.g. analysis time, forecast start time, /// observation time). /// /// The numeric values map **directly** to the GRIB Code Table /// “Significance of reference time”. /// /// @important /// This enum is a **pure GRIB-level representation**. /// It does not encode policy decisions or deduction logic. /// All semantic resolution must be implemented in dedicated deduction /// functions. /// /// @section Source of truth /// GRIB2 Code Table 1.2: /// Significance of reference time /// /// @todo [owner: mival,mds,dgov][scope: tables][reason: correctness][prio: critical] /// - Generate this enumeration automatically from ecCodes definitions /// at build or configure time (e.g. via a Python code-generation step). /// - Avoid manual duplication of GRIB tables to prevent semantic drift /// between mars2grib and ecCodes. /// /// @note /// There is currently no known ecCodes API to set or retrieve these values /// via symbolic names; numeric values must therefore be used. /// enum class SignificanceOfReferenceTime : long { Analysis = 0, ForecastStart = 1, ForecastVerification = 2, ObservationTime = 3, LocalTime = 4, SimulationStart = 5, AssimilationStart = 6, Missing = 255 }; /// /// @brief Map a canonical string identifier to `SignificanceOfReferenceTime`. /// /// This function converts a canonical string representation into the /// corresponding GRIB `SignificanceOfReferenceTime` enumeration value. /// /// The mapping is explicit and strict. /// /// Supported identifiers: /// - `"analysis"` /// - `"forecastStart"` /// - `"forecastVerification"` /// - `"observationTime"` /// - `"localTime"` /// - `"simulationStart"` /// - `"missing"` /// /// @param[in] value Canonical string identifier /// /// @return Corresponding `SignificanceOfReferenceTime` enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the provided string is not supported. /// /// @note /// - This is a **pure table mapping**. /// - No normalization, fallback, or defaulting is performed. /// /// @todo [owner: mival,mds,dgov][scope: tables][reason: correctness][prio: medium] /// - Replace this hard-coded mapping with code generated directly from /// ecCodes GRIB code tables. /// inline SignificanceOfReferenceTime name2enum_SignificanceOfReferenceTime_or_throw(const std::string& value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (value == "analysis") return SignificanceOfReferenceTime::Analysis; else if (value == "forecastStart") return SignificanceOfReferenceTime::ForecastStart; else if (value == "forecastVerification") return SignificanceOfReferenceTime::ForecastVerification; else if (value == "observationTime") return SignificanceOfReferenceTime::ObservationTime; else if (value == "localTime") return SignificanceOfReferenceTime::LocalTime; else if (value == "simulationStart") return SignificanceOfReferenceTime::SimulationStart; else if (value == "assimilationStart") return SignificanceOfReferenceTime::SimulationStart; else if (value == "missing") return SignificanceOfReferenceTime::Missing; else { std::string errMsg = "Invalid SignificanceOfReferenceTime value: "; errMsg += "actual='" + value + "'"; throw Mars2GribTableException(errMsg, Here()); } mars2gribUnreachable(); } /// /// @brief Convert `SignificanceOfReferenceTime` to its canonical string identifier. /// /// This function converts a GRIB-level `SignificanceOfReferenceTime` /// enumeration value into its canonical string representation. /// /// @param[in] value GRIB `SignificanceOfReferenceTime` enumeration value /// /// @return Canonical string identifier /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enumeration value is not supported. /// /// @note /// - This function is the strict inverse of /// `mapto_SignificanceOfReferenceTime_or_throw(const std::string&)`. /// /// @todo [owner: mival,mds,dgov][scope: tables][reason: correctness][prio: medium] /// - Replace this hard-coded mapping with code generated directly from /// ecCodes GRIB code tables. /// inline std::string enum2name_SignificanceOfReferenceTime_or_throw(SignificanceOfReferenceTime value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case SignificanceOfReferenceTime::Analysis: return "analysis"; case SignificanceOfReferenceTime::ForecastStart: return "forecastStart"; case SignificanceOfReferenceTime::ForecastVerification: return "forecastVerification"; case SignificanceOfReferenceTime::ObservationTime: return "observationTime"; case SignificanceOfReferenceTime::LocalTime: return "localTime"; case SignificanceOfReferenceTime::SimulationStart: return "simulationStart"; case SignificanceOfReferenceTime::AssimilationStart: return "assimilationStart"; case SignificanceOfReferenceTime::Missing: return "missing"; default: { std::string errMsg = "Invalid SignificanceOfReferenceTime enum value: "; errMsg += std::to_string(static_cast(value)); throw Mars2GribTableException(errMsg, Here()); } } mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::tablesmetkit-1.19.2/src/metkit/mars2grib/backend/tables/typeOfStatisticalProcessing.h0000664000175000017500000001630715232567717030011 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief GRIB Table 4.10 – Type of statistical processing. /// /// This enumeration represents the GRIB code values used to describe /// the statistical processing applied over a time range. /// /// @section Source of truth /// GRIB2 Code Table 4.10 (ecCodes) /// /// @note /// - Numeric values map **directly** to GRIB code table entries. /// - Value `255` corresponds to GRIB *missing*. /// /// @todo [owner: mds,dgov][scope: tables][reason: correctness][prio: critical] /// - Generate this enum and its mappings automatically from ecCodes /// definitions at build time to avoid divergence. /// enum class TypeOfStatisticalProcessing : long { Average = 0, Accumulation = 1, Maximum = 2, Minimum = 3, DifferenceEndMinusStart = 4, RootMeanSquare = 5, StandardDeviation = 6, Covariance = 7, DifferenceStartMinusEnd = 8, Ratio = 9, StandardizedAnomaly = 10, Summation = 11, ReturnPeriod = 12, Median = 13, Severity = 100, Mode = 101, IndexProcessing = 102, Missing = 255 }; /// /// @brief Convert a symbolic name to a GRIB TypeOfStatisticalProcessing. /// /// Supported names: /// /// - "average" /// - "accumulation" /// - "maximum" /// - "minimum" /// - "difference_end_minus_start" /// - "root_mean_square" /// - "standard_deviation" /// - "covariance" /// - "difference_start_minus_end" /// - "ratio" /// - "standardized_anomaly" /// - "summation" /// - "return_period" /// - "median" /// - "severity" /// - "mode" /// - "index_processing" /// - "missing" /// /// @param[in] name Canonical symbolic name /// /// @return Corresponding `TypeOfStatisticalProcessing` /// /// @throws Mars2GribTableException /// If the name is not supported /// /// @note /// - Case-sensitive /// - No aliasing or normalization /// inline TypeOfStatisticalProcessing name2enum_TypeOfStatisticalProcessing_or_throw(const std::string& name) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (name == "average") return TypeOfStatisticalProcessing::Average; if (name == "accumulation") return TypeOfStatisticalProcessing::Accumulation; if (name == "maximum") return TypeOfStatisticalProcessing::Maximum; if (name == "minimum") return TypeOfStatisticalProcessing::Minimum; if (name == "difference_end_minus_start") return TypeOfStatisticalProcessing::DifferenceEndMinusStart; if (name == "root_mean_square") return TypeOfStatisticalProcessing::RootMeanSquare; if (name == "standard_deviation") return TypeOfStatisticalProcessing::StandardDeviation; if (name == "covariance") return TypeOfStatisticalProcessing::Covariance; if (name == "difference_start_minus_end") return TypeOfStatisticalProcessing::DifferenceStartMinusEnd; if (name == "ratio") return TypeOfStatisticalProcessing::Ratio; if (name == "standardized_anomaly") return TypeOfStatisticalProcessing::StandardizedAnomaly; if (name == "summation") return TypeOfStatisticalProcessing::Summation; if (name == "return_period") return TypeOfStatisticalProcessing::ReturnPeriod; if (name == "median") return TypeOfStatisticalProcessing::Median; if (name == "severity") return TypeOfStatisticalProcessing::Severity; if (name == "mode") return TypeOfStatisticalProcessing::Mode; if (name == "index_processing") return TypeOfStatisticalProcessing::IndexProcessing; if (name == "missing") return TypeOfStatisticalProcessing::Missing; std::string err = "Invalid TypeOfStatisticalProcessing name: actual='" + name + "', expected={average,accumulation,maximum,minimum," "difference_end_minus_start,root_mean_square,standard_deviation," "covariance,difference_start_minus_end,ratio,standardized_anomaly," "summation,return_period,median,severity,mode,index_processing,missing}"; throw Mars2GribTableException(err, Here()); mars2gribUnreachable(); } /// /// @brief Convert a GRIB TypeOfStatisticalProcessing to its symbolic name. /// /// @param[in] value GRIB statistical processing code /// /// @return Canonical symbolic name /// /// @throws Mars2GribTableException /// If the enum value is not supported /// /// @note /// - Returned strings are stable and suitable for round-tripping /// inline std::string enum2name_TypeOfStatisticalProcessing_or_throw(TypeOfStatisticalProcessing value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case TypeOfStatisticalProcessing::Average: return "average"; case TypeOfStatisticalProcessing::Accumulation: return "accumulation"; case TypeOfStatisticalProcessing::Maximum: return "maximum"; case TypeOfStatisticalProcessing::Minimum: return "minimum"; case TypeOfStatisticalProcessing::DifferenceEndMinusStart: return "difference_end_minus_start"; case TypeOfStatisticalProcessing::RootMeanSquare: return "root_mean_square"; case TypeOfStatisticalProcessing::StandardDeviation: return "standard_deviation"; case TypeOfStatisticalProcessing::Covariance: return "covariance"; case TypeOfStatisticalProcessing::DifferenceStartMinusEnd: return "difference_start_minus_end"; case TypeOfStatisticalProcessing::Ratio: return "ratio"; case TypeOfStatisticalProcessing::StandardizedAnomaly: return "standardized_anomaly"; case TypeOfStatisticalProcessing::Summation: return "summation"; case TypeOfStatisticalProcessing::ReturnPeriod: return "return_period"; case TypeOfStatisticalProcessing::Median: return "median"; case TypeOfStatisticalProcessing::Severity: return "severity"; case TypeOfStatisticalProcessing::Mode: return "mode"; case TypeOfStatisticalProcessing::IndexProcessing: return "index_processing"; case TypeOfStatisticalProcessing::Missing: return "missing"; } std::string err = "Invalid TypeOfStatisticalProcessing enum value: actual='" + std::to_string(static_cast(value)) + "'"; throw Mars2GribTableException(err, Here()); mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::tablesmetkit-1.19.2/src/metkit/mars2grib/backend/tables/typeOfProcessedData.h0000664000175000017500000002270115232567717026204 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief GRIB classification of processed data products. /// /// This enumeration represents the GRIB code table values associated with /// *Type of processed data* (GRIB2, Code Table 1.4). /// /// Each enumerator describes the nature of the data contained in the GRIB /// message, distinguishing between analysis, forecast, ensemble components, /// observational products, and derived or experimental datasets. /// /// The numeric values of the enumerators map **directly** to the GRIB /// code table values and must not be changed manually. /// /// @important /// This enum is a **GRIB-level representation**, not a policy decision. /// All semantic validation, defaulting, and deduction logic must be handled /// in the corresponding deduction layer. /// /// @note /// The value `255` corresponds to the GRIB *missing* value. /// /// @section Source of truth /// The authoritative definition of this table is maintained by WMO / ecCodes: /// /// GRIB2 — Code Table 1.4 (Type of processed data) /// /// @todo [owner: mds,dgov][scope: tables][reason: correctness][prio: medium] /// - Generate this enumeration automatically from ecCodes GRIB tables /// at build or configure time to avoid divergence across software stacks. /// enum class TypeOfProcessedData : long { AnalysisProducts = 0, ForecastProducts = 1, AnalysisAndForecastProducts = 2, ControlForecastProducts = 3, PerturbedForecastProducts = 4, ControlAndPerturbedForecastProducts = 5, ProcessedSatelliteObservations = 6, ProcessedRadarObservations = 7, EventProbability = 8, ExperimentalData = 9, MlBasedForecast = 10, Missing = 255 }; /// /// @brief Convert a symbolic name to `TypeOfProcessedData`. /// /// This function maps a string identifier to the corresponding /// `TypeOfProcessedData` enumeration value. /// /// The mapping is explicit and strict. Only supported names are accepted. /// /// @param[in] value Symbolic name of the processed data type /// /// @return Corresponding `TypeOfProcessedData` enumerator /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the provided name is not supported. /// /// @note /// - No normalization or fallback is performed. /// - Intended for configuration, testing, and diagnostics. /// inline TypeOfProcessedData name2enum_TypeOfProcessedData_or_throw(const std::string& value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (value == "an") return TypeOfProcessedData::AnalysisProducts; if (value == "fc") return TypeOfProcessedData::ForecastProducts; if (value == "af") return TypeOfProcessedData::AnalysisAndForecastProducts; if (value == "cf") return TypeOfProcessedData::ControlForecastProducts; if (value == "pf") return TypeOfProcessedData::PerturbedForecastProducts; if (value == "cp") return TypeOfProcessedData::ControlAndPerturbedForecastProducts; if (value == "sa") return TypeOfProcessedData::ProcessedSatelliteObservations; if (value == "ra") return TypeOfProcessedData::ProcessedRadarObservations; if (value == "ep") return TypeOfProcessedData::EventProbability; if (value == "9") return TypeOfProcessedData::ExperimentalData; if (value == "10") return TypeOfProcessedData::MlBasedForecast; if (value == "missing") return TypeOfProcessedData::Missing; std::string errMsg = "Invalid TypeOfProcessedData name: "; errMsg += "actual='" + value + "'"; throw Mars2GribTableException(errMsg, Here()); mars2gribUnreachable(); } /// /// @brief Map a numeric GRIB value to `TypeOfProcessedData`. /// /// This function validates and converts a raw numeric GRIB value /// associated with the `typeOfProcessedData` key into the corresponding /// `TypeOfProcessedData` enumeration. /// /// The mapping is **explicit and strict**. Only numeric values defined by /// GRIB2 Code Table 1.4 and supported by this encoder are accepted. /// Any other value is considered invalid and results in an exception. /// /// @section Accepted values /// The following mappings are supported: /// /// - `0` → `AnalysisProducts` /// - `1` → `ForecastProducts` /// - `2` → `AnalysisAndForecastProducts` /// - `3` → `ControlForecastProducts` /// - `4` → `PerturbedForecastProducts` /// - `5` → `ControlAndPerturbedForecastProducts` /// - `6` → `ProcessedSatelliteObservations` /// - `7` → `ProcessedRadarObservations` /// - `8` → `EventProbability` /// - `9` → `ExperimentalData` /// - `10` → `MlBasedForecast` /// - `255` → `Missing` /// /// @param[in] value Raw numeric GRIB value to be validated and mapped /// /// @return The corresponding `TypeOfProcessedData` enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the provided numeric value does not correspond to a supported /// GRIB code. /// /// @note /// - This function performs **no deduction** and **no defaulting**. /// - It must not be used to infer semantics from MARS metadata. /// - It is intended for validation of existing GRIB state or explicit overrides. /// /// @important /// This function is part of the **tables layer**. /// Policy decisions and semantic deductions must be implemented elsewhere. /// /// @section Source of truth /// WMO GRIB2 Code Table 1.4 – Type of processed data. /// /// @todo [owner: mival][scope: tables][reason: correctness][prio: medium] /// - Replace this hard-coded mapping with code generated automatically /// from ecCodes GRIB tables to prevent divergence between software stacks. /// inline TypeOfProcessedData long2enum_TypeOfProcessedData_or_throw(long value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case 0: return TypeOfProcessedData::AnalysisProducts; case 1: return TypeOfProcessedData::ForecastProducts; case 2: return TypeOfProcessedData::AnalysisAndForecastProducts; case 3: return TypeOfProcessedData::ControlForecastProducts; case 4: return TypeOfProcessedData::PerturbedForecastProducts; case 5: return TypeOfProcessedData::ControlAndPerturbedForecastProducts; case 6: return TypeOfProcessedData::ProcessedSatelliteObservations; case 7: return TypeOfProcessedData::ProcessedRadarObservations; case 8: return TypeOfProcessedData::EventProbability; case 9: return TypeOfProcessedData::ExperimentalData; case 10: return TypeOfProcessedData::MlBasedForecast; case 255: return TypeOfProcessedData::Missing; default: throw Mars2GribTableException("Invalid GRIB value for `typeOfProcessedData`: " + std::to_string(value), Here()); } mars2gribUnreachable(); } /// /// @brief Convert `TypeOfProcessedData` to a symbolic name. /// /// This function maps a `TypeOfProcessedData` enumeration value to its /// canonical string representation. /// /// @param[in] value Enumeration value /// /// @return Canonical string name /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the value is not recognized. /// /// @note /// - Intended for logging, debugging, and diagnostics. /// - The returned names are stable identifiers, not user-facing text. /// inline std::string enum2name_TypeOfProcessedData_or_throw(TypeOfProcessedData value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case TypeOfProcessedData::AnalysisProducts: return "an"; case TypeOfProcessedData::ForecastProducts: return "fc"; case TypeOfProcessedData::AnalysisAndForecastProducts: return "af"; case TypeOfProcessedData::ControlForecastProducts: return "cf"; case TypeOfProcessedData::PerturbedForecastProducts: return "pf"; case TypeOfProcessedData::ControlAndPerturbedForecastProducts: return "cp"; case TypeOfProcessedData::ProcessedSatelliteObservations: return "sa"; case TypeOfProcessedData::ProcessedRadarObservations: return "ra"; case TypeOfProcessedData::EventProbability: return "ep"; case TypeOfProcessedData::ExperimentalData: return "9"; case TypeOfProcessedData::MlBasedForecast: return "10"; case TypeOfProcessedData::Missing: return "missing"; } throw Mars2GribTableException("Invalid TypeOfProcessedData enum value", Here()); } } // namespace metkit::mars2grib::backend::tablesmetkit-1.19.2/src/metkit/mars2grib/backend/tables/derivedForecast.h0000664000175000017500000001543715232567717025415 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief Ensemble statistical processing type. /// /// This enumeration represents GRIB code values defining how ensemble /// members are statistically processed to produce the encoded field. /// /// The numeric values map **directly** to the GRIB code table definitions /// and must not be changed manually. /// /// @note /// The value `255` corresponds to the GRIB *missing* value. /// /// @important /// This enum is a **GRIB-table representation only**. /// No policy, defaulting, or deduction logic belongs here. /// /// @todo [owner: mival,dgov][scope: tables][reason: correctness][prio: medium] /// - Generate this enum and all conversion helpers automatically from /// ecCodes definitions at build time. /// enum class DerivedForecast : long { UnweightedMeanAllMembers = 0, WeightedMeanAllMembers = 1, StdDevClusterMean = 2, StdDevClusterMeanNormalized = 3, SpreadAllMembers = 4, LargeAnomalyIndexAllMembers = 5, UnweightedMeanClusterMembers = 6, InterquartileRange = 7, MinimumAllMembers = 8, MaximumAllMembers = 9, VarianceAllMembers = 10, Missing = 255 }; /// /// @brief Convert `DerivedForecast` to its canonical name. /// /// @param[in] value Enumeration value /// /// @return Canonical string name /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enumeration value is invalid. /// inline std::string enum2name_DerivedForecast_or_throw(DerivedForecast value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case DerivedForecast::UnweightedMeanAllMembers: return "unweighted-mean-all-members"; case DerivedForecast::WeightedMeanAllMembers: return "weighted-mean-all-members"; case DerivedForecast::StdDevClusterMean: return "stddev-cluster-mean"; case DerivedForecast::StdDevClusterMeanNormalized: return "stddev-cluster-mean-normalized"; case DerivedForecast::SpreadAllMembers: return "spread-all-members"; case DerivedForecast::LargeAnomalyIndexAllMembers: return "large-anomaly-index-all-members"; case DerivedForecast::UnweightedMeanClusterMembers: return "unweighted-mean-cluster-members"; case DerivedForecast::InterquartileRange: return "interquartile-range"; case DerivedForecast::MinimumAllMembers: return "minimum-all-members"; case DerivedForecast::MaximumAllMembers: return "maximum-all-members"; case DerivedForecast::VarianceAllMembers: return "variance-all-members"; case DerivedForecast::Missing: return "missing"; default: throw Mars2GribTableException("Invalid DerivedForecast enum value", Here()); } } /// /// @brief Convert a canonical name to `DerivedForecast`. /// /// @param[in] name Canonical string name /// /// @return Corresponding enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the name is not recognized. /// inline DerivedForecast name2enum_DerivedForecast_or_throw(const std::string& name) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (name == "unweighted-mean-all-members") return DerivedForecast::UnweightedMeanAllMembers; else if (name == "weighted-mean-all-members") return DerivedForecast::WeightedMeanAllMembers; else if (name == "stddev-cluster-mean") return DerivedForecast::StdDevClusterMean; else if (name == "stddev-cluster-mean-normalized") return DerivedForecast::StdDevClusterMeanNormalized; else if (name == "spread-all-members") return DerivedForecast::SpreadAllMembers; else if (name == "large-anomaly-index-all-members") return DerivedForecast::LargeAnomalyIndexAllMembers; else if (name == "unweighted-mean-cluster-members") return DerivedForecast::UnweightedMeanClusterMembers; else if (name == "interquartile-range") return DerivedForecast::InterquartileRange; else if (name == "minimum-all-members") return DerivedForecast::MinimumAllMembers; else if (name == "maximum-all-members") return DerivedForecast::MaximumAllMembers; else if (name == "variance-all-members") return DerivedForecast::VarianceAllMembers; else if (name == "missing") return DerivedForecast::Missing; else throw Mars2GribTableException("Invalid DerivedForecast name: '" + name + "'", Here()); } /// /// @brief Convert a numeric GRIB code to `DerivedForecast`. /// /// @param[in] value Numeric GRIB code /// /// @return Corresponding enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the value is not defined in the GRIB table. /// inline DerivedForecast long2enum_DerivedForecast_or_throw(long value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case 0: return DerivedForecast::UnweightedMeanAllMembers; case 1: return DerivedForecast::WeightedMeanAllMembers; case 2: return DerivedForecast::StdDevClusterMean; case 3: return DerivedForecast::StdDevClusterMeanNormalized; case 4: return DerivedForecast::SpreadAllMembers; case 5: return DerivedForecast::LargeAnomalyIndexAllMembers; case 6: return DerivedForecast::UnweightedMeanClusterMembers; case 7: return DerivedForecast::InterquartileRange; case 8: return DerivedForecast::MinimumAllMembers; case 9: return DerivedForecast::MaximumAllMembers; case 10: return DerivedForecast::VarianceAllMembers; case 255: return DerivedForecast::Missing; default: throw Mars2GribTableException( "Invalid DerivedForecast numeric value: actual='" + std::to_string(value) + "', expected={0..10,255}", Here()); } } } // namespace metkit::mars2grib::backend::tablesmetkit-1.19.2/src/metkit/mars2grib/backend/tables/productionStatusOfProcessedData.h0000664000175000017500000002134115232567717030614 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief GRIB Production Status of Processed Data. /// /// This enumeration represents GRIB Code Table values describing the /// *production status* of the processed data, as encoded in the /// Product Definition Section. /// /// The values distinguish between operational, test, research, /// reanalysis, and programme-specific data streams (e.g. TIGGE, S2S, /// Copernicus, Destination Earth). /// /// The numeric values map **directly** to GRIB code table entries and /// must not be modified manually. /// /// @section Source of truth /// The authoritative definition of these values is maintained in /// the GRIB code tables distributed with ecCodes. /// /// @todo [owner: mival][scope: tables][reason: correctness][prio: high] /// - Generate this enumeration and all associated mappings /// automatically from ecCodes GRIB code tables at build time. /// - This avoids silent divergence between the encoder and the /// ecCodes version used at runtime. /// enum class ProductionStatusOfProcessedData : long { OperationalProducts = 0, OperationalTestProducts = 1, ResearchProducts = 2, ReanalysisProducts = 3, TiggeOperational = 4, TiggeTest = 5, S2SOperationalProducts = 6, S2STestProducts = 7, UerraOperational = 8, UerraTest = 9, CopernicusRegionalReanalysis = 10, CopernicusRegionalReanalysisTest = 11, DestinationEarth = 12, DestinationEarthTest = 13, Missing = 255 }; /// /// @brief Convert `ProductionStatusOfProcessedData` to its symbolic name. /// /// @param[in] value Enumeration value /// /// @return Canonical symbolic name corresponding to the enum value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enumeration value is not recognised /// inline std::string enum2name_ProductionStatusOfProcessedData_or_throw(ProductionStatusOfProcessedData value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case ProductionStatusOfProcessedData::OperationalProducts: return "OperationalProducts"; case ProductionStatusOfProcessedData::OperationalTestProducts: return "OperationalTestProducts"; case ProductionStatusOfProcessedData::ResearchProducts: return "ResearchProducts"; case ProductionStatusOfProcessedData::ReanalysisProducts: return "ReanalysisProducts"; case ProductionStatusOfProcessedData::TiggeOperational: return "TiggeOperational"; case ProductionStatusOfProcessedData::TiggeTest: return "TiggeTest"; case ProductionStatusOfProcessedData::S2SOperationalProducts: return "S2SOperationalProducts"; case ProductionStatusOfProcessedData::S2STestProducts: return "S2STestProducts"; case ProductionStatusOfProcessedData::UerraOperational: return "UerraOperational"; case ProductionStatusOfProcessedData::UerraTest: return "UerraTest"; case ProductionStatusOfProcessedData::CopernicusRegionalReanalysis: return "CopernicusRegionalReanalysis"; case ProductionStatusOfProcessedData::CopernicusRegionalReanalysisTest: return "CopernicusRegionalReanalysisTest"; case ProductionStatusOfProcessedData::DestinationEarth: return "DestinationEarth"; case ProductionStatusOfProcessedData::DestinationEarthTest: return "DestinationEarthTest"; case ProductionStatusOfProcessedData::Missing: return "Missing"; default: throw Mars2GribTableException("Invalid ProductionStatusOfProcessedData enum value", Here()); } mars2gribUnreachable(); } /// /// @brief Convert a symbolic name to `ProductionStatusOfProcessedData`. /// /// @param[in] name Canonical symbolic name /// /// @return Corresponding `ProductionStatusOfProcessedData` enum value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the name is not recognised /// inline ProductionStatusOfProcessedData name2enum_ProductionStatusOfProcessedData_or_throw(const std::string& name) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (name == "OperationalProducts") return ProductionStatusOfProcessedData::OperationalProducts; if (name == "OperationalTestProducts") return ProductionStatusOfProcessedData::OperationalTestProducts; if (name == "ResearchProducts") return ProductionStatusOfProcessedData::ResearchProducts; if (name == "ReanalysisProducts") return ProductionStatusOfProcessedData::ReanalysisProducts; if (name == "TiggeOperational") return ProductionStatusOfProcessedData::TiggeOperational; if (name == "TiggeTest") return ProductionStatusOfProcessedData::TiggeTest; if (name == "S2SOperationalProducts") return ProductionStatusOfProcessedData::S2SOperationalProducts; if (name == "S2STestProducts") return ProductionStatusOfProcessedData::S2STestProducts; if (name == "UerraOperational") return ProductionStatusOfProcessedData::UerraOperational; if (name == "UerraTest") return ProductionStatusOfProcessedData::UerraTest; if (name == "CopernicusRegionalReanalysis") return ProductionStatusOfProcessedData::CopernicusRegionalReanalysis; if (name == "CopernicusRegionalReanalysisTest") return ProductionStatusOfProcessedData::CopernicusRegionalReanalysisTest; if (name == "DestinationEarth") return ProductionStatusOfProcessedData::DestinationEarth; if (name == "DestinationEarthTest") return ProductionStatusOfProcessedData::DestinationEarthTest; if (name == "Missing") return ProductionStatusOfProcessedData::Missing; throw Mars2GribTableException("Invalid ProductionStatusOfProcessedData name: '" + name + "'", Here()); mars2gribUnreachable(); } /// /// @brief Convert a numeric GRIB code to `ProductionStatusOfProcessedData`. /// /// @param[in] value Numeric GRIB code table value /// /// @return Corresponding `ProductionStatusOfProcessedData` enum value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the numeric value is not valid /// inline ProductionStatusOfProcessedData long2enum_ProductionStatusOfProcessedData_or_throw(long value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case 0: return ProductionStatusOfProcessedData::OperationalProducts; case 1: return ProductionStatusOfProcessedData::OperationalTestProducts; case 2: return ProductionStatusOfProcessedData::ResearchProducts; case 3: return ProductionStatusOfProcessedData::ReanalysisProducts; case 4: return ProductionStatusOfProcessedData::TiggeOperational; case 5: return ProductionStatusOfProcessedData::TiggeTest; case 6: return ProductionStatusOfProcessedData::S2SOperationalProducts; case 7: return ProductionStatusOfProcessedData::S2STestProducts; case 8: return ProductionStatusOfProcessedData::UerraOperational; case 9: return ProductionStatusOfProcessedData::UerraTest; case 10: return ProductionStatusOfProcessedData::CopernicusRegionalReanalysis; case 11: return ProductionStatusOfProcessedData::CopernicusRegionalReanalysisTest; case 12: return ProductionStatusOfProcessedData::DestinationEarth; case 13: return ProductionStatusOfProcessedData::DestinationEarthTest; case 255: return ProductionStatusOfProcessedData::Missing; default: throw Mars2GribTableException( "Invalid ProductionStatusOfProcessedData numeric value: " + std::to_string(value), Here()); } mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::tablesmetkit-1.19.2/src/metkit/mars2grib/backend/tables/typeOfTimeIntervals.h0000664000175000017500000001352615232567717026256 0ustar alastairalastair/* * (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. */ #pragma once #include #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::tables { /// /// @brief Type of time intervals. /// /// This enumeration represents GRIB code values defining how successive /// time intervals are processed in time-dependent fields. /// /// The numeric values map **directly** to ecCodes GRIB table 4.11 /// and must not be changed manually. /// /// @note /// The value `255` corresponds to the GRIB *missing* value. /// /// @important /// This enum is a **GRIB-table representation only**. /// No policy, defaulting, or deduction logic belongs here. /// /// @todo [owner: mival,dgov][scope: tables][reason: correctness][prio: medium] /// - Generate this enum and all conversion helpers automatically from /// ecCodes definitions at build time. /// enum class TypeOfTimeIntervals : long { Reserved = 0, SameForecastTimeStartIncremented = 1, SameStartTimeForecastIncremented = 2, StartIncrementedForecastDecrementedConstantValid = 3, StartDecrementedForecastIncrementedConstantValid = 4, FloatingSubinterval = 5, Missing = 255 }; /// /// @brief Convert `TypeOfTimeIntervals` to its canonical name. /// /// @param[in] value Enumeration value /// /// @return Canonical string name /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the enumeration value is invalid. /// inline std::string enum2name_TypeOfTimeIntervals_or_throw(TypeOfTimeIntervals value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case TypeOfTimeIntervals::Reserved: return "reserved"; case TypeOfTimeIntervals::SameForecastTimeStartIncremented: return "same-forecast-time-start-incremented"; case TypeOfTimeIntervals::SameStartTimeForecastIncremented: return "same-start-time-forecast-incremented"; case TypeOfTimeIntervals::StartIncrementedForecastDecrementedConstantValid: return "start-incremented-forecast-decremented-constant-valid"; case TypeOfTimeIntervals::StartDecrementedForecastIncrementedConstantValid: return "start-decremented-forecast-incremented-constant-valid"; case TypeOfTimeIntervals::FloatingSubinterval: return "floating-subinterval"; case TypeOfTimeIntervals::Missing: return "missing"; default: throw Mars2GribTableException("Invalid TypeOfTimeIntervals enum value", Here()); } } /// /// @brief Convert a canonical name to `TypeOfTimeIntervals`. /// /// @param[in] name Canonical string name /// /// @return Corresponding enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the name is not recognized. /// inline TypeOfTimeIntervals name2enum_TypeOfTimeIntervals_or_throw(const std::string& name) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; if (name == "reserved") return TypeOfTimeIntervals::Reserved; else if (name == "same-forecast-time-start-incremented") return TypeOfTimeIntervals::SameForecastTimeStartIncremented; else if (name == "same-start-time-forecast-incremented") return TypeOfTimeIntervals::SameStartTimeForecastIncremented; else if (name == "start-incremented-forecast-decremented-constant-valid") return TypeOfTimeIntervals::StartIncrementedForecastDecrementedConstantValid; else if (name == "start-decremented-forecast-incremented-constant-valid") return TypeOfTimeIntervals::StartDecrementedForecastIncrementedConstantValid; else if (name == "floating-subinterval") return TypeOfTimeIntervals::FloatingSubinterval; else if (name == "missing") return TypeOfTimeIntervals::Missing; else throw Mars2GribTableException("Invalid TypeOfTimeIntervals name: '" + name + "'", Here()); } /// /// @brief Convert a numeric GRIB code to `TypeOfTimeIntervals`. /// /// @param[in] value Numeric GRIB code /// /// @return Corresponding enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribTableException /// If the value is not defined in the GRIB table. /// inline TypeOfTimeIntervals long2enum_TypeOfTimeIntervals_or_throw(long value) { using metkit::mars2grib::utils::exceptions::Mars2GribTableException; switch (value) { case 0: return TypeOfTimeIntervals::Reserved; case 1: return TypeOfTimeIntervals::SameForecastTimeStartIncremented; case 2: return TypeOfTimeIntervals::SameStartTimeForecastIncremented; case 3: return TypeOfTimeIntervals::StartIncrementedForecastDecrementedConstantValid; case 4: return TypeOfTimeIntervals::StartDecrementedForecastIncrementedConstantValid; case 5: return TypeOfTimeIntervals::FloatingSubinterval; case 255: return TypeOfTimeIntervals::Missing; default: throw Mars2GribTableException("Invalid TypeOfTimeIntervals numeric value: actual='" + std::to_string(value) + "', expected={0..5,255}", Here()); } } } // namespace metkit::mars2grib::backend::tables metkit-1.19.2/src/metkit/mars2grib/backend/deductions/0000775000175000017500000000000015232567717023010 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/deductions/offsetToEndOf4DvarWindow.h0000664000175000017500000001037515232567717027765 0ustar alastairalastair/* * (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 offsetToEndOf4DvarWindow.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["anoffset"]` /// - 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 `anoffset`. /// /// @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 `anoffset` 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_offsetToEndOf4DvarWindow_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 anoffset auto offsetToEndOf4DvarWindow = get_or_throw(mars, "anoffset"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`offsetToEndOf4DvarWindow` resolved from input dictionaries: value='"; logMsg += std::to_string(offsetToEndOf4DvarWindow) + "'"; return logMsg; }()); // Success exit point return offsetToEndOf4DvarWindow; } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribDeductionException( "Failed to resolve `offsetToEndOf4DvarWindow` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/backgroundProcess.h0000664000175000017500000001510715232567717026643 0ustar alastairalastair/* * (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 backgroundProcess.h /// @brief Deduction of the GRIB `backgroundProcess` attribute. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **GRIB Background Process** from input dictionaries. /// /// The deduction maps the MARS model identifier to a GRIB-compliant /// `BackgroundProcess` value according to predefined, authoritative /// mapping rules. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying deterministic deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply semantic defaults beyond explicit rules /// - do NOT perform GRIB table validation /// /// Error handling follows a strict fail-fast strategy: /// - missing or unsupported 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 via deduction logic from input dictionaries /// - DEFAULT: value defaulted to a predefined constant due to missing input /// /// @section References /// Concept: /// - @ref generatingProcessEncoding.h /// /// Related deductions: /// - @ref generatingProcessIdentifier.h /// - @ref typeOfGeneratingProcess.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include // Tables #include "metkit/mars2grib/backend/tables/backgroundProcess.h" #include "metkit/mars2grib/utils/generalUtils.h" // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the GRIB `backgroundProcess` value from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["model"]` /// - Writes: none /// - Side effects: logging (RESOLVE or DEFAULT) /// - Failure mode: throws /// /// This deduction resolves the GRIB `backgroundProcess` by mapping the /// MARS model identifier to the corresponding /// `tables::BackgroundProcess` enumeration value. /// /// The mapping is explicit and strict: only supported model identifiers /// are accepted. Unsupported or unknown values result in an immediate /// deduction failure. /// If the key `model` is missing, a default value of "ifs" is used, /// which is then mapped to the corresponding `backgroundProcess`. /// /// This function acts as the single authoritative deduction point for /// `backgroundProcess`. All mapping rules and consistency checks for /// this GRIB key must be implemented here. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `model` /// and conversion to `std::string`. If the key `model` is missing, /// a default value of "ifs" is used. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the model identifier is read. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved GRIB `BackgroundProcess` enumeration value. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `model` cannot be mapped to a /// valid GRIB background process, or if any unexpected error occurs /// during deduction. /// /// @note /// This deduction is deterministic and does not rely on any /// pre-existing GRIB header state. /// template tables::BackgroundProcess resolve_BackgroundProcess_or_throw(const MarsDict_t& mars, [[maybe_unused]] const ParDict_t& par, [[maybe_unused]] 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 { if (has(mars, "model")) { // Retrieve mandatory MARS model identifier std::string marsModelVal = get_or_throw(mars, "model"); // Use IFS as backgroundProcess for NEMO and FESOM if (marsModelVal == "IFS-NEMO" || marsModelVal == "IFS-FESOM") { marsModelVal = "IFS"; } // Apply BackgroundProcess mapping logic tables::BackgroundProcess backgroundProcess = tables::name2enum_BackgroundProcess_or_throw(marsModelVal); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`backgroundProcess` resolved from input dictionaries: value='"; logMsg += tables::enum2name_BackgroundProcess_or_throw(backgroundProcess) + "'"; return logMsg; }()); // Success exit point return backgroundProcess; } else { // Retrieve mandatory MARS model identifier std::string marsModelVal = "IFS"; // Apply BackgroundProcess mapping logic tables::BackgroundProcess backgroundProcess = tables::name2enum_BackgroundProcess_or_throw(marsModelVal); // Emit DEFAULT log entry MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`backgroundProcess` defaulted to: value='"; logMsg += tables::enum2name_BackgroundProcess_or_throw(backgroundProcess) + "'"; return logMsg; }()); // Success exit point return backgroundProcess; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `backgroundProcess` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/waveFrequencyNumber.h0000664000175000017500000000767215232567717027172 0ustar alastairalastair/* * (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 waveFrequencyNumber.h /// @brief Deduction of the GRIB wave frequency number. /// /// This header defines the deduction responsible for resolving the /// wave frequency index used in spectral wave products. /// /// The deduction extracts the frequency index directly from the /// MARS dictionary and exposes it for use in GRIB encoding. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - validate semantic correctness of indices /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries /// /// @section References /// Concept: /// - @ref waveEncoding.h /// /// Related deductions: /// - @ref periodItMin.h /// - @ref periodItMax.h /// - @ref waveDirectionNumber.h /// - @ref waveFrequencyGrid.h /// - @ref waveFrequencyGrid.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 GRIB wave frequency number. /// /// This deduction resolves the wave frequency index required for /// spectral wave encoding by retrieving it from the MARS dictionary. /// /// The value is treated as mandatory and must be provided explicitly /// via the MARS key `frequency`. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary; must contain the key `frequency` /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return The resolved wave frequency number /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - the key `frequency` is missing from the MARS dictionary /// - the value cannot be converted to `long` /// - any unexpected error occurs during deduction /// template long resolve_WaveFrequencyNumber_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 wave frequency number from MARS dictionary auto waveFrequencyNumber = get_or_throw(mars, "frequency"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`waveFrequencyNumber` resolved from input dictionaries: value='"; logMsg += std::to_string(waveFrequencyNumber); logMsg += "'"; return logMsg; }()); // Success exit point return waveFrequencyNumber; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `waveFrequencyNumber` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/model.h0000664000175000017500000001057015232567717024264 0ustar alastairalastair/* * (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 model.h /// @brief Deduction of the MARS model identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **model identifier** from MARS metadata. /// /// The deduction retrieves the model identifier explicitly from the /// MARS dictionary and returns it verbatim. No inference, defaulting, /// normalization, or validation against GRIB tables is performed. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying deterministic deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT infer defaults /// - do NOT perform GRIB table validation /// /// 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 directly from input dictionaries /// /// @section References /// Concept: /// - @ref destineEncoding.h /// /// Related deductions: /// - @ref experiment.h /// - @ref expver.h /// - @ref generation.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 model identifier from the MARS dictionary. /// /// @section Deduction contract /// - Reads: `mars["model"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the model identifier from the MARS dictionary. /// /// The value is treated as mandatory and is returned verbatim as a string. /// No semantic interpretation, validation, or normalization is applied. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide the key `model`. /// /// @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 model identifier is retrieved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The model identifier as provided by the MARS dictionary. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `model` is missing, cannot be converted to `std::string`, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction assumes that the model identifier is explicitly /// provided by MARS and does not attempt any inference or defaulting. /// template std::string resolve_Model_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 model std::string marsModelVal = get_or_throw(mars, "model"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`model` resolved from input dictionaries: value='" + marsModelVal + "'"; return logMsg; }()); // Success exit point return marsModelVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `model` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/expver.h0000664000175000017500000001131415232567717024472 0ustar alastairalastair/* * (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 expver.h /// @brief Deduction of the MARS `expver` (experiment version) identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **experiment version identifier (expver)** from /// MARS metadata. /// /// The deduction retrieves the expver identifier directly from the /// MARS dictionary and exposes it to the encoding layer without /// transformation or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, normalization, or defaulting /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref marsEncoding.h /// /// Related deductions: /// - @ref class.h /// - @ref stream.h /// - @ref type.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 experiment version (expver) identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["expver"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the experiment version identifier by retrieving /// the mandatory MARS key `expver` and returning its value as a /// `std::string`. /// /// No semantic interpretation, normalization, or validation is applied. /// The meaning and allowed values of the experiment version identifier /// are defined by upstream MARS conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `expver` /// and conversion to `std::string`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the experiment version identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved experiment version identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `expver` is missing, cannot be retrieved as a string, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult experiment registries or GRIB tables. /// template std::string resolve_Expver_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 expver std::string marsExpverVal = get_or_throw(mars, "expver"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`expver` resolved from input dictionaries: value='" + marsExpverVal + "'"; return logMsg; }()); // Success exit point return marsExpverVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `expver` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/numberOfForecastsInEnsemble.h0000664000175000017500000001404415232567717030555 0ustar alastairalastair/* * (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 numberOfForecastsInEnsemble.h /// @brief Deduction of the GRIB `numberOfForecastsInEnsemble` key. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **total number of forecasts in an ensemble**. /// /// The value cannot be inferred from the MARS request alone and must be /// provided explicitly via the parameter dictionary. /// /// The MARS key `number` (perturbation number) is used exclusively for /// consistency validation and does not affect the returned value. /// /// 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 ensembleEncoding.h /// /// Related deductions: /// - @ref perturbationNumber.h /// - @ref typeOfEnsembleForecast.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the GRIB `numberOfForecastsInEnsemble` key. /// /// @section Deduction contract /// - Reads: `par["numberOfForecastsInEnsemble"]`, `mars["number"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the total number of ensemble forecasts. /// /// The value is taken verbatim from the parameter dictionary. /// No inference, defaulting, or heuristic logic is applied. /// /// The MARS perturbation number (`mars["number"]`) is optional and /// used only for consistency validation. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Optionally provide `number`. /// /// @tparam ParDict_t /// Type of the parameter dictionary. Must provide `numberOfForecastsInEnsemble`. /// Since no defaulting is implemented for this key yet, it must be explicitly provided. /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary providing the perturbation number. /// /// @param[in] par /// Parameter dictionary providing the ensemble size. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The total number of forecasts in the ensemble. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If required keys are missing, if the perturbation number is outside /// the valid range, or if any unexpected error occurs during deduction. /// /// @note /// This deduction is fully deterministic and does not depend on /// pre-existing GRIB header state. /// template long resolve_NumberOfForecastsInEnsemble_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { if (has(par, "numberOfForecastsInEnsemble")) { // The only way to infer this is from parametrization const auto numberOfForecastsInEnsemble = get_or_throw(par, "numberOfForecastsInEnsemble"); const auto perturbationNumber = get_opt(mars, "number"); // Basic validation if (perturbationNumber.has_value()) { if (*perturbationNumber < 0) { std::string errMsg = "`perturbationNumber` ("; errMsg += std::to_string(*perturbationNumber); errMsg += ") is negative"; throw Mars2GribDeductionException(errMsg, Here()); } if (*perturbationNumber > numberOfForecastsInEnsemble) { std::string errMsg = "`perturbationNumber` ("; errMsg += std::to_string(*perturbationNumber); errMsg += ") is bigger than `numberOfForecastsInEnsemble` ("; errMsg += std::to_string(numberOfForecastsInEnsemble); errMsg += ")"; throw Mars2GribDeductionException(errMsg, Here()); } } // Logging of the par::numberOfForecastsInEnsemble MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`numberOfForecastsInEnsemble` resolved from input dictionaries: value='"; logMsg += std::to_string(numberOfForecastsInEnsemble); logMsg += "'"; return logMsg; }()); return numberOfForecastsInEnsemble; } else { /// @todo Implement a defaulting strategy for `numberOfForecastsInEnsemble` when the key is missing from the /// parameter dictionary. std::string errMsg = "Default value for `numberOfForecastsInEnsemble` not implemented"; throw Mars2GribDeductionException(errMsg, Here()); } } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribDeductionException( "Failed to resolve `numberOfForecastsInEnsemble` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/typeOfGeneratingProcess.h0000664000175000017500000002440015232567717027772 0ustar alastairalastair/* * (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 typeOfGeneratingProcess.h /// @brief Optional deduction of the GRIB `typeOfGeneratingProcess` identifier. /// /// This header defines the deduction responsible for *optionally* resolving /// the GRIB `typeOfGeneratingProcess` key (Code Table 3) from MARS metadata. /// /// The deduction is intentionally conservative and returns a value only when /// a formally defined and unambiguous mapping applies. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic and explicitly defined mappings /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - guess or approximate generating process semantics /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: supported mapping applied /// - RESOLVE (skip): deduction intentionally not applied /// /// @section References /// Concept: /// - @ref generatingProcessEncoding.h /// /// Related deductions: /// - @ref generatingProcessIdentifier.h /// - @ref backgroundProcess.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include // Tables includes #include "metkit/mars2grib/backend/tables/typeOfGeneratingProcess.h" #include "metkit/mars2grib/utils/generalUtils.h" // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Optionally resolve the GRIB `typeOfGeneratingProcess` key. /// /// This deduction attempts to infer the GRIB /// `typeOfGeneratingProcess` value from MARS metadata. /// /// The deduction is **non-mandatory** and applies only when a formally /// specified and explicitly supported mapping is identified. /// If no such mapping exists, the deduction returns `std::nullopt` /// without raising an error. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary providing metadata used for deduction /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return /// - `tables::TypeOfGeneratingProcess` if the deduction applies /// - `std::nullopt` if no supported deduction is identified /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If required MARS keys are missing or dictionary access fails /// /// @note /// This deduction does not rely on pre-existing GRIB header state and /// does not apply defaults. /// template std::optional resolve_TypeOfGeneratingProcess_opt( const MarsDict_t& mars, const ParDict_t& par, [[maybe_unused]] const OptDict_t& opt) { using metkit::mars2grib::backend::tables::TypeOfGeneratingProcess; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; // N.B. Sometimes this is overwritten by eccodes as a side effect of setting `param` try { // Retrieve mandatory type from MARS dictionary std::string marsTypeVal = get_or_throw(mars, "type"); // std::string marsStreamVal = get_or_throw(mars, "stream"); // std::string marsClassVal = get_or_throw(mars, "class"); // long paramIdVal = get_or_throw(mars, "param"); // Deduce the typeOfGeneratingProcess if (marsTypeVal == "4i") { tables::TypeOfGeneratingProcess result = TypeOfGeneratingProcess::AnalysisIncrement; // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`typeOfGeneratingProcess` resolved from input dictionaries: value='"; logMsg += tables::enum2name_TypeOfGeneratingProcess_or_throw(result); logMsg += "'"; return logMsg; }()); // Success exit point return {result}; } else if (marsTypeVal == "pf" || marsTypeVal == "cf") { tables::TypeOfGeneratingProcess result = TypeOfGeneratingProcess::EnsembleForecast; // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`typeOfGeneratingProcess` resolved from input dictionaries: value='"; logMsg += tables::enum2name_TypeOfGeneratingProcess_or_throw(result); logMsg += "'"; return logMsg; }()); // Success exit point return {result}; } else if (marsTypeVal == "fc") { // Detect ensemble evidence even when MARS `type` is the generic // `fc`. Legacy GRIB1 data (and some rewritten streams) may carry // `type=fc` together with ensemble-describing keys; in that case // the correct GRIB2 `typeOfGeneratingProcess` is EnsembleForecast // (4), not Forecast (2). // // Signals (any of): // - par.numberOfForecastsInEnsemble > 1 // - par.typeOfEnsembleForecast present // - mars.number > 0 const bool hasEnsembleSize = has(par, "numberOfForecastsInEnsemble") && (get_or_throw(par, "numberOfForecastsInEnsemble") > 1); const bool hasEnsembleType = has(par, "typeOfEnsembleForecast"); const bool hasEnsembleNumber = has(mars, "number") && (get_or_throw(mars, "number") > 0); const bool isEnsemble = hasEnsembleSize || hasEnsembleType || hasEnsembleNumber; tables::TypeOfGeneratingProcess result = isEnsemble ? TypeOfGeneratingProcess::EnsembleForecast : TypeOfGeneratingProcess::Forecast; // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`typeOfGeneratingProcess` resolved from input dictionaries: value='"; logMsg += tables::enum2name_TypeOfGeneratingProcess_or_throw(result); logMsg += "'"; if (isEnsemble) { logMsg += " (type='fc' with ensemble evidence:"; if (hasEnsembleSize) { logMsg += " numberOfForecastsInEnsemble>1"; } if (hasEnsembleType) { logMsg += " typeOfEnsembleForecast-present"; } if (hasEnsembleNumber) { logMsg += " number>0"; } logMsg += ")"; } else { logMsg += " (type='fc', no ensemble evidence)"; } return logMsg; }()); // Success exit point return {result}; } else if (marsTypeVal == "est" || marsTypeVal == "es" || marsTypeVal == "em" || marsTypeVal == "ses") { // Ensemble-derived statistical products (ensemble statistics, // ensemble standard deviation, ensemble mean, ensemble spread // of estimation). No dedicated code table entry exists for // "ensemble-derived analysis"; EnsembleForecast (4) is the // established convention to signal ensemble provenance. tables::TypeOfGeneratingProcess result = TypeOfGeneratingProcess::EnsembleForecast; // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`typeOfGeneratingProcess` resolved from input dictionaries: value='"; logMsg += tables::enum2name_TypeOfGeneratingProcess_or_throw(result); logMsg += "' (type=" + marsTypeVal + ")"; return logMsg; }()); // Success exit point return {result}; } else if (marsTypeVal == "eme" || marsTypeVal == "me") { // 4D-Var model-error fields (eme = ensemble model errors, // me = model errors). Generated as part of the analysis // system; the canonical ECMWF GRIB2 value is Analysis (0). // Without an explicit mapping here the encoder fell back to the // GRIB sample default, which only happened to be 0 by accident. // Grouped with eme to mirror the {4i, 4v, me, eme} grouping // already used in significanceOfReferenceTime. tables::TypeOfGeneratingProcess result = TypeOfGeneratingProcess::Analysis; // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`typeOfGeneratingProcess` resolved from input dictionaries: value='"; logMsg += tables::enum2name_TypeOfGeneratingProcess_or_throw(result); logMsg += "' (type=eme/me)"; return logMsg; }()); // Success exit point return {result}; } else { // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`typeOfGeneratingProcess` not resolved from input dictionaries: no supported mapping"; return logMsg; }()); // Success exit point return std::nullopt; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `typeOfGeneratingProcess` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/productTime.h0000664000175000017500000005073215232567717025467 0ustar alastairalastair/* * (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 productTime.h /// @brief Public deduction header for `ProductTime`. /// /// Exposes `resolve_ProductTime_or_throw`, the canonical entry point that /// produces a `ProductTime` from MARS / par / opt input dictionaries. /// /// All temporal consumers (`referenceTime`, `pointInTime`, `statistics`) /// MUST obtain their temporal data exclusively from a `ProductTime` /// produced by this function. They MUST NOT reinterpret raw MARS keys /// (`date`, `time`, `hdate`, `htime`, `fcyear`, `fcmonth`, `step`, /// `timespan`, `stattype`) independently. /// /// The implementation detail (types, factory, helpers) lives in /// `deductions/detail/ProductTime.h` (UpperCamelCase initial per §20.1 /// because the file is type-primary). The shared `stattype` parser lives /// in `deductions/detail/StatType.h` (§22). The shared `StatisticalWindow` /// type lives in `deductions/detail/StatisticalWindow.h` (§21). /// /// See `deductions/timeProducts.md` for the full normative specification. /// /// @section References /// Concept consumers: /// - referenceTimeEncoding.h /// - pointInTimeEncoding.h /// - statisticsEncoding.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include #include #include #include #include // eckit #include "eckit/exception/Exceptions.h" #include "eckit/types/DateTime.h" // Project utilities (must precede timeIncrementInSeconds.h, which uses // dict_traits but does not include the corresponding header itself). #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" // Detail #include "metkit/mars2grib/backend/deductions/detail/ProductTime.h" #include "metkit/mars2grib/backend/deductions/detail/StatType.h" // Sibling deductions #include "metkit/mars2grib/backend/deductions/timeIncrementInSeconds.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Test whether a single-loop statistic requires fakeDoubleLoop representation. /// /// FakeDoubleLoop is a compatibility representation for selected single-loop /// statistics where `timespan="none"` and the single `stattype` block carries /// the statistical period. Products outside this allow-list must use the /// standard single-loop representation (`timespan` duration, no `stattype`). /// /// @param[in] klass MARS `class` value. /// @param[in] stream MARS `stream` value. /// @return `true` when fakeDoubleLoop representation is required. /// inline bool requiresFakeDoubleLoopRepresentation(const std::string& klass, const std::string& stream) { // Rule valid for ERA6 products. if (klass == "e6" && (stream == "sttd" || stream == "stte")) { return true; } // Rule valid for SEAS6 products. if ((klass == "od" || klass == "rd" || klass == "c3") && (stream == "sfmd" || stream == "shmd")) { return true; } // Other explicitly enabled products. if ((klass == "gh" || klass == "eh") && (stream == "msmm" || stream == "rfsd")) { return true; } return false; } /// /// @brief Resolve the canonical `ProductTime` for one MARS product. /// /// @section Deduction contract /// - Reads (MARS): `date`, `time`, `hdate`, `htime`, `fcyear`, `fcmonth`, /// `step`, `timespan`, `stattype`; additionally `class` and /// `stream` when validating single-loop statistics /// - Reads (par): `timeIncrementInSeconds` (via `timeIncrementInSeconds_opt`) /// - Reads (opt): none (signature-only, reserved) /// - Writes: none /// - Side effects: one `MARS2GRIB_LOG_RESOLVE` line on success /// - Failure mode: throws `Mars2GribDeductionException` (nested-with) /// /// Resolution proceeds in two stages: /// 1. **Resolver** (this function): reads the input dictionaries and /// normalizes them into a `ProductTimeInput`. /// 2. **Factory** (`detail::make_ProductTime_or_throw`): validates all /// invariants and returns the immutable `ProductTime`. /// /// For single-loop statistics, the resolver also validates whether the product /// must use the standard representation (`timespan` duration, no `stattype`) or /// the fakeDoubleLoop representation (`timespan="none"`, one `stattype` block), /// according to the `(class, stream)` allow-list. /// /// On success, exactly one composite RESOLVE log line is emitted listing /// every resolved field in a stable, greppable form (§12). /// /// @tparam MarsDict_t MARS dictionary type. /// @tparam ParDict_t Parameter dictionary type. /// @tparam OptDict_t Options dictionary type (currently unused). /// /// @param[in] mars MARS dictionary providing temporal keys. /// @param[in] par Parameter dictionary providing `timeIncrementInSeconds`. /// @param[in] opt Options dictionary (signature-only). /// /// @return The resolved, immutable `ProductTime`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// on any rule violation (§10.x), with the original cause attached /// via `std::throw_with_nested`. /// /// @note This function is thread-safe given thread-safe dictionary reads /// (§14). /// template detail::ProductTime resolve_ProductTime_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::dict_to_json; using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; // Suppress "unused parameter" warning while preserving the documented // signature (§8: opt is reserved). (void)opt; try { // ========================================================= // §7.1 / §7.2: labelDateTime from (date, time) or // defaulted from (fcyear, fcmonth) // ========================================================= const bool hasDate = has(mars, "date"); const bool hasTime = has(mars, "time"); const bool hasFcYear = has(mars, "fcyear"); const bool hasFcMonth = has(mars, "fcmonth"); eckit::DateTime labelDateTime; if (hasDate && hasTime) { const long marsDate = get_or_throw(mars, "date"); const long marsTime = get_or_throw(mars, "time"); labelDateTime = eckit::DateTime(detail::convert_YYYYMMDD2Date_or_throw(marsDate), detail::convert_hhmmss2Time_or_throw(marsTime)); } else if (!hasDate && !hasTime && hasFcYear && hasFcMonth) { // R2 default: labelDateTime := DateTime(fcyear, fcmonth, 1, 00:00:00). const long fcYear = get_or_throw(mars, "fcyear"); const long fcMonth = get_or_throw(mars, "fcmonth"); try { labelDateTime = eckit::DateTime(eckit::Date(fcYear, fcMonth, 1), eckit::Time(0, 0, 0)); } catch (const eckit::Exception& e) { throw Mars2GribDeductionException( "Invalid (fcyear, fcmonth) for default labelDateTime: " + std::string(e.what()), Here()); } } else { // §10.1: date or time missing without an applicable fallback. throw Mars2GribDeductionException(std::string("ProductTime invariant violated [§10.1]: ") + "missing 'date'/'time' without (fcyear,fcmonth) fallback. " + "has(date)=" + (hasDate ? "true" : "false") + ", has(time)=" + (hasTime ? "true" : "false") + ", has(fcyear)=" + (hasFcYear ? "true" : "false") + ", has(fcmonth)=" + (hasFcMonth ? "true" : "false"), Here()); } // ========================================================= // §7.3: initialConditionsDateTime from (hdate, htime) // ========================================================= const bool hasHdate = has(mars, "hdate"); const bool hasHtime = has(mars, "htime"); std::optional initialConditionsDateTime; if (!hasHdate && !hasHtime) { // initialConditionsDateTime defaults to labelDateTime in the factory. } else if (hasHdate && !hasHtime) { const long marsHdate = get_or_throw(mars, "hdate"); initialConditionsDateTime = eckit::DateTime(detail::convert_YYYYMMDD2Date_or_throw(marsHdate), eckit::Time(0, 0, 0)); } else if (hasHdate && hasHtime) { const long marsHdate = get_or_throw(mars, "hdate"); const long marsHtime = get_or_throw(mars, "htime"); initialConditionsDateTime = eckit::DateTime(detail::convert_YYYYMMDD2Date_or_throw(marsHdate), detail::convert_hhmmss2Time_or_throw(marsHtime)); } else { // §10.2: htime present without hdate. throw Mars2GribDeductionException("ProductTime invariant violated [§10.2]: 'htime' present without 'hdate'", Here()); } // ========================================================= // §7.4: referenceDateTime from (fcyear, fcmonth) // ========================================================= std::optional referenceDateTime; if (!hasFcYear && !hasFcMonth) { // referenceDateTime defaults to initialConditionsDateTime in the factory. } else if (hasFcYear && hasFcMonth) { const long fcYear = get_or_throw(mars, "fcyear"); const long fcMonth = get_or_throw(mars, "fcmonth"); try { referenceDateTime = eckit::DateTime(eckit::Date(fcYear, fcMonth, 1), eckit::Time(0, 0, 0)); } catch (const eckit::Exception& e) { throw Mars2GribDeductionException( "Invalid (fcyear, fcmonth) for referenceDateTime: " + std::string(e.what()), Here()); } } else { // §10.3: exactly one of fcyear / fcmonth present. throw Mars2GribDeductionException(std::string("ProductTime invariant violated [§10.3]: ") + "exactly one of 'fcyear'/'fcmonth' present. " + "has(fcyear)=" + (hasFcYear ? "true" : "false") + ", has(fcmonth)=" + (hasFcMonth ? "true" : "false"), Here()); } // ========================================================= // §7.5: stepInSeconds from step (default 0 if missing) // ========================================================= long stepInSeconds = 0; if (has(mars, "step")) { // MARS may expose 'step' either as a long (legacy hours) or as a // string (suffixed). Prefer a string read so the parser handles // both bare-numeric and unit-suffixed forms uniformly. std::optional stepStr = get_opt(mars, "step"); if (stepStr.has_value()) { stepInSeconds = detail::toSeconds_or_throw(stepStr.value()); } else { // Fallback: numeric step interpreted as hours (§7.5 bare-numeric rule). const long marsStep = get_or_throw(mars, "step"); stepInSeconds = marsStep * 3600L; } } // ========================================================= // §7.6: timespan // ========================================================= detail::TimespanKind timespanKind = detail::TimespanKind::Missing; detail::StatisticalWindow timespan{}; if (has(mars, "timespan")) { std::optional tsStr = get_opt(mars, "timespan"); if (tsStr.has_value()) { if (tsStr.value() == "none") { timespanKind = detail::TimespanKind::None; } else { timespanKind = detail::TimespanKind::Duration; timespan.unit = tables::TimeUnit::Second; timespan.count = detail::toSeconds_or_throw(tsStr.value()); } } else { // Numeric-only timespan: interpret as hours per §7.6 (→ §7.5). const long tsNum = get_or_throw(mars, "timespan"); timespanKind = detail::TimespanKind::Duration; timespan.unit = tables::TimeUnit::Second; timespan.count = tsNum * 3600L; } } // ========================================================= // §7.7: stattype → stattypeWindows (period part only) // // Parser is shared with resolve_TypeOfStatisticalProcessing_or_throw // (§22). This deduction consumes only the `timeWindow` field of // each parsed block; the `typeOfStatisticalProcessing` field is // consumed by the sibling deduction. // ========================================================= std::array stattypeWindows{}; std::size_t stattypeWindowCount = 0; if (has(mars, "stattype")) { const std::string statTypeVal = get_or_throw(mars, "stattype"); const std::vector blocks = detail::parse_StatType_or_throw(statTypeVal); if (blocks.size() > detail::maxStatisticalWindows) { throw Mars2GribDeductionException( "ProductTime invariant violated [§10.15]: stattype yields " + std::to_string(blocks.size()) + " block(s) > maxStatisticalWindows (" + std::to_string(detail::maxStatisticalWindows) + ")", Here()); } for (std::size_t i = 0; i < blocks.size(); ++i) { stattypeWindows[i] = blocks[i].timeWindow; } stattypeWindowCount = blocks.size(); } // ========================================================= // §7.8: single-loop representation policy // ========================================================= const bool isStandardSingleLoopStatistic = (timespanKind == detail::TimespanKind::Duration && stattypeWindowCount == 0); const bool isFakeDoubleLoopSingleLoopStatistic = (timespanKind == detail::TimespanKind::None && stattypeWindowCount == 1); if (isStandardSingleLoopStatistic || isFakeDoubleLoopSingleLoopStatistic) { std::string klass; std::string stream; try { klass = get_or_throw(mars, "class"); stream = get_or_throw(mars, "stream"); } catch (...) { std::throw_with_nested(Mars2GribDeductionException( "ProductTime invariant violated [§10.19]: single-loop statistic requires MARS 'class' and " "'stream' to validate the standard/fakeDoubleLoop representation policy", Here())); } const bool requiresFakeDoubleLoop = requiresFakeDoubleLoopRepresentation(klass, stream); if (isStandardSingleLoopStatistic && requiresFakeDoubleLoop) { throw Mars2GribDeductionException( "ProductTime invariant violated [§10.20]: standard single-loop statistic is not valid for " "class='" + klass + "', stream='" + stream + "'; fakeDoubleLoop representation is required", Here()); } if (isFakeDoubleLoopSingleLoopStatistic && !requiresFakeDoubleLoop) { throw Mars2GribDeductionException( "ProductTime invariant violated [§10.21]: fakeDoubleLoop single-loop statistic is not valid for " "class='" + klass + "', stream='" + stream + "'; standard single-loop representation is required", Here()); } } // ========================================================= // §7.9: timeIncrementInSeconds (par) // ========================================================= std::optional tInc = std::nullopt; if ((timespanKind == detail::TimespanKind::None && stattypeWindowCount == 1) || timespanKind == detail::TimespanKind::Duration) { tInc = timeIncrementInSeconds_opt(mars, par); } // ========================================================= // Assemble the input bundle and call the factory. // ========================================================= detail::ProductTimeInput input; input.labelDateTime = labelDateTime; input.initialConditionsDateTime = initialConditionsDateTime; input.referenceDateTime = referenceDateTime; input.stepInSeconds = stepInSeconds; input.timespanKind = timespanKind; input.timespan = timespan; input.stattypeWindows = stattypeWindows; input.stattypeWindowCount = stattypeWindowCount; input.timeIncrementInSeconds = tInc; detail::ProductTime pt = detail::make_ProductTime_or_throw(input); // ========================================================= // §12: composite RESOLVE log line (success path only) // ========================================================= MARS2GRIB_LOG_RESOLVE([&]() { std::string msg = "`ProductTime` resolved from input dictionaries: "; msg += "labelDateTime='" + detail::fmt(pt.labelDateTime) + "'"; msg += " initialConditionsDateTime='" + detail::fmt(pt.initialConditionsDateTime) + "'"; msg += " referenceDateTime='" + detail::fmt(pt.referenceDateTime) + "'"; msg += " windowStart='" + detail::fmt(pt.windowStart) + "'"; msg += " windowEnd='" + detail::fmt(pt.windowEnd) + "'"; msg += " statisticalWindowCount='" + std::to_string(pt.statisticalWindowCount) + "'"; msg += " statisticalWindows=" + detail::fmt(pt.statisticalWindows, pt.statisticalWindowCount); msg += " timeIncrementInSeconds='" + (pt.timeIncrementInSeconds.has_value() ? std::to_string(pt.timeIncrementInSeconds.value()) : std::string("missing")) + "'"; return msg; }()); return pt; } catch (...) { // §11: nested rethrow with context. std::throw_with_nested(Mars2GribDeductionException("Unable to resolve ProductTime", Here())); } // Remove compiler warning mars2gribUnreachable(); } /// /// @brief Number of statistical time ranges encoded in a `ProductTime`. /// /// This is a thin accessor: it returns `pt.statisticalWindowCount` cast to /// `long`, which is the GRIB-side `numberOfTimeRanges` value (PDT 4.8 / 4.11). /// /// Provided as a free function so that consumers do not need to access /// the `ProductTime` field directly nor reach into `std::size_t` arithmetic. /// /// @param[in] pt Resolved `ProductTime`. /// @return Number of statistical loops (`0` for instant products). /// inline long numberOfTimeRanges(const detail::ProductTime& pt) { return static_cast(pt.statisticalWindowCount); } } // namespace metkit::mars2grib::backend::deductionsmetkit-1.19.2/src/metkit/mars2grib/backend/deductions/satelliteNumber.h0000664000175000017500000001027315232567717026323 0ustar alastairalastair/* * (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 satelliteNumber.h /// @brief Deduction of the satellite platform identifier from MARS metadata. /// /// This header defines the deduction responsible for resolving the /// satellite identifier used in satellite-based products. /// /// The deduction extracts the identifier from the MARS dictionary /// and returns it verbatim without interpretation or validation. /// /// Deductions: /// - extract values from MARS, parameter, or option dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - normalize or validate semantics /// - perform GRIB table validation /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained through deduction logic from input dictionaries /// /// @section References /// Concept: /// - @ref satelliteEncoding.h /// /// Related deductions: /// - @ref instrumentType.h /// - @ref channel.h /// - @ref satelliteSeries.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System include #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 satellite identifier from the MARS dictionary. /// /// @section Deduction contract /// - Reads: `mars["ident"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the mandatory `ident` entry from the /// MARS dictionary and returns it as a numeric satellite identifier. /// /// No inference, defaulting, normalization, or semantic validation /// is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide the key `ident`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused). /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary providing the satellite identifier. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// Satellite identifier resolved from the MARS dictionary. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `ident` is missing, cannot be retrieved as a `long`, /// or if any unexpected error occurs. /// /// @note /// This deduction is deterministic and does not depend on any /// pre-existing GRIB header state. /// template long resolve_satelliteNumber_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 satellite identifier from MARS dictionary long satelliteNumber = get_or_throw(mars, "ident"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`satelliteNumber` resolved from MARS dictionary: value='"; logMsg += std::to_string(satelliteNumber); logMsg += "'"; return logMsg; }()); // Success exit point return satelliteNumber; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `satelliteNumber` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/activity.h0000664000175000017500000001121415232567717025014 0ustar alastairalastair/* * (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 activity.h /// @brief Deduction of the MARS `activity` attribute. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **MARS activity identifier** from input dictionaries. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal deduction logic where required /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply semantic defaults beyond explicit rules /// - 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 resolved via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref destineEncoding.h /// /// Related deductions: /// - @ref experiment.h /// - @ref generation.h /// - @ref model.h /// - @ref realization.h /// - @ref resolution.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 MARS activity identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["activity"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the mandatory key `activity` from the MARS /// dictionary and returns its value as a `std::string`. /// /// The value is resolved directly from the input dictionaries without /// semantic interpretation, validation, or defaulting. The meaning of /// the activity identifier is defined by upstream MARS conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `activity` /// and conversion to `std::string`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the activity identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved MARS activity identifier as a `std::string`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `activity` is missing, cannot be converted to /// `std::string`, or if any unexpected error occurs during access. /// /// @note /// This deduction follows a fail-fast strategy and emits exactly one /// RESOLVE log entry on successful execution. /// template std::string resolve_Activity_or_throw(const MarsDict_t& mars, [[maybe_unused]] const ParDict_t& par, [[maybe_unused]] const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Retrieve mandatory MARS activity std::string marsActivityVal = get_or_throw(mars, "activity"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`activity` resolved from input dictionary: value='" + marsActivityVal + "'"; return logMsg; }()); // Success exit point return marsActivityVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `activity` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/typeOfEnsembleForecast.h0000664000175000017500000001371415232567717027577 0ustar alastairalastair/* * (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 typeOfEnsembleForecast.h /// @brief Deduction of the GRIB `typeOfEnsembleForecast` identifier. /// /// This header defines the deduction responsible for resolving the /// GRIB `typeOfEnsembleForecast` key (Code Table 4.6), which describes /// the nature of ensemble perturbations. /// /// The value may be provided explicitly via parametrization or /// deduced deterministically from MARS metadata. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values beyond defined rules /// - apply silent fallbacks /// - rely on pre-existing GRIB header state /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value deduced from input dictionaries /// - OVERRIDE: explicit user override via parameter dictionary /// /// @section References /// Concept: /// - @ref ensembleEncoding.h /// /// Related deductions: /// - @ref perturbationNumber.h /// - @ref numberOfForecastsInEnsemble.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include // Tables includes #include "metkit/mars2grib/backend/tables/typeOfEnsembleForecast.h" #include "metkit/mars2grib/utils/generalUtils.h" // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the GRIB `typeOfEnsembleForecast` key. /// /// Resolution follows a strict precedence order: /// /// 1. **Explicit override** /// If `par::typeOfEnsembleForecast` is present, its value is taken /// as authoritative and validated against GRIB Code Table 4.6. /// /// 2. **Automatic deduction** /// If no override is provided, the value is deduced from `mars::type`: /// - `"cf"` → `Unperturbed` /// - `"pf"` → `Perturbed` /// /// Any unsupported input results in a deduction failure. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary /// @param[in] par Parameter dictionary /// @param[in] opt Options dictionary (unused) /// /// @return The resolved `TypeOfEnsembleForecast` enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the value cannot be resolved /// /// @note /// This deduction is deterministic and authoritative for /// `typeOfEnsembleForecast`. /// template tables::TypeOfEnsembleForecast resolve_TypeOfEnsembleForecast_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Get mars type from dictionary if (has(par, "typeOfEnsembleForecast")) { // Retrieve mandatory typeOfEnsembleForecast from parameter dictionary long typeOfEnsembleForecastVal = get_or_throw(par, "typeOfEnsembleForecast"); // Get the enum value tables::TypeOfEnsembleForecast typeOfEnsembleForecast = tables::long2enum_TypeOfEnsembleForecast_or_throw(typeOfEnsembleForecastVal); // Emit RESOLVE log entry MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`typeOfEnsembleForecast` overridden from parameter dictionary: value='"; logMsg += tables::enum2name_TypeOfEnsembleForecast_or_throw(typeOfEnsembleForecast); logMsg += "'"; return logMsg; }()); // Success exit point return typeOfEnsembleForecast; } else { // Retrieve mandatory type from MARS dictionary std::string marsType = get_or_throw(mars, "type"); tables::TypeOfEnsembleForecast typeOfEnsembleForecast = tables::TypeOfEnsembleForecast::Missing; if (marsType == "cf") { typeOfEnsembleForecast = tables::TypeOfEnsembleForecast::Unperturbed; } else if (marsType == "pf") { typeOfEnsembleForecast = tables::TypeOfEnsembleForecast::Perturbed; } else { throw Mars2GribDeductionException( "`type` value '" + marsType + "' is not mapped to any known `typeOfEnsembleForecast`", Here()); } // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`typeOfEnsembleForecast` resolved from input dictionaries: value='"; logMsg += tables::enum2name_TypeOfEnsembleForecast_or_throw(typeOfEnsembleForecast); logMsg += "'"; return logMsg; }()); // Success exit point return typeOfEnsembleForecast; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `typeOfEnsembleForecast` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/productTime.md0000664000175000017500000017147615232567717025651 0ustar alastairalastair# ProductTime — Specification > Status: **normative**. This document supersedes all prior design notes for time > handling in `mars2grib`. It contains no open questions and no historical > commentary; every rule is prescriptive (`MUST` / `MUST NOT` / `MAY`). --- ## 1. Purpose and scope `ProductTime` is the **single canonical representation** of all temporal information associated with a MARS product within `mars2grib`. The temporal consumers `referenceTime`, `pointInTime`, and `statistics` MUST obtain their temporal data exclusively from a `ProductTime` instance produced by `resolve_ProductTime_or_throw`. They MUST NOT reinterpret raw MARS keys (`date`, `time`, `hdate`, `htime`, `fcyear`, `fcmonth`, `step`, `timespan`, `stattype`) independently. Out of scope for this document: - The MARS-side parsing of request strings. - The `paramId` → statistical-processing-type lookup table. - The GRIB-side encoding of fields outside the temporal domain. - The migration of consumer concept files (see §18.4). --- ## 2. Architecture ``` MARS par opt \ | / \ | / v v v resolve_ProductTime_or_throw (deductions/productTime.h) | | builds ProductTimeInput, calls factory v make_ProductTime_or_throw (deductions/detail/ProductTime.h) | | validates invariants, returns immutable ProductTime v +----------------+ | ProductTime | +----------------+ | +--> referenceTime consumer +--> pointInTime consumer +--> statistics consumer | +--> resolve_TypeOfStatisticalProcessing_or_throw | (deductions/typeOfStatisticalProcessing.h, §23; | self-contained, depends on detail/StatType.h, §22) +--> computeStatisticDescription(productTime, types) (concepts/statistics/impl/statisticsDescriptor.h) ``` `computeStatisticDescription` is a **pure function**: it depends only on its arguments. No MARS / par / opt access at descriptor-build time. --- ## 3. `StatisticalWindow` model `ProductTime` represents each statistical window as a `(unit, count)` pair, reusing the existing GRIB-aligned `tables::TimeUnit` enum (`backend/tables/timeUnits.h`, GRIB2 Code Table 4.4). No parallel "kind" enum is introduced. The `StatisticalWindow` type is defined in the shared header `deductions/detail/StatisticalWindow.h` (§21) and is consumed by `detail/ProductTime.h`, `detail/StatType.h` (the shared `stattype` parser, §22), and `typeOfStatisticalProcessing.h` (§23). ```cpp #include "metkit/mars2grib/backend/tables/timeUnits.h" namespace metkit::mars2grib::backend::deductions::detail { struct StatisticalWindow { tables::TimeUnit unit{tables::TimeUnit::Second}; long count{0}; }; } // namespace metkit::mars2grib::backend::deductions::detail ``` ### 3.1 Allowed `TimeUnit` subset Although `tables::TimeUnit` enumerates the full GRIB2 code table, the only values legal inside a `ProductTime::statisticalWindows` entry are: ```text tables::TimeUnit::Second tables::TimeUnit::Day tables::TimeUnit::Month ``` The factory `make_ProductTime_or_throw` MUST reject any other value (including but not limited to `Minute`, `Hour`, `Hours3`, `Hours6`, `Hours12`, `Year`, `Decade`, `Normal`, `Century`, `Missing`) as a hard error (§10.18). This restriction reflects the locked production paths: - `timespan` is always pre-converted to fixed seconds → produces `tables::TimeUnit::Second`; - the locked `stattype` grammar (§7.7) emits only `mo` and `da` periods → produce `tables::TimeUnit::Month` and `tables::TimeUnit::Day` respectively. Any future widening of this allow-list is an explicit spec amendment, not an implementation freedom. ### 3.2 Invariants - For any `StatisticalWindow` stored inside a `ProductTime` (i.e. inside `statisticalWindows[0 .. statisticalWindowCount)`), `count > 0` MUST hold. - The factory rejects any input that contains a `StatisticalWindow` with `count <= 0` (hard error §10.11). - The factory rejects any input that contains a `StatisticalWindow` with `unit` outside the §3.1 allow-list (hard error §10.18). - The default-constructed `StatisticalWindow{}` has `count == 0`; it is legal at the C++ language level but illegal as a `ProductTime` member; it serves only as a temporary. ### 3.3 Calendar-vs-fixed classification For the purposes of windowing semantics (§4) and calendar subtraction (§9.6), the units in §3.1 are classified as: | `unit` | Classification | |------------------------------|----------------| | `tables::TimeUnit::Second` | fixed-duration | | `tables::TimeUnit::Day` | calendar-aligned | | `tables::TimeUnit::Month` | calendar-aligned | The classification is implicit in the unit value; no separate field encodes it. ### 3.4 Examples ```text 1h -> StatisticalWindow{tables::TimeUnit::Second, 3600} 24h -> StatisticalWindow{tables::TimeUnit::Second, 86400} 1 calendar day -> StatisticalWindow{tables::TimeUnit::Day, 1} 1 calendar month -> StatisticalWindow{tables::TimeUnit::Month, 1} ``` `tables::TimeUnit::Year` is intentionally excluded from the allow-list: it is unreachable through the locked `stattype` grammar (§7.7) and not produced by any other path. --- ## 4. Calendar-window semantics and strict alignment ### 4.1 Half-open interval convention A product temporal support is the half-open interval `[windowStart, windowEnd)`, **when** `windowStart < windowEnd`. The degenerate case `windowStart == windowEnd` denotes the **point in time** at `windowEnd` and is reserved for instant products (§9.1). ### 4.2 Calendar day A `StatisticalWindow{tables::TimeUnit::Day, N}` window is a window of N **named** calendar days, internally represented as ```text [YYYY-MM-DD 00:00:00, YYYY-MM-(DD+N) 00:00:00) ``` (with appropriate calendar carry into the next month / year). Alignment rule for a `tables::TimeUnit::Day` outermost window: ```text windowEnd MUST be at hh=00, mm=00, ss=00. ``` ### 4.3 Calendar month A `StatisticalWindow{tables::TimeUnit::Month, N}` window is a window of N **named** calendar months. Its length depends on the concrete months involved. Alignment rule for a `tables::TimeUnit::Month` outermost window: ```text windowEnd MUST be on day=1 at hh=00, mm=00, ss=00. ``` ### 4.4 Strict alignment policy When the outermost window in `statisticalWindows` is calendar-aligned (i.e. its `unit` is `tables::TimeUnit::Day` or `tables::TimeUnit::Month`, per §3.3), the factory MUST reject any input whose `windowEnd` violates the corresponding alignment rule (hard errors §10.9, §10.10). This is a deliberate behavioural change relative to legacy `timeUtils.h`-based code, which silently tolerated misaligned ends. There is no opt-in flag and no warn-only mode. Misaligned operational requests fail loudly and must be fixed upstream. --- ## 5. `ProductTime` struct ```cpp inline constexpr std::size_t maxStatisticalWindows = 3; struct ProductTime { const eckit::DateTime labelDateTime; const eckit::DateTime initialConditionsDateTime; const eckit::DateTime referenceDateTime; // Internal convention: [windowStart, windowEnd) when windowStart < windowEnd. // For instant products: windowStart == windowEnd (the point in time at windowEnd). const eckit::DateTime windowStart; const eckit::DateTime windowEnd; // Valid entries: statisticalWindows[0 .. statisticalWindowCount). // Ordering: outermost -> innermost. const std::array statisticalWindows; const std::size_t statisticalWindowCount; // Sampling increment of the innermost statistical loop. // std::nullopt for instant products (§9.1). // Optionally absent for single-window statistical products (AIFS path, §9.4). // Required and > 0 for multi-window statistical products. const std::optional timeIncrementInSeconds; }; ``` All members are `const`. The struct is therefore immutable after construction, copyable, and not assignable. `maxStatisticalWindows = 3` matches the maximum producible by the locked `stattype` grammar (§7.6): one `mo` block + one `da` block + one inner `timespan` window. ### 5.1 Tri-equivalent instant invariant The factory MUST enforce, as a single equivalence: ```text windowStart == windowEnd <=> statisticalWindowCount == 0 <=> timeIncrementInSeconds == std::nullopt ``` i.e. all three conditions hold together or none does. Any other state is a hard error (§10.5). ### 5.2 Window-end ordering invariant ```text windowStart <= windowEnd (always) ``` Strictly `<` for statistical products; equality reserved for instants. ### 5.3 Reference-vs-simulated invariant ```text referenceDateTime >= initialConditionsDateTime (always) ``` Violation is a hard error (§10.4). The corner case where reference < simulated is officially out-of-spec MARS semantics and must be fixed upstream. --- ## 6. `ProductTimeInput` The factory does not consume raw MARS keys. The resolver (§8) normalizes them into `ProductTimeInput` first. ```cpp enum class TimespanKind { Missing, // MARS keyword absent Duration, // MARS keyword has a duration value None // MARS keyword equals literal "none" (fakeDoubleLoop, §9.4) }; struct ProductTimeInput { eckit::DateTime labelDateTime; std::optional initialConditionsDateTime; // from hdate/htime std::optional referenceDateTime; // from fcyear/fcmonth // Offset from referenceDateTime to ProductTime::windowEnd. long stepInSeconds{0}; TimespanKind timespanKind{TimespanKind::Missing}; // Valid only when timespanKind == TimespanKind::Duration. StatisticalWindow timespan{}; // Temporal windows decoded from stattype (period part only), // ordered outermost -> innermost. std::array stattypeWindows{}; std::size_t stattypeWindowCount{0}; // Source: deductions::timeIncrementInSeconds_opt(mars, par) when the resolver invokes it (§7.9). // Resolver-forced std::nullopt for instant products and for invalid // timespan/stattype combinations that fail before the value is used. // Also optionally absent for the AIFS single-window path (§9.4). std::optional timeIncrementInSeconds; }; ``` Factory: ```cpp ProductTime make_ProductTime_or_throw(const ProductTimeInput& input); ``` The factory: 1. Resolves defaults (simulated, reference) per §7. 2. Computes `windowEnd = referenceDateTime + stepInSeconds`. 3. Assembles `statisticalWindows` per the case table in §9. 4. Computes `windowStart` per §9. 5. Validates all invariants (§5.1, §5.2, §5.3) and all hard errors (§10). 6. Returns the immutable `ProductTime`. --- ## 7. MARS keywords and field-by-field resolution ### 7.1 Keyword reference | Key | Required | Source | |------------|----------------------|----------------------------------------------| | `date` | conditional (§7.2) | MARS | | `time` | conditional (§7.2) | MARS | | `hdate` | no | MARS | | `htime` | no | MARS | | `fcyear` | no | MARS | | `fcmonth` | no | MARS | | `step` | no, default `0` (§7.5) | MARS | | `timespan` | no | MARS | | `stattype` | no | MARS | | `class` | conditional (§7.8) | MARS | | `stream` | conditional (§7.8) | MARS | | `timeIncrementInSeconds` | no | par (parameter dictionary), conditionally via `deductions::timeIncrementInSeconds_opt` (§7.9) | ### 7.2 `date` / `time` Resolution rule for `labelDateTime`: ```text date present and time present: labelDateTime := DateTime(date, time) date missing and time missing and fcyear present and fcmonth present: labelDateTime := DateTime(Date(fcyear, fcmonth, 1), Time(00:00:00)) (this is the same value §7.4 produces for referenceDateTime; consequently labelDateTime == referenceDateTime in this case) any other combination -> hard error (§10.1) ``` `labelDateTime` is a label for the simulation; it is not used for `step` arithmetic. The `hdate`/`htime` rules in §7.3 are unchanged regardless of whether the R2 default in §7.2 is taken: if `hdate` is present it still drives `initialConditionsDateTime`, and the §5.3 invariant `referenceDateTime >= initialConditionsDateTime` continues to apply (it may now constrain `(fcyear, fcmonth, 1)` against `hdate`). ### 7.3 `hdate` / `htime` ```text hdate missing, htime missing -> initialConditionsDateTime := labelDateTime hdate present, htime missing -> initialConditionsDateTime := DateTime(hdate, 00:00:00) hdate present, htime present -> initialConditionsDateTime := DateTime(hdate, htime) hdate missing, htime present -> hard error (§10.2) ``` ### 7.4 `fcyear` / `fcmonth` Both must be present together or both absent. ```text both missing -> referenceDateTime := initialConditionsDateTime both present -> referenceDateTime := DateTime(Date(fcyear, fcmonth, 1), Time(00:00:00)) exactly one present -> hard error (§10.3) ``` The day component is `1` (Gregorian-valid). No day-zero variant exists. ### 7.5 `step` `step` is optional with default `0`. ```text step missing -> stepInSeconds := 0 step present -> stepInSeconds := toSeconds_or_throw(step) ``` `toSeconds_or_throw` (signature locked from existing `detail/timeUtils.h`) parses: - bare numeric `N` as **N hours**; - suffixed numeric with `h` (hours), `m` (minutes), `s` (seconds), `d` (days). Then: ```text windowEnd := referenceDateTime + stepInSeconds ``` `step` missing is observationally indistinguishable from `step = 0`; both produce the same `ProductTime`. The internal type is `long`, not `std::optional`. Whether the resulting `ProductTime` is then a legal instant or a misaligned calendar window is determined by the case table (§9) and the alignment rule (§4.4) — no special-case logic for the absent / zero step. ### 7.6 `timespan` Three states (`TimespanKind`): - **`Missing`** — keyword absent. - **`Duration`** — keyword carries a duration. Parsed by `toSeconds_or_throw` (same rules as `step`, §7.5). The result is wrapped in `StatisticalWindow{tables::TimeUnit::Second, seconds}`. **Note**: in production the resolver always converts `timespan` to fixed seconds before entering the factory; the spec deliberately leaves no path for calendar-aligned `timespan`. If future needs require it, this section is the single point that must be amended. - **`None`** — keyword equals the literal string `"none"`. Valid only in the fakeDoubleLoop case (§9.4); any other use is a hard error (§10.6, §10.7). ### 7.7 `stattype` Grammar (locked, identical to the legacy `detail/timeUtils.h::parseStatType_or_throw`, now `detail/StatType.h::parse_StatType_or_throw`): ```text stattype := block ('_' block)* block := period operation period := 'mo' | 'da' operation := 'av' | 'mn' | 'mx' | 'sd' ``` Block length is exactly 4 characters; separator is exactly one underscore. Semantic constraints (also locked from existing parser): - at most one `mo` block; - at most one `da` block; - if both present, `mo` MUST precede `da` (outermost-to-innermost order). The `ProductTime` factory uses the **period prefix only** for window assembly: ```text period 'mo' -> StatisticalWindow{tables::TimeUnit::Month, 1} period 'da' -> StatisticalWindow{tables::TimeUnit::Day, 1} ``` The operation suffix is consumed by `resolveTypeOfStatisticalProcessing` (a sibling deduction, out of scope here). The `stattype` parser MUST be a single shared helper (now in `detail/StatType.h` as `parse_StatType_or_throw`) used by both deductions, so that the two never drift. ### 7.8 Single-loop representation policy When the resolver detects a single-loop statistic, it MUST read MARS `class` and `stream` and validate whether the product must use the standard single-loop representation or the fakeDoubleLoop representation. The two single-loop representations are: ```text standard single-loop: timespanKind == Duration AND stattypeWindowCount == 0 fakeDoubleLoop single-loop: timespanKind == None AND stattypeWindowCount == 1 ``` The fakeDoubleLoop representation is required exactly for the following `(class, stream)` combinations: | `class` | `stream` values | |---------|-----------------| | `e6` | `sttd`, `stte` | | `od` | `sfmd`, `shmd` | | `rd` | `sfmd`, `shmd` | | `c3` | `sfmd`, `shmd` | | `gh` | `msmm`, `rfsd` | | `eh` | `msmm`, `rfsd` | For these combinations, standard single-loop statistics are a hard error (§10.20). For all other combinations, fakeDoubleLoop single-loop statistics are a hard error (§10.21). If `class` or `stream` cannot be read while validating a single-loop statistic, this is a hard error (§10.19). This rule applies only to single-loop statistics. It does not apply to instant products or old-style multi-loop statistics. ### 7.9 `timeIncrementInSeconds` Source: `deductions::timeIncrementInSeconds_opt(mars, par)`, which reads `par["timeIncrementInSeconds"]` when invoked. The resolver invokes it only for inputs that are statistical candidates: ```text timespanKind == Duration OR timespanKind == None AND stattypeWindowCount == 1 ``` For instant products, and for invalid `timespan` / `stattype` combinations that fail before the value is used, the resolver forwards `std::nullopt` without reading `par["timeIncrementInSeconds"]`. When the helper is invoked, existing normalization is preserved: - absent -> `std::nullopt` - present, value `0` -> `std::nullopt` (legacy normalization) - present, value < 0 -> hard error (§10.14) - present, value > 0 -> the value The forwarded `std::optional` becomes `ProductTime::timeIncrementInSeconds` subject to the case table (§9) and the tri-equivalent invariant (§5.1). --- ## 8. Resolver ```cpp template ProductTime resolve_ProductTime_or_throw( const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt); ``` - `opt` is accepted for signature consistency with sibling deductions but reads no keys at present. Reserved for future options. - The resolver: 1. Reads MARS keys per §7. 2. Validates the standard/fakeDoubleLoop representation policy for single-loop statistics according to §7.8. 3. Conditionally reads `par["timeIncrementInSeconds"]` via the existing `timeIncrementInSeconds_opt` helper, according to §7.9. 4. Builds a `ProductTimeInput`. 5. Calls `make_ProductTime_or_throw`. 6. On success, emits exactly one `MARS2GRIB_LOG_RESOLVE` line (§12). 7. On failure, rethrows-with-nested per §11. `resolve_ProductTime_or_throw` is the canonical name. Any additional spelling (e.g. `resolveProductTime_or_throw`) is a deprecated alias and SHOULD NOT be used in new code. Header location: `deductions/productTime.h` (public). The factory `make_ProductTime_or_throw` called by this resolver is declared in `deductions/detail/ProductTime.h`. The two files are distinguished by both the `detail/` subdirectory and the case of the basename per the §20.1 naming convention (function-primary public headers use lowercase initials; type-primary detail headers use UpperCamelCase initials). --- ## 9. Window-assembly cases Four mutually-exclusive cases, distinguished by `timespanKind` and presence of `stattype` blocks. Each row of the table specifies the **complete** state of the resulting `ProductTime`. | Case | `timespanKind` | `stattype` blocks | `statisticalWindows` (out→in) | `windowStart` | `timeIncrementInSeconds` | |------|----------------|-------------------|-------------------------------|---------------|--------------------------| | §9.1 Instant | `Missing` | 0 | (empty) | `windowEnd` | `std::nullopt` (resolver-forced) | | §9.2 Old single-loop | `Duration`| 0 | `[timespan]` | `windowEnd - timespan` | optional or required (§9.5) | | §9.3 Old multi-loop | `Duration`| 1 or 2 | `[parse(stattype) ..., timespan]` | `windowEnd - statisticalWindows[0]` | required and > 0 | | §9.4 New fakeDoubleLoop | `None` | exactly 1 | `[parse(stattype)]` | `windowEnd - statisticalWindows[0]` | optional or required (§9.5) | | any other combination | — | — | — | — | hard error (§10.6, §10.7, §10.8) | In all rows, `labelDateTime`, `initialConditionsDateTime`, `referenceDateTime`, and `windowEnd` are computed per §7. The strict-alignment rule (§4.4) is applied after `windowStart` is computed. ### 9.1 Instant product ```text windowEnd := referenceDateTime + stepInSeconds windowStart := windowEnd statisticalWindows := (empty array, statisticalWindowCount = 0) timeIncrementInSeconds := std::nullopt (resolver-forced; par value is not read) ``` Used primarily by `referenceTime` and `pointInTime`. `statistics` MUST NOT be invoked on an instant `ProductTime`. ### 9.2 Old-style single-loop statistic ```text windowEnd := referenceDateTime + stepInSeconds statisticalWindows := [timespan] statisticalWindowCount := 1 windowStart := windowEnd - timespan (= windowEnd - timespan.count seconds) ``` `timespan.unit` is `tables::TimeUnit::Second` (§7.6). `windowStart` is therefore a simple seconds subtraction; no calendar arithmetic is involved on this path. This representation is valid only when the `(class, stream)` policy in §7.8 does not require fakeDoubleLoop representation. ### 9.3 Old-style multi-loop statistic ```text windowEnd := referenceDateTime + stepInSeconds statisticalWindows := [ , timespan ] statisticalWindowCount := stattypeWindowCount + 1 windowStart := windowEnd - statisticalWindows[0] (calendar subtraction; see §9.6) ``` ### 9.4 New-style fakeDoubleLoop ("`timespan = none`") A deliberately redundant encoding that exists in production. Single statistical loop only. The single `stattype` block carries both: - a **period** (`mo` or `da`) consumed by `ProductTime`; - an **operation** (`av` / `mn` / `mx` / `sd`) consumed by `resolveTypeOfStatisticalProcessing`. The same statistical operation is also implied by `paramId`. The two MUST agree; disagreement is a hard error (§10.12). This contract is enforced inside the deduction `resolve_TypeOfStatisticalProcessing_or_throw` (§23), which receives the `paramId`-derived operation as its `innerTypeOfStatisticalProcessing` argument and compares it against the parsed `stattype` block operation in this case. This representation is valid only when the `(class, stream)` policy in §7.8 requires fakeDoubleLoop representation. ```text windowEnd := referenceDateTime + stepInSeconds statisticalWindows := [parse(single stattype block)] statisticalWindowCount := 1 windowStart := windowEnd - statisticalWindows[0] (calendar subtraction; see §9.6) ``` ### 9.5 `timeIncrementInSeconds` per case | Case | Required value of `timeIncrementInSeconds` | |------|--------------------------------------------| | §9.1 Instant | MUST be `std::nullopt` | | §9.2 Old single-loop | MAY be `std::nullopt` (AIFS path); if present MUST be > 0 | | §9.3 Old multi-loop | MUST be present and > 0 | | §9.4 New fakeDoubleLoop | MAY be `std::nullopt` (AIFS path); if present MUST be > 0 | Violation is a hard error (§10.5 for the constructed `ProductTime` invariant, §10.14 for negative values when the helper is invoked, §10.13 for missing-where-required). For §9.1, the resolver forces the constructed field to `std::nullopt` and does not consult the par value. The free helper `numberOfTimeRanges(const detail::ProductTime& pt)` declared in `productTime.h` returns the number of statistical loops contributing to the product, defined as `pt.statisticalWindowCount`. For instant products (§9.1) it returns `0`; for old-style single-loop and new-style fakeDoubleLoop statistics (§9.2, §9.4) it returns `1`; for old-style multi-loop statistics (§9.3) it returns the number of nested loops (`1` or `2`, matching the §3.1 allow-list and the §22.6 ordering). Consumers (e.g. the GRIB `numberOfTimeRange` key) MUST use this helper rather than recompute the count from `statisticalWindows.size()` to preserve the `statisticalWindowCount` invariant (§3.2). ### 9.6 Calendar subtraction For a window with `unit == tables::TimeUnit::Day` and `count == N`: ```text windowStart := DateTime(windowEnd.date() - N days, 00:00:00) ``` For a window with `unit == tables::TimeUnit::Month` and `count == N`: ```text windowStart := DateTime(Date(year, month, 1) shifted back N months, 00:00:00) ``` For a window with `unit == tables::TimeUnit::Second` and `count == N`: ```text windowStart := windowEnd - N seconds (no calendar arithmetic) ``` The calendar paths presuppose `windowEnd` already satisfies the corresponding alignment rule (§4); the factory verifies alignment before performing the subtraction. --- ## 10. Hard errors The factory `make_ProductTime_or_throw` and the resolver `resolve_ProductTime_or_throw` MUST throw `Mars2GribDeductionException` on any of the following conditions. Each entry corresponds to exactly one check site in the implementation. | # | Condition | |-------|------------------------------------------------------------------| | 10.1 | `date` or `time` missing without fallback: `date` missing OR `time` missing, **except** when both `date` AND `time` are missing AND both `fcyear` AND `fcmonth` are present (in which case §7.2 substitutes the default). Note: `step` missing is NOT an error (§7.5 default 0). | | 10.2 | `htime` present without `hdate` | | 10.3 | exactly one of `fcyear` / `fcmonth` present (must be both or neither) | | 10.4 | `referenceDateTime < initialConditionsDateTime` | | 10.5 | tri-equivalence broken: `windowStart == windowEnd` XOR `statisticalWindowCount == 0` XOR `timeIncrementInSeconds == nullopt` | | 10.6 | `stattype` present but `timespan` missing | | 10.7 | `timespan = none` but `stattype` missing | | 10.8 | `timespan = none` with more than one `stattype` block | | 10.9 | outermost window has `unit == tables::TimeUnit::Day` and `windowEnd` is not at hh=00,mm=00,ss=00 | | 10.10 | outermost window has `unit == tables::TimeUnit::Month` and `windowEnd` is not on day=1 at hh=00,mm=00,ss=00 | | 10.11 | any `StatisticalWindow` in `statisticalWindows[0..count)` has `count <= 0` | | 10.12 | (deduction-side, `resolve_TypeOfStatisticalProcessing_or_throw`, §23) in the §9.4 fakeDoubleLoop case, the parsed `stattype` block operation disagrees with the `innerTypeOfStatisticalProcessing` argument supplied by the caller | | 10.13 | `statisticalWindowCount >= 2` and `timeIncrementInSeconds == nullopt` | | 10.14 | `timeIncrementInSeconds` value < 0 after resolver normalization, or raw input value < 0 when `timeIncrementInSeconds_opt` is invoked (§7.9) | | 10.15 | `statisticalWindowCount > maxStatisticalWindows` (= 3) | | 10.16 | `stattype` block parsed with unknown period or operation token (raised by the shared parser, §22; period MUST be in `{mo, da}` and operation MUST be in `{av, mn, mx, sd}`) | | 10.17 | `stattype` blocks not in outermost-to-innermost order (e.g. `da_mo`) (raised by the shared parser, §22) | | 10.18 | any `StatisticalWindow` in `statisticalWindows[0..count)` has `unit` outside the §3.1 allow-list `{Second, Day, Month}` (e.g. `Hour`, `Hours6`, `Year`, `Missing`). **Two-level enforcement**: (a) the shared parser (§22) enforces the narrow `stattype`-grammar allow-list `{Day, Month}` at parse time; (b) the factory `make_ProductTime_or_throw` enforces the extended assembled-window allow-list `{Second, Day, Month}` after window assembly (the `Second` extension covers the innermost window when it originates from `timespan` rather than `stattype`). | | 10.19 | single-loop statistic detected, but MARS `class` or `stream` cannot be read, so the standard/fakeDoubleLoop representation policy cannot be evaluated (§7.8) | | 10.20 | standard single-loop statistic (`timespanKind == Duration`, no `stattype`) used for a `(class, stream)` combination that requires fakeDoubleLoop representation (§7.8) | | 10.21 | fakeDoubleLoop single-loop statistic (`timespan = none`, exactly one `stattype` block) used for a `(class, stream)` combination that does not require fakeDoubleLoop representation (§7.8) | The tri-equivalence check (10.5) subsumes several otherwise-separate checks (e.g. "instant with non-null increment", "statistical with zero-length window"); they are aggregated into a single invariant for clarity. --- ## 11. Error contract All failures use `Mars2GribDeductionException` with `Here()` for source location. The pattern matches sibling deductions (e.g. `typeOfStatisticalProcessing.h`): ```cpp try { /* internal logic, possibly throwing other deductions' exceptions */ } catch (...) { std::throw_with_nested( Mars2GribDeductionException("Unable to resolve ProductTime", Here())); } ``` Every check in §10 throws a short imperative message at its precise check site. The outer wrapper adds context. Inner exceptions form a chain visible at the catch boundary. No other exception type is introduced. No subclassing per failure category. --- ## 12. Logging contract `resolve_ProductTime_or_throw` MUST emit exactly **one** log line on successful completion, via `MARS2GRIB_LOG_RESOLVE`. The payload is built inline (no `operator<<` is provided; see §13) and lists every resolved field in a stable, greppable form. Indicative payload shape: ```text `ProductTime` resolved from input dictionaries: labelDateTime='...' \ initialConditionsDateTime='...' referenceDateTime='...' windowStart='...' windowEnd='...' \ statisticalWindowCount='N' statisticalWindows=['...','...'] timeIncrementInSeconds='...|missing' ``` No log emissions on intermediate sub-steps. No log emissions on failure (failures travel via the exception chain). --- ## 13. Serialization No `operator<<` is provided for `ProductTime` or `StatisticalWindow`. Both the RESOLVE log line (§12) and any inline error-message text build their string representation locally, in the same lambda style as `typeOfStatisticalProcessing.h`. Tests assert on individual fields rather than on whole-struct equality. --- ## 14. Thread-safety - `ProductTime` is immutable and trivially safe to share across threads. - `make_ProductTime_or_throw` and `resolve_ProductTime_or_throw` access no shared mutable state. Concurrent invocation on **disjoint** inputs is safe. - Concurrent invocation that shares the same `MarsDict_t` / `ParDict_t` / `OptDict_t` instances is safe iff those dictionary types' read operations are themselves thread-safe (this is the dictionary author's contract, not this module's). --- ## 15. Per-consumer field-access table The following table is **normative**: each consumer MUST read only the fields marked `R` and MUST NOT read any field marked `—`. The table is not enforced at the language level; reviewers and consumer-side tests are responsible. | Field | `referenceTime` | `pointInTime` | `statistics` | |-----------------------------|:---------------:|:-------------:|:------------:| | `labelDateTime` | R | — | R | | `initialConditionsDateTime` | R | — | R | | `referenceDateTime` | R | R | R | | `windowStart` | — | — | R | | `windowEnd` | — | R | R | | `statisticalWindows` | — | — | R | | `statisticalWindowCount` | — | — | R | | `timeIncrementInSeconds` | — | — | R | --- ## 16. Test plan The implementation MUST ship the following tests, alongside the existing `tests/mars2grib/...` layout (exact harness to match the in-place style). ### 16.1 Unit tests — happy paths One test per worked example in §17. All §17 examples use concrete numeric values, so each maps directly to a fixture. ### 16.2 Unit tests — hard errors One negative test per entry in §10. Each test name MUST cite the §10 entry number for traceability. ### 16.3 Operational regression sweep (pre-merge gate) A curated set of real MARS requests is processed by both the legacy `timeUtils.h`-based pipeline and the new `ProductTime` pipeline; outputs are compared at the GRIB byte level. - Requests that today produce **misaligned** calendar windows are EXPECTED to fail under the new pipeline (§4.4). Such failures are flagged in the test set, triaged, and forwarded upstream — they are not regressions. - All other requests MUST produce **bit-identical** GRIB output. Any byte difference is a regression that MUST be fixed before merge. The fixture loader is a placeholder; populating it with operational request samples is an environment task, not a source-code task. --- ## 17. Worked examples All values are concrete; each example is a unit-test fixture (§16.1). ### 17.1 Instant forecast product Input: ```text date = 20260501 time = 000000 step = 24 timespan = (missing) stattype = (missing) ``` Output: ```text labelDateTime = 2026-05-01 00:00:00 initialConditionsDateTime = 2026-05-01 00:00:00 referenceDateTime = 2026-05-01 00:00:00 windowEnd = 2026-05-02 00:00:00 windowStart = 2026-05-02 00:00:00 statisticalWindows = [] statisticalWindowCount = 0 timeIncrementInSeconds = std::nullopt ``` ### 17.2 Hindcast/reforecast product Input: ```text date = 20260501 time = 000000 hdate = 19930501 htime = (missing) step = 24 ``` Output: ```text labelDateTime = 2026-05-01 00:00:00 initialConditionsDateTime = 1993-05-01 00:00:00 referenceDateTime = 1993-05-01 00:00:00 windowEnd = 1993-05-02 00:00:00 windowStart = 1993-05-02 00:00:00 statisticalWindows = [] statisticalWindowCount = 0 timeIncrementInSeconds = std::nullopt ``` ### 17.3 Climate / reforecast anchor Input: ```text date = 20260501 time = 000000 hdate = 19930501 fcyear = 1993 fcmonth = 5 step = 24 ``` Output: ```text labelDateTime = 2026-05-01 00:00:00 initialConditionsDateTime = 1993-05-01 00:00:00 referenceDateTime = 1993-05-01 00:00:00 # Date(1993, 5, 1) windowEnd = 1993-05-02 00:00:00 windowStart = 1993-05-02 00:00:00 ``` Invariant §5.3 holds: `referenceDateTime == initialConditionsDateTime`. ### 17.4 Old-style single-loop statistic — hourly accumulation over 1h Input: ```text date = 20260501 time = 000000 step = 24 timespan = 1h stattype = (missing) timeIncrementInSeconds (par) = 3600 ``` Output: ```text windowEnd = 2026-05-02 00:00:00 windowStart = 2026-05-01 23:00:00 statisticalWindows = [StatisticalWindow{tables::TimeUnit::Second, 3600}] statisticalWindowCount = 1 timeIncrementInSeconds = 3600 ``` ### 17.5 Old-style multi-loop — monthly average of daily minimum of hourly accumulation Input (concrete values; replaces the previous textual placeholder): ```text date = 20260501 time = 000000 step = 744 # 31 days * 24 hours, end of May 2026 timespan = 1h stattype = moav_damn timeIncrementInSeconds (par) = 3600 ``` Output: ```text referenceDateTime = 2026-05-01 00:00:00 windowEnd = 2026-06-01 00:00:00 # day=1, alignment satisfied (§4.3) statisticalWindows = [ StatisticalWindow{tables::TimeUnit::Month, 1}, # outermost StatisticalWindow{tables::TimeUnit::Day, 1}, StatisticalWindow{tables::TimeUnit::Second, 3600} # innermost (= timespan) ] statisticalWindowCount = 3 windowStart = 2026-05-01 00:00:00 # windowEnd minus 1 calendar month timeIncrementInSeconds = 3600 ``` ### 17.6 New-style fakeDoubleLoop — monthly average Input: ```text date = 20260501 time = 000000 step = 744 timespan = none stattype = moav timeIncrementInSeconds (par) = 86400 paramId-implied operation = average # required to match 'av' (§9.4, §10.12); enforced inside resolve_TypeOfStatisticalProcessing_or_throw (§23) ``` Output: ```text referenceDateTime = 2026-05-01 00:00:00 windowEnd = 2026-06-01 00:00:00 # alignment satisfied statisticalWindows = [StatisticalWindow{tables::TimeUnit::Month, 1}] statisticalWindowCount = 1 windowStart = 2026-05-01 00:00:00 timeIncrementInSeconds = 86400 ``` ### 17.7 Invalid new-style multi-loop — must throw Input: ```text timespan = none stattype = moav_damn ``` Result: hard error per §10.8. ### 17.8 Analysis-time product (R1: step missing) Input: ```text date = 20260501 time = 000000 step = (missing) timespan = (missing) stattype = (missing) ``` Output: ```text labelDateTime = 2026-05-01 00:00:00 initialConditionsDateTime = 2026-05-01 00:00:00 referenceDateTime = 2026-05-01 00:00:00 windowEnd = 2026-05-01 00:00:00 # step defaults to 0 windowStart = 2026-05-01 00:00:00 statisticalWindows = [] statisticalWindowCount = 0 timeIncrementInSeconds = std::nullopt ``` ### 17.9 Default date/time from fcyear/fcmonth (R2) Input: ```text date = (missing) time = (missing) fcyear = 1993 fcmonth = 5 step = (missing) ``` Output: ```text labelDateTime = 1993-05-01 00:00:00 # defaulted from (fcyear, fcmonth, 1, 00:00:00) initialConditionsDateTime = 1993-05-01 00:00:00 # from labelDateTime, hdate absent referenceDateTime = 1993-05-01 00:00:00 # from (fcyear, fcmonth, 1, 00:00:00) windowEnd = 1993-05-01 00:00:00 # step defaults to 0 windowStart = 1993-05-01 00:00:00 statisticalWindows = [] statisticalWindowCount = 0 timeIncrementInSeconds = std::nullopt ``` All three datetime fields collapse to the same value — the natural consequence of the defaults. --- ## 18. Migration & cleanup plan ### 18.1 Files added - `src/metkit/mars2grib/backend/deductions/productTime.h` — public deduction header, exposing `resolve_ProductTime_or_throw`. Named after the concept produced (`ProductTime`), consistent with sibling deduction files in this directory. Lowercase initial per the §20 naming convention (function-primary). - `src/metkit/mars2grib/backend/deductions/detail/ProductTime.h` — types (`TimespanKind`, `ProductTimeInput`, `ProductTime`), factory (`make_ProductTime_or_throw`), and helpers (calendar arithmetic, alignment checks, signed-second shifts). UpperCamelCase initial per the §20 naming convention (type-primary). The shared `StatisticalWindow` type is no longer defined here; it lives in `detail/StatisticalWindow.h` (§21). The shared `stattype` parser is no longer defined here; it lives in `detail/StatType.h` (§22). - `src/metkit/mars2grib/backend/deductions/detail/StatisticalWindow.h` — shared header exposing the `StatisticalWindow` type (§21). - `src/metkit/mars2grib/backend/deductions/detail/StatType.h` — shared `stattype` parser exposing `parse_StatType_or_throw` and the `ParsedStatTypeBlock` type (§22). - `src/metkit/mars2grib/backend/deductions/typeOfStatisticalProcessing.h` — public deduction header, exposing `resolve_TypeOfStatisticalProcessing_or_throw` (§23). Lowercase initial per the §20 naming convention (function-primary). - Unit tests per §16.1 and §16.2. ### 18.2 Files rewritten - `src/metkit/mars2grib/backend/concepts/statistics/impl/statisticsDescriptor.h` — pure function `computeStatisticDescription(productTime, typeOfStatisticalProcessing)`. The existing `StatisticalProcessing` struct is preserved. The legacy `reserve(...)` bug (vectors are indexed without being resized) is fixed by switching to `resize(...)`. Header was relocated out of `deductions/` because it is a `concepts/`-side consumer-stage helper, not a deduction. ### 18.3 Files deleted The following deductions are subsumed by `ProductTime` and removed: - `src/metkit/mars2grib/backend/deductions/detail/timeUtils.h` - `src/metkit/mars2grib/backend/deductions/forecastTimeInSeconds.h` - `src/metkit/mars2grib/backend/deductions/timeSpanInSeconds.h` - `src/metkit/mars2grib/backend/deductions/numberOfTimeRanges.h` - `src/metkit/mars2grib/backend/deductions/referenceDateTime.h` - `src/metkit/mars2grib/backend/deductions/hindcastDateTime.h` `significanceOfReferenceTime.h` is **preserved**: it carries non-temporal semantics (derived from `mars::type`) orthogonal to `ProductTime` and remains a standalone deduction. `timeIncrementInSeconds.h` is **preserved**: its `timeIncrementInSeconds_opt` helper is consumed by the `ProductTime` resolver to populate the `ProductTime::timeIncrementInSeconds` field (§5). ### 18.4 Consumer files migrated The three consumer headers below were migrated to source all temporal data from `resolve_ProductTime_or_throw` per the per-consumer field-access table (§15): - `src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeEncoding.h` - `src/metkit/mars2grib/backend/concepts/reference-time/referenceTimeEncoding.h` - `src/metkit/mars2grib/backend/concepts/statistics/statisticsEncoding.h` ### 18.5 Build system `CMakeLists.txt` (or equivalent) entries that reference any deleted file are updated in the same change set as §18.3. ### 18.6 Breaking-change call-out The strict alignment rule (§4.4) is a behavioural change. Operational requests that today silently produce misaligned calendar windows will start throwing. This is the intended improvement; it MUST be flagged in release notes. --- ## 19. Locked-decision cross-reference Each numbered design decision recorded in this specification maps to one or more sections. The cross-reference is maintained for traceability; future amendments SHOULD update both columns. | Decision | Section(s) | |----------|---------------------------| | A1 (timespan=none semantics) | §6, §7.6, §9.4, §10.7, §10.8 | | A2 / B8 (strict calendar alignment) | §4.4, §10.9, §10.10 | | A3 (reference >= simulated) | §5.3, §10.4 | | A4 / C6 (instant tri-equivalence) | §4.1, §5.1, §9.1, §10.5 | | A5 / B1 (timeIncrementInSeconds in PT) | §5, §6, §7.9, §9.5 | | Q1 (fakeDoubleLoop paramId↔stattype, deduction-side) | §9.4, §10.12, §23 | | Single-loop standard/fakeDoubleLoop representation policy | §7.8, §9.2, §9.4, §10.19, §10.20, §10.21 | | B2 / C9 (stattype grammar) | §7.7, §10.16, §10.17, §22 | | B3 (bare numeric units = hours) | §7.5, §7.6 | | B4 (Date(fcyear, fcmonth, 1)) | §7.4 | | B5 (reuse Period/StatOp/StatTypeBlock) | §7.7, §18.1, §22 | | B6 (no sub-day calendar units) | §3.1 | | B7 (maxStatisticalWindows = 3) | §5, §10.15 | | B11 / B12 (per-consumer access table) | §15 | | B13 (StatisticalWindow.count > 0) | §3.2, §10.11 | | C13 (drop CalendarYears / restrict TimeUnit subset) | §3.1, §3.4, §10.18 | | StatisticalWindow uses tables::TimeUnit | §3, §3.1, §10.18, §21 | | StatisticalWindow shared header | §3, §21 | | Shared stattype parser (Option A) | §22 | | typeOfStatisticalProcessing deduction | §23 | | Filename / function naming convention | §20 | | D1 (OptDict_t pass-through, unused) | §8 | | D2 (nested Mars2GribDeductionException) | §11 | | D3 (single composite RESOLVE log) | §12 | | D4 (thread-safety paragraph) | §14 | | D5 (no operator<<) | §13 | | D6 (full test coverage) | §16, §17 | | D7 (hard cut for strict alignment) | §4.4, §18.6 | | D10 (step = 0 allowed uniformly) | §7.5 | | R1 (step optional, default 0) | §7.1, §7.5, §10.1, §17.8 | | R2 (date/time defaulted from fcyear/fcmonth when both missing) | §7.1, §7.2, §10.1, §17.9 | | C-list (cosmetic cleanups) | applied throughout | --- ## 20. Filename and function-name naming convention This section is **normative** and applies to every header and function added or renamed by this specification. ### 20.1 Filenames Headers under `deductions/` and `deductions/detail/` follow a single rule based on what the file **primarily exposes**: | Primary export | Filename initial | Examples | |----------------|------------------|--------------------------------------------------------------------| | A function | lowercase | `productTime.h`, `typeOfStatisticalProcessing.h` | | A type | UpperCamelCase | `detail/ProductTime.h`, `detail/StatisticalWindow.h`, `detail/StatType.h` | Public deduction headers under `deductions/` are typically function-primary (they expose `resolve__or_throw`) and therefore lowercase. Detail headers under `deductions/detail/` are typically type-primary (they expose the data type the public header resolves to) and therefore UpperCamelCase. A file MUST NOT be renamed once published; this convention applies only to files added or replaced by this specification. ### 20.2 Function names Functions in the deductions layer follow the pattern: ``` __ ``` where: - `` is the operation: `resolve`, `parse`, `make`, `convert`, … - `` is the produced concept or type, in UpperCamelCase: `ProductTime`, `StatType`, `TypeOfStatisticalProcessing`, `Date`, … - `` is the failure policy: `or_throw`, `opt`, … Examples already in the codebase: ``` resolve_ProductTime_or_throw make_ProductTime_or_throw (factory) parse_StatType_or_throw resolve_TypeOfStatisticalProcessing_or_throw convert_YYYYMMDD2Date_or_throw ``` The convention is **observed**, not enforced by tooling; reviewers are responsible. --- ## 21. Shared `StatisticalWindow` type header ### 21.1 Header ``` src/metkit/mars2grib/backend/deductions/detail/StatisticalWindow.h ``` UpperCamelCase initial per §20.1 (type-primary). ### 21.2 Definition ```cpp #include "metkit/mars2grib/backend/tables/timeUnits.h" namespace metkit::mars2grib::backend::deductions::detail { struct StatisticalWindow { tables::TimeUnit unit{tables::TimeUnit::Second}; long count{0}; }; } // namespace metkit::mars2grib::backend::deductions::detail ``` The type carries no methods, no `operator<<` (per §13), and no validation; all validation is performed by the consumers (the parser at parse time, §22; the factory at assembly time, §10.18). ### 21.3 Consumers - `deductions/detail/StatType.h` (§22) — produces values of this type. - `deductions/detail/ProductTime.h` — stores values of this type inside `ProductTime::statisticalWindows`. - `deductions/typeOfStatisticalProcessing.h` (§23) — does NOT consume this type (it consumes only the operation field of `ParsedStatTypeBlock`). ### 21.4 Rationale The type is a shared primitive consumed by both `ProductTime` and the `stattype` parser. It lives in its own detail header, rather than inside `detail/ProductTime.h` or `detail/StatType.h`, so both detail components can include the shared primitive without coupling either one to the other's implementation header. --- ## 22. Shared `stattype` parser ### 22.1 Header ``` src/metkit/mars2grib/backend/deductions/detail/StatType.h ``` UpperCamelCase initial per §20.1 (type-primary; the file exposes the `ParsedStatTypeBlock` type alongside the parser function). ### 22.2 Type ```cpp #include "metkit/mars2grib/backend/deductions/detail/StatisticalWindow.h" #include "metkit/mars2grib/backend/tables/typeOfStatisticalProcessing.h" namespace metkit::mars2grib::backend::deductions::detail { struct ParsedStatTypeBlock { StatisticalWindow timeWindow; tables::TypeOfStatisticalProcessing typeOfStatisticalProcessing; }; } // namespace metkit::mars2grib::backend::deductions::detail ``` The `timeWindow` field carries the **period** part of the block (the stattype-grammar pair `(period_unit, count)`). The `typeOfStatisticalProcessing` field carries the **operation** part, already mapped to a value of GRIB Code Table 4.10. ### 22.3 Function ```cpp namespace metkit::mars2grib::backend::deductions::detail { std::vector parse_StatType_or_throw(std::string_view value); } // namespace metkit::mars2grib::backend::deductions::detail ``` ### 22.4 Contract - **Input**: the raw textual value of the MARS `stattype` keyword. Callers MUST NOT invoke the parser when the `stattype` key is absent from the MARS dictionary; the absent case is handled at the caller. - **Output**: a `std::vector` of length **1 or 2**, in MARS textual order. Index 0 is the leftmost block, which corresponds to the **outermost** loop in `ProductTime::statisticalWindows` (matching the spec-wide outer→inner convention). - **Block-count limit**: the parser MUST reject values with zero blocks or more than two blocks. The cap of two blocks is the locked grammar limit; widening it is an explicit spec amendment. ### 22.5 Allow-lists (parser-level enforcement) The parser MUST reject blocks that violate the locked `stattype` grammar: | Field | Allow-list | Mapped to | |------------|-------------------------------------------------|-------------------------------------------------| | period unit| `mo`, `da` | `tables::TimeUnit::Month`, `tables::TimeUnit::Day` | | operation | `av`, `mn`, `mx`, `sd` | `tables::TypeOfStatisticalProcessing::Average`, `::Minimum`, `::Maximum`, `::StandardDeviation` | | count | positive integer | `long` | Period units `Second`, `Hour`, `Year`, `Missing`, etc. are **not** legal in the `stattype` grammar and the parser MUST reject them at parse time. (The extended `Second`-inclusive allow-list applies only to the assembled `statisticalWindows` array inside `ProductTime`, where the innermost window may originate from `timespan`; see §10.18 for the two-level enforcement narrative.) ### 22.6 Ordering check The parser MUST reject `stattype` values whose blocks are not in outermost-to-innermost period-unit order — i.e., a `da` block MUST NOT precede a `mo` block. This is hard error §10.17. ### 22.7 Hard errors raised by the parser | Spec entry | Condition | |------------|-------------------------------------------------------------------| | §10.16 | unknown period or operation token | | §10.17 | blocks not in outermost-to-innermost order | | §10.18 (a) | period unit outside the narrow `stattype` allow-list `{Day, Month}` | All other §10 errors are raised by the factory, not the parser. ### 22.8 Rationale A single shared parser eliminates the drift risk between `productTime` and `typeOfStatisticalProcessing` (which would otherwise need to size their output arrays in lock-step from independent parses of the same string). The parser is shared infrastructure, not a deduction; "self-contained deduction" means *no dependency on other deductions*, not *no dependency on shared parsing primitives*. --- ## 23. `typeOfStatisticalProcessing` deduction ### 23.1 Header ``` src/metkit/mars2grib/backend/deductions/typeOfStatisticalProcessing.h ``` Lowercase initial per §20.1 (function-primary). Named after the GRIB key produced (`typeOfStatisticalProcessing`), consistent with the codebase convention "filename = concept produced". The header is **self-contained**: it depends on `detail/StatType.h` (§22) and `tables/typeOfStatisticalProcessing.h`, but does NOT depend on `ProductTime`, `detail/ProductTime.h`, or any other deduction. ### 23.2 Prototype ```cpp #include "metkit/mars2grib/backend/tables/typeOfStatisticalProcessing.h" namespace metkit::mars2grib::backend::deductions { template std::vector resolve_TypeOfStatisticalProcessing_or_throw( tables::TypeOfStatisticalProcessing innerTypeOfStatisticalProcessing, const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt); } // namespace metkit::mars2grib::backend::deductions ``` `OptDict_t` is passed for signature symmetry with sibling deductions; it is not currently read. ### 23.3 Implicit invariant — array length and ordering **Normative.** The output `std::vector` has length and order **identical** to `ProductTime::statisticalWindows` resolved from the same MARS input — outermost loop at index 0, innermost loop at index `size() - 1`. This invariant is **not** verified at runtime (this deduction never reads `ProductTime`); callers MUST rely on it when zipping the two vectors. The invariant is preserved by the locked `stattype` grammar, the locked window-assembly cases (§9), and the case-by-case output sizing rules in §23.5. ### 23.4 Precondition (discharged by the type system) The function takes a `tables::TypeOfStatisticalProcessing` argument. A caller cannot construct this argument without having already identified the product as statistical (i.e., having decoded the inner statistical operation from `paramId`). Therefore the deduction is **structurally unreachable** for instant products (§9.1). No runtime check is performed for this case. ### 23.5 Output sizing per case The deduction inspects MARS locally (no `ProductTime` dependency) to classify the input into one of the §9 cases, then sizes its output accordingly: | MARS state | §9 case | Output size | Output content | |---------------------------------------------------------|---------|------------:|---------------------------------------------------------------------------------| | `stattype` absent, `timespan` is a Duration | §9.2 | 1 | `[innerTypeOfStatisticalProcessing]` | | `stattype` has N blocks (N ∈ {1, 2}), `timespan` is a Duration | §9.3 | N + 1 | `[op(block_0), …, op(block_{N-1}), innerTypeOfStatisticalProcessing]` | | `stattype` has exactly 1 block, `timespan = "none"` | §9.4 | 1 | `[innerTypeOfStatisticalProcessing]` (after equality assertion, §23.6) | | `stattype` absent, `timespan` absent | §9.1 | unreachable | (precondition violated, see §23.4 — no defensive check) | Where: - `op(block_i)` is the `typeOfStatisticalProcessing` field of the `i`-th `ParsedStatTypeBlock` returned by `parse_StatType_or_throw` (§22). - The innermost slot is **always** filled by the `innerTypeOfStatisticalProcessing` argument — never by a parsed block — except for the §9.4 fakeDoubleLoop case where the single slot is filled by the argument *after* the equality assertion in §23.6. ### 23.6 fakeDoubleLoop equality assertion (§9.4) In the §9.4 case, the single parsed `stattype` block carries a redundantly encoded operation. The deduction MUST assert: ```text parsedBlocks[0].typeOfStatisticalProcessing == innerTypeOfStatisticalProcessing ``` Disagreement is hard error §10.12. After the assertion succeeds, the single output slot is filled by `innerTypeOfStatisticalProcessing`. The two values are equal in this branch by construction; using the argument keeps a single authoritative source for the inner operation across all cases (§9.2, §9.3, §9.4). ### 23.7 Outer/inner asymmetry **Normative.** - **Outer slots** (indices `0 .. size()-2` in the §9.3 case) are constrained to `{Average, Minimum, Maximum, StandardDeviation}` by the parser-level op allow-list (§22.5). No other GRIB Code Table 4.10 value can appear in an outer slot via the `stattype` path. - **Innermost slot** (always the last index) is **unrestricted** within GRIB Code Table 4.10. It can be any value the caller passes — typically `Accumulation`, `Difference`, `Ratio`, `Average`, etc. — derived from `paramId` outside this deduction's scope. Callers consuming the resulting vector against GRIB encoding rules MUST take this asymmetry into account. ### 23.8 Hard errors | Spec entry | Condition | |------------|----------------------------------------------------------------------------------------------| | §10.12 | §9.4 case with parsed block operation ≠ `innerTypeOfStatisticalProcessing` (§23.6) | | §10.16 | propagated from `parse_StatType_or_throw` (§22.7) | | §10.17 | propagated from `parse_StatType_or_throw` (§22.7) | | §10.18 (a) | propagated from `parse_StatType_or_throw` (§22.7) — narrow `stattype` unit allow-list | | §10.6 | `stattype` present but `timespan` missing (caller-side / consumer-side; this deduction does NOT classify timespan presence beyond what is needed for §23.5; the `timespan`-only checks remain on the `ProductTime` resolver) | This deduction does **not** raise §10.6, §10.7, or §10.8 directly — those are `ProductTime`-resolver responsibilities. Misclassification of cases at this level (e.g., MARS that would fail §10.7 in `ProductTime`) is undefined behavior here; callers are expected to invoke this deduction only on inputs that also resolve cleanly through `resolve_ProductTime_or_throw`. ### 23.9 Error contract Identical to §11. All failures use `Mars2GribDeductionException` with `Here()` for source location and a nested wrapper: ```cpp try { /* parser call, classification, equality assertion */ } catch (...) { std::throw_with_nested( Mars2GribDeductionException( "Unable to resolve typeOfStatisticalProcessing", Here())); } ``` ### 23.10 Logging contract `resolve_TypeOfStatisticalProcessing_or_throw` MUST emit exactly **one** log line on successful completion, via `MARS2GRIB_LOG_RESOLVE`. Indicative payload: ```text `typeOfStatisticalProcessing` resolved from input dictionaries: \ innerTypeOfStatisticalProcessing='...' size='N' \ typesOfStatisticalProcessing=['...','...','...'] ``` No log emissions on intermediate sub-steps. No log emissions on failure. ### 23.11 Worked examples #### 23.11.1 §9.2 — single-loop hourly accumulation Input: ```text mars.stattype = (missing) mars.timespan = "1h" innerTypeOfStatisticalProcessing = Accumulation ``` Output: ```text [Accumulation] # size 1; matches ProductTime::statisticalWindows = [{Second, 3600}] ``` #### 23.11.2 §9.3 — multi-loop monthly average of daily minimum of hourly accumulation Input: ```text mars.stattype = "moav_damn" mars.timespan = "1h" innerTypeOfStatisticalProcessing = Accumulation ``` Output: ```text [Average, Minimum, Accumulation] # size 3; matches ProductTime::statisticalWindows = [{Month,1}, {Day,1}, {Second,3600}] ``` #### 23.11.3 §9.4 — fakeDoubleLoop monthly average Input (success): ```text mars.stattype = "moav" mars.timespan = "none" innerTypeOfStatisticalProcessing = Average ``` Output: ```text [Average] # size 1; equality assertion (§23.6) passes ``` Input (failure): ```text mars.stattype = "moav" mars.timespan = "none" innerTypeOfStatisticalProcessing = Maximum ``` Result: hard error §10.12 (parsed `Average` ≠ argument `Maximum`).metkit-1.19.2/src/metkit/mars2grib/backend/deductions/lengthOfTimeWindow.h0000664000175000017500000001422715232567717026744 0ustar alastairalastair/* * (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 lengthOfTimeWindow.h /// @brief Deduction of the GRIB lengthOfTimeWindow (in seconds). /// /// This header defines the deduction used by the mars2grib backend to resolve /// the GRIB lengthOfTimeWindow key from input dictionaries. /// /// The deduction reads the parameter dictionary entry lengthOfTimeWindow, /// 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 `lengthOfTimeWindow` expressed in seconds. /// /// This deduction determines the value of the GRIB `lengthOfTimeWindow` /// (in seconds) based on the parameter dictionary key `lengthOfTimeWindow`. /// /// The deduction follows these rules: /// /// - If the key `lengthOfTimeWindow` 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::lengthOfTimeWindow` 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::lengthOfTimeWindow` 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 `lengthOfTimeWindow` 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_LengthOfTimeWindowInSeconds_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Big assumption here: // - lengthOfTimeWindow is in hours if (has(par, "lengthOfTimeWindow")) { long lengthOfTimeWindowInHoursVal = get_or_throw(par, "lengthOfTimeWindow"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`lengthOfTimeWindow` resolved from input dictionaries: value='"; logMsg += std::to_string(lengthOfTimeWindowInHoursVal) + "' [hours]"; return logMsg; }()); // Success exit point return {lengthOfTimeWindowInHoursVal * 3600}; // Convert hours to seconds } else { // Emit DEFAULT log entry MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`lengthOfTimeWindow` defaulted to MISSING (nullopt)"; return logMsg; }()); // Success exit point return std::nullopt; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Unable to get `lengthOfTimeWindow` from Par dictionary", Here())); }; // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/periodItMin.h0000664000175000017500000001125015232567717025403 0ustar alastairalastair/* * (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 periodItMin.h /// @brief Deduction of the minimum wave period index (`iTmin`). /// /// This header defines deduction utilities used by the mars2grib backend /// to retrieve the **minimum wave period index** (`iTmin`) from input /// dictionaries. /// /// The deduction treats `iTmin` as an optional parameter: /// - if present in the parameter dictionary, the value is returned /// - if absent, no default is applied and an empty optional is returned /// /// No semantic validation or consistency checking is performed at this /// level. /// /// Error handling follows a strict fail-fast strategy: /// - unexpected access errors 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 presence or absence resolved from input dictionaries /// /// @section References /// Concept: /// - @ref waveEncoding.h /// /// Related deductions: /// - @ref periodItMax.h /// - @ref waveFrequencyGrid.h /// - @ref waveFrequencyNumber.h /// - @ref waveDirectionGrid.h /// - @ref waveDirectionNumber.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #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 optional minimum wave period index (`iTmin`). /// /// @section Deduction contract /// - Reads: `par["iTmin"]` (optional) /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws on unexpected errors /// /// This deduction retrieves the optional wave period index `iTmin` /// from the parameter dictionary. /// /// If the key is present, the value is returned wrapped in a /// `std::optional`. If the key is absent, an empty optional is returned. /// No defaulting, inference, or semantic validation is applied. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused). /// /// @tparam ParDict_t /// Type of the parameter dictionary. May contain `iTmin`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary from which `iTmin` may be retrieved. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// An optional containing `iTmin` if present; otherwise an empty /// optional. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If any unexpected error occurs during dictionary access. /// /// @note /// This deduction performs no semantic validation of the retrieved /// value. /// template std::optional resolve_PeriodItMin_opt(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Retrieve optional minimum wave period index from parameter dictionary std::optional itMinOpt = get_opt(par, "iTmin"); if (itMinOpt.has_value()) { // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`iTmin` resolved from input dictionaries: value='"; logMsg += std::to_string(itMinOpt.value()); logMsg += "'"; return logMsg; }()); } else { // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`iTmin` resolved from input dictionaries: value not present"; return logMsg; }()); } // Success exit point return itMinOpt; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `iTmin` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/significanceOfReferenceTime.h0000664000175000017500000001526515232567717030537 0ustar alastairalastair/* * (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 significanceOfReferenceTime.h /// @brief Deduction of the GRIB `significanceOfReferenceTime` identifier. /// /// This header defines the deduction responsible for resolving the /// GRIB `significanceOfReferenceTime` key, which describes the semantic /// meaning of the GRIB reference time. /// /// The value is deterministically deduced from the MARS request, /// specifically from the `mars::type` key, according to established /// ECMWF/MARS conventions. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - validate against external GRIB code tables beyond explicit mappings /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value deduced deterministically from input dictionaries /// /// @section References /// Concept: /// - @ref referenceTimeEncoding.h /// /// Related deductions: /// - @ref productTime.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include // Tables includes #include "metkit/mars2grib/backend/tables/significanceOfReferenceTime.h" #include "metkit/mars2grib/utils/generalUtils.h" // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the GRIB `significanceOfReferenceTime` key. /// /// This deduction resolves the GRIB `significanceOfReferenceTime` /// identifier based on the value of the MARS key `type`. /// /// Resolution rules: /// - Analysis-like MARS types map to /// `SignificanceOfReferenceTime::Analysis` /// - Forecast-like MARS types map to /// `SignificanceOfReferenceTime::ForecastStart` /// /// The mapping is explicit and exhaustive. Any unsupported MARS /// `type` value results in a deduction error. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary; must contain the key `type` /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return The resolved `SignificanceOfReferenceTime` enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - `mars::type` is missing /// - `mars::type` cannot be mapped to a supported significance /// - any unexpected error occurs during deduction /// /// @note /// This deduction is fully deterministic and does not rely on any /// pre-existing GRIB header state. /// template tables::SignificanceOfReferenceTime resolve_SignificanceOfReferenceTime_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 { auto significanceOfReferenceTime = tables::SignificanceOfReferenceTime::Missing; // Retrieve mandatory type from Mars dictionary auto marsStream = get_or_throw(mars, "stream"); auto marsType = get_or_throw(mars, "type"); constexpr std::array analysisTypes = { {"an", "ia", "oi", "3v", "3g", "4g", "ea", "pa", "tpa", "ga", "gai", "ai", "af", "ab", "oai", "ga", "gai"}}; constexpr std::array forecastTypes = { {"fc", "cf", "pf", "cm", "fp", "em", "ep", "es", "fa", "efi", "efic", "bf", "cd", "wem", "wes", "cr", "ses", "taem", "taes", "sg", "sf", "if", "fcmean", "fcmax", "fcmin", "fcstdev", "ssd", "tf", "bf", "cd", "hcmean", "s3", "si", "gbf", "gwt", "est", "icp", "pfc", "sot"}}; constexpr std::array startOfDataAssimilationTypes = {{"4i", "4v", "me", "eme"}}; if (marsType == "fc" && (marsStream == "clte" || marsStream == "clmn")) { // ClimateDT and ExtremesDT significanceOfReferenceTime = tables::SignificanceOfReferenceTime::ForecastVerification; } else if (std::any_of(analysisTypes.begin(), analysisTypes.end(), [&marsType](auto v) { return marsType == v; })) { significanceOfReferenceTime = tables::SignificanceOfReferenceTime::Analysis; } else if (std::any_of(forecastTypes.begin(), forecastTypes.end(), [&marsType](auto v) { return marsType == v; })) { significanceOfReferenceTime = tables::SignificanceOfReferenceTime::ForecastStart; } else if (std::any_of(startOfDataAssimilationTypes.begin(), startOfDataAssimilationTypes.end(), [&marsType](auto v) { return marsType == v; })) { significanceOfReferenceTime = tables::SignificanceOfReferenceTime::AssimilationStart; } else { // Unhandled cases throw Mars2GribDeductionException( "Failed to resolve `significanceOfReferenceTime` from MARS type: " + marsType, Here()); } // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`significanceOfReferenceTime` resolved from input dictionaries: value='"; logMsg += tables::enum2name_SignificanceOfReferenceTime_or_throw(significanceOfReferenceTime); logMsg += "'"; return logMsg; }()); /// Success exit point return significanceOfReferenceTime; } catch (...) { std::throw_with_nested(Mars2GribDeductionException( "Failed to resolve `significanceOfReferenceTime` from input dictionaries", Here())); } // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/subCentre.h0000664000175000017500000000771415232567717025124 0ustar alastairalastair/* * (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 subCentre.h /// @brief Deduction of the GRIB `subCentre` identifier. /// /// This header defines the deduction responsible for resolving the /// GRIB `subCentre` key, which identifies the originating sub-centre /// within a GRIB-producing centre. /// /// The value is obtained from the parameter dictionary when provided. /// If absent, the deduction applies an explicit and deterministic /// default according to GRIB conventions. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values from MARS metadata /// - apply implicit or hidden defaults /// - validate against official GRIB code tables /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained or defaulted from input dictionaries /// /// @section References /// Concept: /// - @ref originEncoding.h /// /// Related deductions: /// - @ref centre.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 GRIB `subCentre` key. /// /// This deduction resolves the GRIB `subCentre` identifier using the /// parameter dictionary. /// /// Resolution rules: /// - If `par::subCentre` is present, its value is used directly. /// - If `par::subCentre` is absent, the value defaults explicitly to `0`, /// corresponding to the GRIB convention for an unspecified sub-centre. /// /// No inference from MARS metadata is performed. /// /// @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; may contain `subCentre` /// @param[in] opt Options dictionary (unused) /// /// @return The resolved GRIB `subCentre` value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If an unexpected error occurs during dictionary access /// /// @note /// This deduction is fully deterministic and does not depend on /// any pre-existing GRIB header state. /// template long resolve_SubCentre_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Retrieve optional subCentre from parameter dictionary long subCentre = get_opt(par, "subCentre").value_or(0L); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`subCentre` resolved from input dictionaries: value='"; logMsg += std::to_string(subCentre); logMsg += "'"; return logMsg; }()); // Success exit point return subCentre; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `subCentre` from input dictionaries", Here())); } }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/typeOfStatisticalProcessing.h0000664000175000017500000003437515232567717030705 0ustar alastairalastair/* * (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 typeOfStatisticalProcessing.h /// @brief Public deduction header for `typeOfStatisticalProcessing`. /// /// Exposes `resolve_TypeOfStatisticalProcessing_or_throw`, which produces a /// `std::vector` describing the /// statistical processing applied at each time loop of a statistical /// product, in outer→inner order. /// /// **Self-contained**: this deduction does NOT depend on `ProductTime`. /// It depends only on: /// - `detail/StatType.h` — the shared `stattype` parser (§22) /// - `tables/typeOfStatisticalProcessing.h` — GRIB Code Table 4.10 enum /// /// **Implicit invariant** (well-documented; not enforced at runtime): /// the output `std::vector` has length and order **identical** to /// `ProductTime::statisticalWindows` resolved from the same MARS input — /// outermost loop at index 0, innermost loop at index `size() - 1`. /// Callers MUST rely on this invariant when zipping the two vectors. /// /// **Precondition discharged by the type system**: the function takes a /// `tables::TypeOfStatisticalProcessing` argument; a caller cannot /// construct this argument without having already identified the product as /// statistical (i.e., having decoded the inner statistical operation from /// `paramId`). Therefore this deduction is structurally unreachable for /// instant products (§9.1); no runtime check is performed for that case. /// /// See `deductions/timeProducts.md` §23 for the full normative /// specification. /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include #include #include // eckit #include "eckit/exception/Exceptions.h" // Project utilities #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" // Detail (shared parser) #include "metkit/mars2grib/backend/deductions/detail/StatType.h" // Tables #include "metkit/mars2grib/backend/tables/typeOfStatisticalProcessing.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the per-loop GRIB `typeOfStatisticalProcessing` array for /// one MARS statistical product. /// /// @section Deduction contract /// - Reads (MARS): `stattype`, `timespan` (presence only — for case /// classification per §23.5) /// - Reads (par): none /// - Reads (opt): none (signature-only, reserved) /// - Writes: none /// - Side effects: one `MARS2GRIB_LOG_RESOLVE` line on success /// - Failure mode: throws `Mars2GribDeductionException` (nested-with) /// /// @section Output sizing per case (§23.5) /// /// Local classification of MARS into one of the §9 cases (no `ProductTime` /// dependency): /// /// | MARS state | §9 case | Output size | Output content | /// |---------------------------------------------------------|---------|------------:|---------------------------------------------------------------------------------| /// | `stattype` absent, `timespan` is a Duration | §9.2 | 1 | /// `[innerTypeOfStatisticalProcessing]` | | `stattype` has N blocks, /// `timespan` is a Duration | §9.3 | N + 1 | `[op(block_0), …, op(block_{N-1}), /// innerTypeOfStatisticalProcessing]` | | `stattype` has 1 block, `timespan = "none"` | §9.4 | /// 1 | `[innerTypeOfStatisticalProcessing]` (after equality assertion, §23.6) | | `stattype` absent, /// `timespan` absent | §9.1 | unreachable | (precondition violated, see §23.4 — no defensive check) | /// /// The innermost slot is **always** filled by the /// `innerTypeOfStatisticalProcessing` argument — never by a parsed block. /// /// @section fakeDoubleLoop equality assertion (§23.6) /// /// In the §9.4 case, the single parsed `stattype` block carries a /// redundantly encoded operation. This deduction asserts: /// /// parsedBlocks[0].typeOfStatisticalProcessing /// == innerTypeOfStatisticalProcessing /// /// Disagreement is hard error §10.12. /// /// @section Outer/inner asymmetry (§23.7) /// /// - **Outer slots** (indices 0..size()-2 in the §9.3 case) are /// constrained to `{Average, Minimum, Maximum, StandardDeviation}` by /// the parser-level op allow-list (§22.5). /// - **Innermost slot** is **unrestricted** within GRIB Code Table 4.10; /// it can be any value the caller passes. /// /// @tparam MarsDict_t MARS dictionary type. /// @tparam ParDict_t Parameter dictionary type (currently unused). /// @tparam OptDict_t Options dictionary type (currently unused). /// /// @param[in] innerTypeOfStatisticalProcessing /// The pre-resolved inner statistical processing type, derived by /// the caller from `paramId`. /// @param[in] mars /// MARS dictionary providing `stattype` and `timespan`. /// @param[in] par /// Parameter dictionary (signature-only). /// @param[in] opt /// Options dictionary (signature-only). /// /// @return A `std::vector` whose length /// and order match `ProductTime::statisticalWindows` for the same /// MARS input (implicit invariant, §23.3). /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// on: /// - §10.12 — fakeDoubleLoop equality violation; /// - §10.16, §10.17, §10.18 (a) — propagated from /// `parse_StatType_or_throw` (§22.7). /// All failures are wrapped via `std::throw_with_nested`. /// /// @note This function does NOT classify `timespan` exhaustively against the /// §9 case table; it only distinguishes the three presence-states /// (absent, Duration, "none") needed for §23.5 sizing. Misclassification /// at this level (e.g. inputs that would fail §10.7 at the `ProductTime` /// resolver) is undefined behavior here; callers are expected to invoke /// this deduction only on inputs that also resolve cleanly through /// `resolve_ProductTime_or_throw`. /// template std::vector resolve_TypeOfStatisticalProcessing_or_throw( tables::TypeOfStatisticalProcessing innerTypeOfStatisticalProcessing, const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; // Suppress "unused parameter" warnings while preserving the documented // signature (par and opt are reserved for future use). (void)par; (void)opt; try { // ========================================================= // Local case classification (§23.5) // // We look at presence of `stattype` and the textual value of // `timespan` to distinguish §9.2 / §9.3 / §9.4. Full §9-case // dispatch (with hard errors §10.6 / §10.7 / §10.8) remains the // responsibility of the `ProductTime` resolver; this deduction // only needs enough classification to size its output. // ========================================================= const bool hasStatType = has(mars, "stattype"); // Classify timespan: missing | "none" | Duration. // We do NOT parse the duration value here; only its presence and // the literal "none" matter for §23.5 sizing. bool hasTimespanNone = false; bool hasTimespanDuration = false; if (has(mars, "timespan")) { std::optional tsStr = get_opt(mars, "timespan"); if (tsStr.has_value()) { if (tsStr.value() == "none") { hasTimespanNone = true; } else { hasTimespanDuration = true; } } else { // Numeric-only timespan: a Duration per §7.6. hasTimespanDuration = true; } } // ========================================================= // Parse `stattype` once (shared parser, §22). // ========================================================= std::vector blocks; if (hasStatType) { const std::string statTypeVal = get_or_throw(mars, "stattype"); blocks = detail::parse_StatType_or_throw(statTypeVal); } // ========================================================= // Output assembly per §23.5. // // Default-fallback path: when MARS does not match any of the // statistical cases (§9.2 / §9.3 / §9.4) we still emit a size-1 // vector containing the inner type. Other case-table violations // (e.g. §10.6 / §10.7 / §10.8) are not raised here; they are // raised by the `ProductTime` resolver when the caller invokes it // on the same MARS input (per the contract in §23.8 and the // class-level note above). // ========================================================= std::vector out; if (!hasStatType && hasTimespanDuration) { // ----- §9.2: Old-style single-loop ----- out.reserve(1); out.push_back(innerTypeOfStatisticalProcessing); } else if (hasStatType && hasTimespanDuration) { // ----- §9.3: Old-style multi-loop ----- out.reserve(blocks.size() + 1); for (const detail::ParsedStatTypeBlock& b : blocks) { out.push_back(b.typeOfStatisticalProcessing); } out.push_back(innerTypeOfStatisticalProcessing); } else if (hasStatType && hasTimespanNone) { // ----- §9.4: New-style fakeDoubleLoop ----- // // Equality assertion (§23.6 / §10.12). Block count is // structurally guaranteed to be 1 here under the spec; if the // caller has supplied input that would fail §10.8 at the // `ProductTime` resolver (more than one block), the assertion // below acts on blocks[0] only — the multi-block error itself // is reported by the `ProductTime` resolver. if (blocks.empty()) { // §10.7: timespan='none' with no stattype block. // Defensive only — the parser rejects empty `stattype`, // and `hasStatType` is true here, so blocks.size() >= 1. throw Mars2GribDeductionException( "typeOfStatisticalProcessing invariant violated [§10.7]: " "timespan='none' requires exactly one stattype block, got 0", Here()); } const tables::TypeOfStatisticalProcessing parsedOp = blocks[0].typeOfStatisticalProcessing; if (parsedOp != innerTypeOfStatisticalProcessing) { throw Mars2GribDeductionException( std::string("typeOfStatisticalProcessing invariant violated " "[§10.12]: fakeDoubleLoop disagreement: parsed " "stattype block operation ('") + tables::enum2name_TypeOfStatisticalProcessing_or_throw(parsedOp) + "') != innerTypeOfStatisticalProcessing argument ('" + tables::enum2name_TypeOfStatisticalProcessing_or_throw(innerTypeOfStatisticalProcessing) + "')", Here()); } out.reserve(1); out.push_back(innerTypeOfStatisticalProcessing); } else { // Fallback: any other MARS shape (instant, or shapes that // would fail at the `ProductTime` resolver). Per §23.4 the // function is not expected to be called on such inputs; // produce a size-1 vector with the inner type so the caller // sees a defined result if they invoke us defensively. // // Strict §9-case enforcement is the `ProductTime` resolver's // responsibility; we deliberately do not duplicate §10.6 / // §10.7 / §10.8 here. out.reserve(1); out.push_back(innerTypeOfStatisticalProcessing); } // ========================================================= // §23.10: composite RESOLVE log line (success path only) // ========================================================= MARS2GRIB_LOG_RESOLVE([&]() { std::string msg = "`typeOfStatisticalProcessing` resolved from input dictionaries: "; msg += "innerTypeOfStatisticalProcessing='" + tables::enum2name_TypeOfStatisticalProcessing_or_throw(innerTypeOfStatisticalProcessing) + "'"; msg += " size='" + std::to_string(out.size()) + "'"; msg += " typesOfStatisticalProcessing=["; for (std::size_t i = 0; i < out.size(); ++i) { if (i) { msg += ","; } msg += "'" + tables::enum2name_TypeOfStatisticalProcessing_or_throw(out[i]) + "'"; } msg += "]"; return msg; }()); return out; } catch (...) { // §11 / §23.9: nested rethrow with context. std::throw_with_nested(Mars2GribDeductionException("Unable to resolve typeOfStatisticalProcessing", Here())); } // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/channel.h0000664000175000017500000001131615232567717024573 0ustar alastairalastair/* * (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 channel.h /// @brief Deduction of the instrument channel identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **instrument channel identifier** from MARS metadata. /// /// The deduction retrieves the channel identifier directly from the /// MARS dictionary and exposes it to the encoding layer without /// transformation or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, defaulting, or consistency checks /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref satelliteEncoding.h /// /// Related deductions: /// - @ref instrumentType.h /// - @ref satelliteNumber.h /// - @ref satelliteSeries.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 instrument channel identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["channel"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the instrument channel identifier by retrieving /// the mandatory MARS key `channel` and returning its value as a `long`. /// /// No semantic interpretation, normalization, or validation is performed /// beyond basic type conversion. The meaning of the channel identifier is /// defined by upstream metadata conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `channel` /// and conversion to `long`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the channel identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved instrument channel identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `channel` is missing, cannot be converted to `long`, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult instrument metadata or GRIB tables. /// template long resolve_Channel_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 channel long channel = get_or_throw(mars, "channel"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`channel` resolved from input dictionaries: value='"; logMsg += std::to_string(channel); logMsg += "'"; return logMsg; }()); // Success exit point return channel; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `channel` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/paramId.h0000664000175000017500000001006615232567717024541 0ustar alastairalastair/* * (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 paramId.h /// @brief Deduction of the GRIB `paramId` identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **GRIB parameter identifier** (`paramId`) from input /// dictionaries. /// /// The deduction retrieves the parameter identifier explicitly from the /// MARS dictionary and returns it verbatim as a numeric value. /// No inference, defaulting, normalization, or GRIB table validation is /// performed at this stage. /// /// 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 paramEncoding.h /// /// Related deductions: /// - @ref level.h /// - @ref pvArray.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include // exception and logging #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 parameter identifier (`paramId`). /// /// @section Deduction contract /// - Reads: `mars["param"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the GRIB parameter identifier associated with /// the field being encoded. /// /// The value is treated as mandatory and is returned verbatim as a /// numeric identifier. No semantic interpretation or validation against /// GRIB parameter tables is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide the key `param`. /// /// @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 parameter identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved GRIB parameter identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `param` is missing, cannot be converted to `long`, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction assumes that the parameter identifier is explicitly /// provided by MARS and does not attempt any inference or defaulting. /// template long resolve_ParamId_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 param long paramId = get_or_throw(mars, "param"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`paramId` resolved from input dictionaries: value='"; logMsg += std::to_string(paramId) + "'"; return logMsg; }()); // Success exit point return paramId; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `paramId` from input dictionaries", Here())); } }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/waveFrequencyGrid.h0000664000175000017500000003617515232567717026627 0ustar alastairalastair/* * (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 waveFrequencyGrid.h /// @brief Deduction of the GRIB wave frequency grid. /// /// This header defines the deduction responsible for resolving the /// wave frequency discretization used in spectral wave products. /// /// The deduction produces a frequency grid together with its scaled /// integer representation, suitable for GRIB encoding. /// /// Deductions: /// - extract values from input dictionaries /// - reconstruct frequency grids deterministically when required /// - apply fixed scaling rules /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing reconstruction parameters /// - apply heuristic defaults beyond explicitly defined ones /// - validate physical correctness of frequency values /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: grid obtained directly or deterministically reconstructed /// /// @section References /// Concept: /// - @ref waveEncoding.h /// /// Related deductions: /// - @ref periodItMin.h /// - @ref periodItMax.h /// - @ref waveDirectionGrid.h /// - @ref waveDirectionNumber.h /// - @ref waveFrequencyNumber.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include #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 Metadata and scaled representation of a wave frequency discretization. /// /// This structure describes a wave frequency grid together with its integer, /// scaled representation. It is intended for use in contexts where frequencies /// must be stored or transmitted as integers (e.g. GRIB encoding), while /// retaining a well-defined mapping to physical frequency values in Hz. /// /// The scaling convention used is logarithmic: /// - `scaleFactorFrequencies` represents the base-10 logarithm of the real /// scaling factor applied to the physical frequencies. /// - A value of `scaleFactorFrequencies = 6` corresponds to a real scaling /// factor of \f$10^6\f$. /// /// Scaled integer values are obtained by: /// \f[ /// \text{scaledValue}_i = /// \mathrm{round}\!\left( f_i \times 10^{\text{scaleFactorFrequencies}} \right) /// \f] /// where \f$f_i\f$ is the physical frequency in Hz. /// /// The inverse operation (reconstruction of physical frequencies) is: /// \f[ /// f_i = /// \frac{\text{scaledValue}_i}{10^{\text{scaleFactorFrequencies}}} /// \f] /// /// @note /// This structure is a plain data container and does not enforce consistency /// between `numFrequencies` and the size of `scaledValuesFrequencies`. /// Such validation is expected to be performed by the caller. /// struct WaveFrequencyGrid { /// /// @brief Number of discrete wave frequencies. /// long numFrequencies; /// /// @brief Base-10 logarithm of the real frequency scaling factor. /// /// This value defines the scaling applied to physical frequencies prior to /// integer encoding. For example: /// - `scaleFactorFrequencies = 6` implies a real scaling factor of \f$10^6\f$. /// long scaleFactorFrequencies; /// /// @brief Scaled integer representation of wave frequencies. /// /// Each element is the rounded value of the corresponding physical frequency /// (in Hz) multiplied by \f$10^{\text{scaleFactorFrequencies}}\f$. /// std::vector scaledValuesFrequencies; }; namespace wave_frequency_detail { /// /// @brief Compute a geometrically spaced wave frequency grid. /// /// This function constructs a frequency grid using geometric spacing, /// centered around a reference wave frequency. The reference frequency /// is placed at a specified index, and lower and higher frequencies are /// obtained by successive division or multiplication by a constant ratio. /// /// The discretization follows: /// - fr[indexOfReferenceWaveFrequency] = referenceWaveFrequency /// - fr[i-1] = fr[i] / waveFrequencySpacingRatio /// - fr[i+1] = fr[i] * waveFrequencySpacingRatio /// /// This function is a direct C++ translation of the original Fortran routine /// `MFR` located in: /// - `ecwam/src/ecwam/mfr.F90` /// /// from the ECMWF ECWAM model repository: /// - https://github.com/ecmwf-ifs/ecwam.git /// /// The numerical behavior and discretization logic are preserved exactly. /// /// @param[in] numberOfWaveFrequencies /// Total number of wave frequencies to generate. /// /// @param[in] indexOfReferenceWaveFrequency /// Index (1-based) of the reference wave frequency. /// /// @param[in] referenceWaveFrequency /// Reference wave frequency value. /// /// @param[in] waveFrequencySpacingRatio /// Geometric ratio between two consecutive wave frequencies. /// /// @return /// Vector containing the discretized wave frequency grid. /// /// @throws std::out_of_range /// If `indexOfReferenceWaveFrequency` is outside the valid range. /// /// @note /// The reference index follows Fortran conventions (1-based) to preserve /// compatibility with legacy configurations. /// /// @note /// This implementation assumes geometric spacing of frequencies and does /// not perform validation of the physical consistency of the input values. /// inline std::vector compute_WaveFrequencyGrid(long numberOfWaveFrequencies, long indexOfReferenceWaveFrequency, double referenceWaveFrequency, double waveFrequencySpacingRatio) { if (indexOfReferenceWaveFrequency == 0 || indexOfReferenceWaveFrequency > numberOfWaveFrequencies) { throw std::out_of_range("indexOfReferenceWaveFrequency out of range"); } std::vector waveFrequencies(numberOfWaveFrequencies); // Convert reference index from 1-based (Fortran-style) to 0-based const long ref = indexOfReferenceWaveFrequency - 1; // Reference frequency waveFrequencies[ref] = referenceWaveFrequency; // Frequencies below the reference for (long i = ref; i-- > 0;) { waveFrequencies[i] = waveFrequencies[i + 1] / waveFrequencySpacingRatio; } // Frequencies above the reference for (long i = ref + 1; i < numberOfWaveFrequencies; ++i) { waveFrequencies[i] = waveFrequencySpacingRatio * waveFrequencies[i - 1]; } return waveFrequencies; } /// /// @brief Construct a scaled wave frequency grid from physical frequency values. /// /// This function builds a `WaveFrequencyGrid` structure from a vector of /// physical wave frequencies expressed in Hertz. The physical values are /// converted to an integer representation using a base-10 logarithmic /// scaling factor. /// /// The scaling convention is defined as follows: /// - `scaleFactorOfWaveFrequencies` represents the base-10 logarithm of the /// real scaling factor applied to the physical frequencies. /// - A value of `scaleFactorOfWaveFrequencies = 6` corresponds to a real /// scaling factor of \f$10^6\f$. /// /// Each scaled integer value is computed as: /// \f[ /// \text{scaledValue}_i = /// \mathrm{round}\!\left( f_i \times 10^{\text{scaleFactorOfWaveFrequencies}} \right) /// \f] /// where \f$f_i\f$ is the physical frequency in Hz. /// /// The resulting structure stores: /// - the total number of frequencies, /// - the scaling factor (logarithmic form), /// - the vector of scaled integer frequency values. /// /// This representation is typically used for serialization or encoding /// purposes (e.g. GRIB), where integer storage with a known scaling factor /// is required. /// /// @param[in] waveFrequenciesInHz /// Vector of physical wave frequencies expressed in Hertz. /// /// @param[in] scaleFactorOfWaveFrequencies /// Base-10 logarithm of the scaling factor applied to the frequencies /// prior to integer encoding. /// /// @return /// A `WaveFrequencyGrid` structure containing the scaled integer /// representation of the input frequency grid. /// /// @note /// No validation is performed on the input frequencies (e.g. positivity, /// monotonicity) or on the scaling factor. The caller is responsible for /// ensuring that the provided values are physically meaningful and that /// the scaled values fit within the range of the target integer type. /// inline WaveFrequencyGrid compute_WaveScaledFrequencyGrid(const std::vector& waveFrequenciesInHz, long scaleFactorOfWaveFrequencies) { WaveFrequencyGrid out{}; out.numFrequencies = static_cast(waveFrequenciesInHz.size()); out.scaleFactorFrequencies = scaleFactorOfWaveFrequencies; out.scaledValuesFrequencies.resize(waveFrequenciesInHz.size()); for (long i = 0; i < static_cast(waveFrequenciesInHz.size()); ++i) { out.scaledValuesFrequencies[i] = static_cast(std::round(waveFrequenciesInHz[i] * std::pow(10.0, scaleFactorOfWaveFrequencies))); } return out; } } // namespace wave_frequency_detail /// /// @brief Resolve the GRIB wave frequency grid. /// /// This deduction resolves the wave frequency discretization required /// for spectral wave encoding and produces a scaled integer /// representation suitable for GRIB. /// /// Resolution follows a strict precedence: /// /// 1. **Explicit grid** /// If `par::waveFrequencies` is present, it is interpreted as the /// full frequency grid expressed in Hz. /// /// 2. **Deterministic reconstruction** /// If the explicit grid is not present, the grid is reconstructed /// provided that all of the following keys exist: /// - `numberOfWaveFrequencies` /// - `indexOfReferenceWaveFrequency` /// - `referenceWaveFrequency` /// - `waveFrequencySpacingRatio` /// /// The resulting grid is scaled using: /// - `scaleFactorOfWaveFrequencies` (optional, defaults to `6`) /// /// @tparam OptDict_t Type of the options dictionary (unused) /// @tparam MarsDict_t Type of the MARS dictionary (unused) /// @tparam ParDict_t Type of the parameter dictionary /// /// @param[in] opt Options dictionary (unused) /// @param[in] mars MARS dictionary (unused) /// @param[in] par Parameter dictionary providing frequency information /// /// @return A fully populated `WaveFrequencyGrid` /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - neither explicit frequencies nor reconstruction parameters exist /// - a required key is missing or has an invalid type /// - reconstruction or scaling fails /// template WaveFrequencyGrid resolve_WaveFrequencyGrid_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { WaveFrequencyGrid out{}; std::vector waveFrequenciesInHz; // Retrieve optional scaling factor from parameter dictionary long scaleFactorOfWaveFrequencies = get_opt(par, "scaleFactorOfWaveFrequencies").value_or(6L); // Check for explicit frequency grid bool hasWaveFrequencies = has(par, "waveFrequencies"); // Check for reconstruction parameters bool hasNumberOfWaveFrequencies = has(par, "numberOfWaveFrequencies"); bool hasIndexOfReferenceWaveFrequency = has(par, "indexOfReferenceWaveFrequency"); bool hasReferenceWaveFrequency = has(par, "referenceWaveFrequency"); bool hasWaveFrequencySpacingRatio = has(par, "waveFrequencySpacingRatio"); bool canReconstructWaveFrequencies = hasNumberOfWaveFrequencies && hasIndexOfReferenceWaveFrequency && hasReferenceWaveFrequency && hasWaveFrequencySpacingRatio; if (hasWaveFrequencies) { // Retrieve mandatory wave frequencies from parameter dictionary waveFrequenciesInHz = get_or_throw>(par, "waveFrequencies"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`waveFrequencyGrid` resolved from input dictionaries"; return logMsg; }()); } else if (canReconstructWaveFrequencies && !hasWaveFrequencies) { // Retrieve mandatory reconstruction parameters from parameter dictionary long numberOfWaveFrequencies = get_or_throw(par, "numberOfWaveFrequencies"); long indexOfReferenceWaveFrequency = get_or_throw(par, "indexOfReferenceWaveFrequency"); double referenceWaveFrequency = get_or_throw(par, "referenceWaveFrequency"); double waveFrequencySpacingRatio = get_or_throw(par, "waveFrequencySpacingRatio"); // Reconstruct frequency grid deterministically waveFrequenciesInHz = wave_frequency_detail::compute_WaveFrequencyGrid(numberOfWaveFrequencies, indexOfReferenceWaveFrequency, referenceWaveFrequency, waveFrequencySpacingRatio); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`waveFrequencyGrid` reconstructed from input dictionaries with params={"; logMsg += std::to_string(numberOfWaveFrequencies) + ", "; logMsg += std::to_string(indexOfReferenceWaveFrequency) + ", "; logMsg += std::to_string(referenceWaveFrequency) + ", "; logMsg += std::to_string(waveFrequencySpacingRatio) + "}"; return logMsg; }()); } else { /// @todo Need a defaulting strategy for: [`numberOfWaveFrequencies`, `indexOfReferenceWaveFrequency`, /// `referenceWaveFrequency`, `waveFrequencySpacingRatio`] throw Mars2GribDeductionException( "Default value NOT IMPLEMENTED for: [`numberOfWaveFrequencies`, `indexOfReferenceWaveFrequency`, " "`referenceWaveFrequency`, `waveFrequencySpacingRatio`]", Here()); } // Build the scaled frequency grid out = wave_frequency_detail::compute_WaveScaledFrequencyGrid(waveFrequenciesInHz, scaleFactorOfWaveFrequencies); // Success exit point return out; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `waveFrequencyGrid` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductionsmetkit-1.19.2/src/metkit/mars2grib/backend/deductions/centre.h0000664000175000017500000001074715232567717024452 0ustar alastairalastair/* * (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 centre.h /// @brief Deduction of the GRIB `centre` identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **GRIB centre identifier** from MARS metadata. /// /// The deduction retrieves the originating centre identifier directly /// from the MARS dictionary and exposes it to the encoding layer without /// transformation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply normalization or defaulting unless explicitly defined /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref originEncoding.h /// /// Related deductions: /// - @ref subCentre.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 GRIB `centre` identifier from MARS metadata. /// /// @section Deduction contract /// - Reads: `mars["origin"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the GRIB `centre` identifier by retrieving the /// optional MARS key `origin` and returning its value verbatim. /// /// No normalization, translation, or defaulting is applied at this stage. /// Any semantic interpretation or mapping to numeric GRIB centre codes /// must be handled by downstream encoding logic. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `origin` /// and conversion to `std::string`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the originating centre identifier is read. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The originating centre identifier as provided by MARS. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If any unexpected error occurs during deduction. /// /// @note /// This deduction enforces presence-only validation and does not /// consult GRIB centre code tables. /// template std::string resolve_Centre_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Retrieve (optional) MARS origin std::string origin = get_opt(mars, "origin").value_or("ecmf"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`centre` resolved from input dictionaries: value='"; logMsg += origin + "'"; return logMsg; }()); // Success exit point return origin; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `origin` from input dictionaries", Here())); } }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/shapeOfTheEarth.h0000664000175000017500000001240115232567717026171 0ustar alastairalastair/* * (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 shapeOfTheEarth.h /// @brief Deduction of the GRIB shape of the Earth. /// /// This header defines the deduction responsible for resolving the /// GRIB `shapeOfTheEarth` key used to describe the reference system /// of the Earth in GRIB messages. /// /// The deduction currently applies a fixed, deterministic value /// corresponding to a spherical Earth with radius 6371229 m. /// /// Deductions: /// - extract values from input dictionaries (currently unused) /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer values from MARS metadata /// - apply configuration-based defaults /// - validate against GRIB code tables /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - DEFAULT: value defaulted by the deduction /// /// @section References /// Concept: /// - @ref shapeOfTheEarthEncoding.h /// /// Related deductions: /// (none) /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include // Tables #include "metkit/mars2grib/backend/tables/shapeOfTheReferenceSystem.h" #include "metkit/mars2grib/utils/generalUtils.h" // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the GRIB `shapeOfTheEarth` key. /// /// @section Deduction contract /// - Reads: none /// - Writes: none /// - Side effects: logging (DEFAULT) /// - Failure mode: none (deterministic) /// /// This deduction resolves the GRIB `shapeOfTheEarth` key by /// applying a fixed, deterministic value corresponding to /// a spherical Earth with radius 6371229 m when the value is not specified in the parameter dictionary. /// /// No inference from MARS, or options dictionaries is currently performed. /// /// @tparam MarsDict_t Type of the MARS dictionary (unused) /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary (unused) /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return /// The resolved `ShapeOfTheReferenceSystem` enumeration value. /// /// @note /// This function is the **single authoritative deduction** /// for `shapeOfTheEarth`. /// /// @todo [owner: mds,dgov][scope: deduction][reason: extensibility][prio: low] /// - Introduce inference or configuration-based selection when /// non-spherical Earth representations are required. /// template tables::ShapeOfTheReferenceSystem resolve_ShapeOfTheEarth_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { if (has(par, "shapeOfTheEarth")) { // Retrieve the value from the parameter dictionary long shapeOfTheEarthId = get_or_throw(par, "shapeOfTheEarth"); // Convert to enumeration tables::ShapeOfTheReferenceSystem shapeOfTheEarth = tables::long2enum_ShapeOfTheReferenceSystem_or_throw(shapeOfTheEarthId); // Logging of the channel MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`shapeOfTheEarth` resolved from PAR dictionary: value='"; logMsg += tables::enum2name_ShapeOfTheReferenceSystem_or_throw(shapeOfTheEarth); logMsg += "'"; return logMsg; }()); // Success exit point return shapeOfTheEarth; } else { // Default value tables::ShapeOfTheReferenceSystem shapeOfTheEarth = tables::ShapeOfTheReferenceSystem::EarthSphericalRadius6371229; // Logging of the channel MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`shapeOfTheEarth` defaulted with value='"; logMsg += tables::enum2name_ShapeOfTheReferenceSystem_or_throw(shapeOfTheEarth); logMsg += "'"; return logMsg; }()); // Success exit point return shapeOfTheEarth; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `shapeOfTheEarth` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/allowedReferenceValue.h0000664000175000017500000002161115232567717027425 0ustar alastairalastair/* * (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 allowedReferenceValue.h /// @brief Deduction of the GRIB `allowedReferenceValue`. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **GRIB allowed reference value** associated with a given /// MARS parameter identifier. /// /// The deduction retrieves the MARS parameter code from the input dictionaries /// and derives a representative reference value based on a statically defined /// set of admissible value ranges. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, normalization, or defaulting beyond explicit rules /// - 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 via deduction logic from input dictionaries /// /// @section References /// Concept: /// - @ref marsEncoding.h /// /// Related deductions: /// - @ref class.h /// - @ref expver.h /// - @ref stream.h /// - @ref type.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #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 GRIB allowed reference value from input dictionaries. /// /// @section Deduction contract /// - Reads: exactly one of `mars["grid"]` or `mars["truncation"]`; `mars["param"]` for grid-point products /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the GRIB `allowedReferenceValue` by first determining /// whether the request describes a grid-point or spectral representation. Exactly /// one of `grid` or `truncation` must be present in the MARS dictionary. /// /// For grid-point products, the mandatory MARS parameter identifier (`param`) is /// retrieved and used to consult a statically defined table of admissible /// reference value ranges. For parameters with an explicit range definition, the /// resolved reference value is chosen as the midpoint of the corresponding /// `[min, max]` interval. If no explicit range is defined for the parameter, a /// default reference value of `0.0` is returned. /// /// For spectral products identified by `truncation`, the resolved reference /// value is `0.0`. /// /// No semantic interpretation beyond the explicit range table is applied. /// The admissible ranges are defined locally and are not validated against /// external GRIB tables. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support presence checks for `grid` and /// `truncation`; grid-point products must also support keyed access to `param` /// and conversion to an integral type. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the parameter identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved allowed reference value. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If neither or both of `grid` and `truncation` are present, if `param` is /// missing or malformed for a grid-point product, or if any unexpected error /// occurs during deduction. /// /// @note /// This deduction applies a local, table-driven rule and does not /// consult GRIB metadata tables or apply parameter-specific semantics. /// template double resolve_AllowedReferenceValue_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Static map of allowed reference value ranges keyed by parameter code static const std::unordered_map> param_ranges = { {3, {170.0, 1200.0}}, {10, {0.0, 300.0}}, {31, {-0.00001, 1.001}}, {33, {10.0, 1000.0}}, {34, {160.0, 320.0}}, {43, {0.0, 10.0}}, {49, {0.0, 100.0}}, {54, {100.0, 108000.0}}, {59, {0.0, 40000.0}}, {60, {-1.0, 1.0}}, {121, {160.0, 380.0}}, {122, {150.0, 330.0}}, {129, {-13000.0, 3500000.0}}, {130, {140.0, 400.0}}, {131, {-250.0, 250.0}}, {132, {-250.0, 250.0}}, {133, {-0.1, 0.1}}, {134, {43000.0, 115000.0}}, {135, {-30.0, 30.0}}, {136, {-50.0, 220.0}}, {151, {85000.0, 125000.0}}, {156, {-1300.0, 35000.0}}, {157, {0.0, 180.0}}, {164, {0.0, 1.0}}, {165, {-150.0, 150.0}}, {166, {-100.0, 100.0}}, {167, {160.0, 370.0}}, {168, {25.0, 350.0}}, {172, {0.0, 1.0}}, {173, {0.0, 10.0}}, {186, {0.0, 1.0}}, {187, {0.0, 1.0}}, {188, {0.0, 1.0}}, {207, {0.0, 300.0}}, {235, {120.0, 380.0}}, {246, {-0.001, 1e6}}, {247, {-0.001, 0.01}}, {3031, {0.0, 360.1}}, {3062, {-0.05, 130.0}}, {3066, {0.0, 5.0}}, {3073, {0.0, 100.0}}, {3074, {0.0, 100.0}}, {3075, {0.0, 100.0}}, {140230, {-1.0, 360.5}}, {151131, {-3.5, 3.5}}, {151132, {-3.5, 3.5}}, {151145, {-4.0, 4.0}}, {228001, {-60000.0, 1000.0}}, {228002, {-1300.0, 8888.0}}, {228004, {160.0, 370.0}}, {228005, {0.0, 300.0}}, {228006, {0.0, 1.0}}, {228141, {-1e-10, 15000.0}}, {260057, {-3.0, 150.0}}, {260259, {-10.0, 5.0}}, {260260, {0.0, 360.1}}, {262101, {160.0, 320.0}}, {262140, {-3.5, 3.5}}, {262501, {173.0, 1000.0}}, {263101, {160.0, 320.0}}, {263140, {-3.5, 3.5}}, {263501, {173.0, 1000.0}}, }; const bool hasGrid = has(mars, "grid"); const bool hasTruncation = has(mars, "truncation"); if (hasGrid == hasTruncation) { throw Mars2GribDeductionException( "`allowedReferenceValue` requires exactly one of MARS keys `grid` or `truncation`", Here()); } if (hasTruncation) { MARS2GRIB_LOG_RESOLVE([&]() { return std::string{"`allowedReferenceValue` resolved for spectral representation: value='0.000000'"}; }()); return 0.0; } // Default reference value for grid-point products double ret = 0.0; // Retrieve mandatory MARS parameter identifier long marsParamVal = get_or_throw(mars, "param"); // Lookup allowed value in the mid of the allowed range if (auto rangeIt = param_ranges.find(marsParamVal); rangeIt != param_ranges.end()) { const auto& [minVal, maxVal] = rangeIt->second; ret = 0.5 * (minVal + maxVal); } // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`allowedReferenceValue` resolved from input dictionaries: value='" + std::to_string(ret) + "'"; return logMsg; }()); // Success exit point return ret; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `allowedReferenceValue` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/typeOfProcessedData.h0000664000175000017500000001650615232567717027101 0ustar alastairalastair/* * (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 typeOfProcessedData.h /// @brief Deduction of the GRIB `typeOfProcessedData` identifier. /// /// This header defines the deduction responsible for resolving the /// GRIB `typeOfProcessedData` key (GRIB2 Code Table 4.10). /// /// The value may be explicitly provided via parametrization or, /// if absent, deterministically deduced from MARS metadata. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values beyond documented rules /// - guess semantics not defined by policy /// - apply implicit defaults without explicit mapping /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - OVERRIDE: explicit value provided via parameter dictionary /// - RESOLVE: value deduced from MARS metadata /// /// @section References /// Concept: /// - @ref dataTypeEncoding.h /// /// Related deductions: /// - @ref productionStatusOfProcessedData.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include // Tables includes #include "metkit/mars2grib/backend/tables/typeOfProcessedData.h" #include "metkit/mars2grib/utils/generalUtils.h" // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the GRIB `typeOfProcessedData` key. /// /// This deduction determines the value of the GRIB /// `typeOfProcessedData` key using the following precedence: /// /// 1. **User override (parameter dictionary)** /// If the key `typeOfProcessedData` is present in the parameter /// dictionary, its value is taken as authoritative. The value may /// be provided either as: /// - a numeric GRIB code (`long`) /// - a symbolic GRIB name (`string`) /// /// 2. **Automatic deduction (MARS dictionary)** /// If no override is provided, the value is deduced from /// `mars::type` using a fixed, explicitly defined mapping. /// /// Unsupported or unmapped MARS types result in the value /// `TypeOfProcessedData::Missing`. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary providing `class`, `type`, and `stream` /// @param[in] par Parameter dictionary providing optional override /// @param[in] opt Options dictionary (unused) /// /// @return The resolved `TypeOfProcessedData` enumeration value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - override value is present but invalid /// - dictionary access fails /// - any unexpected error occurs during deduction /// template tables::TypeOfProcessedData resolve_TypeOfProcessedData_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Retrieve mandatory class from MARS dictionary std::string marsClass = get_or_throw(mars, "class"); // Retrieve mandatory type from MARS dictionary std::string marsType = get_or_throw(mars, "type"); // Retrieve mandatory stream from MARS dictionary std::string marsStream = get_or_throw(mars, "stream"); // Return value tables::TypeOfProcessedData result = tables::TypeOfProcessedData::Missing; if (has(par, "typeOfProcessedData")) { // Retrieve mandatory override from parameter dictionary if (has(par, "typeOfProcessedData")) { long typeOfProcessedDataVal = get_or_throw(par, "typeOfProcessedData"); // Convert long to enum (validate) result = tables::long2enum_TypeOfProcessedData_or_throw(typeOfProcessedDataVal); } else if (has(par, "typeOfProcessedData")) { std::string typeOfProcessedDataVal = get_or_throw(par, "typeOfProcessedData"); // Convert string to enum (validate) result = tables::name2enum_TypeOfProcessedData_or_throw(typeOfProcessedDataVal); } else { throw Mars2GribDeductionException( "Key `typeOfProcessedData` is not of expected type `long` or `string`", Here()); } // Emit OVERRIDE log entry MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`typeOfProcessedData` overridden from parameter dictionary: value='"; logMsg += enum2name_TypeOfProcessedData_or_throw(result); logMsg += "'"; return logMsg; }()); } else { // Deduce typeOfProcessedData from mars class and type if (marsClass == "ai") { // Mars class only for AI output result = tables::TypeOfProcessedData::MlBasedForecast; } else { // Mars type for everything else if (marsType == "an") { result = tables::TypeOfProcessedData::AnalysisProducts; } else if (marsType == "fc" || marsType == "ssd") { result = tables::TypeOfProcessedData::ForecastProducts; } else if (marsType == "pf") { result = tables::TypeOfProcessedData::PerturbedForecastProducts; } else if (marsType == "cf") { result = tables::TypeOfProcessedData::ControlForecastProducts; } else if (marsType == "gsd") { result = tables::TypeOfProcessedData::ProcessedSatelliteObservations; } else { result = tables::TypeOfProcessedData::Missing; } } // Emit OVERRIDE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`typeOfProcessedData` resolved from input dictionaries: value='"; logMsg += enum2name_TypeOfProcessedData_or_throw(result); logMsg += "'"; return logMsg; }()); } // Success exit point return result; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `typeOfProcessedData` from input dictionaries", Here())); } }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/totalNumberOfIterations.h0000664000175000017500000001427715232567717030017 0ustar alastairalastair/* * (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 metkit-1.19.2/src/metkit/mars2grib/backend/deductions/derivedForecast.h0000664000175000017500000001502315232567717026273 0ustar alastairalastair/* * (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 derivedForecast.h /// @brief Deduction of the GRIB `derivedForecast` key. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **GRIB derived forecast type** (GRIB2 Code Table 4.7). /// /// The deduction supports both explicit user override and automatic /// deduction from MARS metadata, following a strict precedence order. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying explicit validation and mapping logic /// - returning strongly typed GRIB table values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply implicit defaults outside explicitly defined rules /// - do NOT perform GRIB table validation beyond table lookups /// /// Error handling follows a strict fail-fast strategy: /// - missing or unsupported 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref derivedEncoding.h /// /// Related deductions: /// - @ref numberOfForecastsInEnsemble.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include // Tables #include "metkit/mars2grib/backend/tables/derivedForecast.h" #include "metkit/mars2grib/utils/generalUtils.h" // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the GRIB `derivedForecast` key. /// /// @section Deduction contract /// - Reads: /// - `par["derivedForecast"]` (if present), /// - otherwise `mars["type"]` /// - Writes: none /// - Side effects: logging (RESOLVE or OVERRIDE) /// - Failure mode: throws /// /// This deduction resolves the GRIB `derivedForecast` value following a /// strict precedence order. /// /// If the parameter dictionary provides `derivedForecast`, the value is /// treated as authoritative and validated via the GRIB code table. /// Otherwise, the value is intended to be deduced from the MARS key /// `type`. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide `type` if automatic /// deduction is required. /// /// @tparam ParDict_t /// Type of the parameter dictionary. May contain `derivedForecast` /// as a numeric override. /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary providing metadata for automatic deduction. /// /// @param[in] par /// Parameter dictionary optionally providing `derivedForecast`. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved `tables::DerivedForecast` enumeration value. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If an override value is invalid, if required MARS metadata is /// missing, if automatic deduction is not supported, or if any /// unexpected error occurs. /// template tables::DerivedForecast resolve_DerivedForecast_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Default to Missing tables::DerivedForecast derivedForecast = tables::DerivedForecast::Missing; if (has(par, "derivedForecast")) { // Retrieve override from parameter dictionary const auto derivedForecastVal = get_or_throw(par, "derivedForecast"); // Lookup and validate enum value derivedForecast = tables::long2enum_DerivedForecast_or_throw(derivedForecastVal); // Emit OVERRIDE log entry MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`derivedForecast` overridden from parameter dictionary: value='"; logMsg += tables::enum2name_DerivedForecast_or_throw(derivedForecast); logMsg += "'"; return logMsg; }()); // Success exit point return derivedForecast; } else { const auto marsType = get_or_throw(mars, "type"); if (marsType == "em" || marsType == "taem") { derivedForecast = tables::DerivedForecast::UnweightedMeanAllMembers; } else if (marsType == "es" || marsType == "ses" || marsType == "taes") { derivedForecast = tables::DerivedForecast::SpreadAllMembers; } else if (marsType == "efi" || marsType == "efic" || marsType == "sot") { derivedForecast = tables::DerivedForecast::LargeAnomalyIndexAllMembers; } else { throw Mars2GribDeductionException( "Mapping from mars type " + marsType + " to derivedForecast is not implemented", Here()); } // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`derivedForecast` resolved from input dictionaries: value='"; logMsg += tables::enum2name_DerivedForecast_or_throw(derivedForecast); logMsg += "'"; return logMsg; }()); // Success exit point return derivedForecast; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `derivedForecast` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/level.h0000664000175000017500000001155215232567717024274 0ustar alastairalastair/* * (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 level.h /// @brief Deduction of the vertical level identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **vertical level identifier** from MARS metadata. /// /// The deduction retrieves the level identifier directly from the /// MARS dictionary and exposes it to the encoding layer without /// transformation or interpretation. /// /// The semantic interpretation of the level value (e.g. pressure level, /// model level index) depends on the associated level type (`levtype`) /// and is handled by downstream encoding logic. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, normalization, or defaulting /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref levelEncoding.h /// /// Related deductions: /// - @ref levtype.h /// - @ref pvArray.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 vertical level identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["levelist"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the vertical level identifier by retrieving /// the mandatory MARS key `levelist` and returning its value as a /// `long`. /// /// No semantic interpretation or validation of the level value is /// performed. The meaning of the level identifier depends on the /// associated level type (`levtype`) and is handled elsewhere. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to /// `levelist` and conversion to `long`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the level identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved vertical level identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `levelist` is missing, cannot be retrieved as a /// `long`, or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult level definition tables or apply unit conversions. /// template long resolve_Level_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 level identifier long marsLevelistVal = get_or_throw(mars, "levelist"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`level` resolved from input dictionaries: value='"; logMsg += std::to_string(marsLevelistVal) + "'"; return logMsg; }()); // Success exit point return marsLevelistVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `levelist` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/scaledValueOfCentralWaveNumber.h0000664000175000017500000001062715232567717031211 0ustar alastairalastair/* * (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 scaledValueOfCentralWaveNumber.h /// @brief Deduction of the GRIB `scaledValueOfCentralWaveNumber` key. /// /// This file defines the deduction responsible for resolving the GRIB /// `scaledValueOfCentralWaveNumber` key used in satellite-based products. /// /// The value is provided explicitly via the parameter dictionary and is /// combined at encoding time with `scaleFactorOfCentralWaveNumber` to /// represent the central wave number. /// /// This deduction: /// - reads exclusively from the parameter dictionary /// - applies no inference, defaulting, or validation /// - emits structured diagnostic logging /// /// Error handling follows a fail-fast strategy with nested exception /// propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries /// /// @section References /// Concept: /// - @ref satelliteEncoding.h /// /// Related deductions: /// - @ref scaleFactorOfCentralWaveNumber.h /// - @ref channel.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 GRIB `scaledValueOfCentralWaveNumber` identifier. /// /// @section Deduction contract /// - Reads: `par["scaledValueOfCentralWaveNumber"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the scaled value used to encode the central /// wave number in satellite products. /// /// The value is retrieved verbatim from the parameter dictionary. /// No inference from MARS metadata and no consistency validation with /// `scaleFactorOfCentralWaveNumber` is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused). /// /// @tparam ParDict_t /// Type of the parameter dictionary. Must provide /// `scaledValueOfCentralWaveNumber`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary containing the scaled value. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// Scaled value of the central wave number. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `scaledValueOfCentralWaveNumber` is missing, cannot be /// retrieved as a `long`, or if any unexpected error occurs. /// /// @note /// This deduction is deterministic and independent of GRIB header state. /// template long resolve_ScaledValueOfCentralWaveNumber_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 scaled value of central wave number from parameter dictionary auto scaledValueOfCentralWaveNumberVal = get_or_throw(par, "scaledValueOfCentralWaveNumber"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`scaledValueOfCentralWaveNumber` resolved from parameter dictionary: value='"; logMsg += std::to_string(scaledValueOfCentralWaveNumberVal); logMsg += "'"; return logMsg; }()); // Success exit point return scaledValueOfCentralWaveNumberVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribDeductionException( "Failed to resolve `scaledValueOfCentralWaveNumber` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/generation.h0000664000175000017500000001136715232567717025324 0ustar alastairalastair/* * (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 generation.h /// @brief Deduction of the MARS `generation` identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **generation identifier** from MARS metadata. /// /// The deduction retrieves the generation identifier directly from the /// MARS dictionary and exposes it to the encoding layer without /// transformation or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, normalization, or defaulting /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref destineEncoding.h /// /// Related deductions: /// - @ref activity.h /// - @ref dataset.h /// - @ref experiment.h /// - @ref model.h /// - @ref realization.h /// - @ref resolution.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 generation identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["generation"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the generation identifier by retrieving the /// mandatory MARS key `generation` and returning its value as a `long`. /// /// No semantic interpretation, normalization, or validation is applied. /// The meaning and allowed values of the generation identifier are /// defined by upstream MARS conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to /// `generation` and conversion to `long`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the generation identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved generation identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `generation` is missing, cannot be retrieved as a /// `long`, or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult generation registries or GRIB tables. /// template long resolve_Generation_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 generation long marsGenerationVal = get_or_throw(mars, "generation"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`generation` resolved from input dictionaries: value='" + std::to_string(marsGenerationVal) + "'"; return logMsg; }()); // Success exit point return marsGenerationVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `generation` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/timeIncrementInSeconds.h0000664000175000017500000001345315232567717027600 0ustar alastairalastair/* * (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 timeIncrementInSeconds.h /// @brief Deduction of the optional `timeIncrementInSeconds` parameter. /// /// Resolves the `timeIncrementInSeconds` key from the parameter dictionary. /// The value, when present and strictly positive, denotes the inner time /// increment (in seconds) of a statistical product (the spacing between /// the samples consumed by the innermost statistical operation, e.g. the /// 1-hour spacing of an hourly accumulation). /// /// Normalization rules: /// - absent key -> `std::nullopt` /// - present, value `0` -> `std::nullopt` (legacy normalization) /// - present, value `> 0` -> `value` /// - present, value `< 0` -> hard error /// /// Deductions: /// - extract value from the parameter dictionary /// - apply normalization rule above /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - validate against external GRIB code tables /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// @section References /// Consumer: /// - @ref productTime.h (consumes `timeIncrementInSeconds_opt` to populate /// the `ProductTime::timeIncrementInSeconds` field) /// /// Specification: /// - `deductions/timeProducts.md` §7.8 (`timeIncrementInSeconds` keyword) /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include #include #include #include // Core deduction includes #include "eckit/exception/Exceptions.h" #include "eckit/log/Log.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve `timeIncrementInSeconds` as an optional value. /// /// Reads the `timeIncrementInSeconds` key from the parameter dictionary and /// applies the normalization rules described in the file-level /// documentation. /// /// @tparam MarsDict_t MARS dictionary type (unused; kept for symmetry with /// sibling deductions). /// @tparam ParDict_t Parameter dictionary type. /// @param mars MARS dictionary (unused). /// @param par Parameter dictionary; queried for /// `timeIncrementInSeconds`. /// @return `std::optional` holding the strictly-positive seconds value /// when present, `std::nullopt` otherwise (key absent or value `0`). /// @throws Mars2GribDeductionException if the key is present and the value /// is strictly negative, or if any underlying dictionary access /// throws (rethrown nested with diagnostic context). /// template std::optional timeIncrementInSeconds_opt(const MarsDict_t& mars, const ParDict_t& par) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { auto lengthOfTimeStepInSeconds_opt = get_opt(par, "timeIncrementInSeconds"); if (lengthOfTimeStepInSeconds_opt.has_value()) { if (lengthOfTimeStepInSeconds_opt.value() < 0) { throw Mars2GribDeductionException("`timeIncrementInSeconds` must be > 0 if present", Here()); } else if (lengthOfTimeStepInSeconds_opt.value() == 0) { lengthOfTimeStepInSeconds_opt = std::nullopt; } } return lengthOfTimeStepInSeconds_opt; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Unable to get `timeIncrementInSeconds` from Mars dictionary", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; /// /// @brief Resolve `timeIncrementInSeconds` or throw if absent. /// /// Thin wrapper around `timeIncrementInSeconds_opt` that converts a /// `std::nullopt` result into a hard error. Use when the consumer requires /// a definite value. /// /// @tparam MarsDict_t MARS dictionary type. /// @tparam ParDict_t Parameter dictionary type. /// @param mars MARS dictionary (forwarded). /// @param par Parameter dictionary (forwarded). /// @return The strictly-positive seconds value. /// @throws Mars2GribDeductionException if the value is absent (key missing /// or normalized to `std::nullopt`), strictly negative, or if any /// underlying dictionary access throws. /// template long timeIncrementInSeconds_or_throw(const MarsDict_t& mars, const ParDict_t& par) { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { std::optional timeIncrementInSecondsOpt = timeIncrementInSeconds_opt(mars, par); if (timeIncrementInSecondsOpt.has_value()) { return timeIncrementInSecondsOpt.value(); } else { throw Mars2GribDeductionException("`timeIncrementInSeconds` is not defined in Mars/Par dictionary", Here()); } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Unable to get `timeIncrementInSeconds` from Mars dictionary", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/realization.h0000664000175000017500000001162415232567717025506 0ustar alastairalastair/* * (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 realization.h /// @brief Deduction of the GRIB realization identifier. /// /// This file defines the deduction responsible for resolving the /// **realization identifier**, which distinguishes individual realizations /// (members) within an ensemble, stochastic system, or DestinE workflow. /// /// The realization identifier is retrieved explicitly from the MARS /// dictionary and passed verbatim to the encoder. No inference, defaulting, /// or normalization is performed. /// /// Deductions in this file are responsible for: /// - extracting realization metadata from the MARS dictionary /// - applying deterministic, fail-fast resolution rules /// - returning a strongly typed value to the encoding layer /// /// Deductions: /// - do NOT infer realization semantics /// - do NOT validate ensemble consistency /// - do NOT depend on pre-existing GRIB header state /// /// Error handling follows a strict fail-fast policy: /// - missing or invalid inputs cause immediate failure /// - errors are reported using Mars2Grib deduction exceptions /// - original errors are preserved via nested exception propagation /// /// Logging follows the mars2grib deduction policy: /// - RESOLVE: value retrieved directly from the MARS dictionary /// /// @section References /// Concept: /// - @ref destineEncoding.h /// /// Related deductions: /// - @ref activity.h /// - @ref experiment.h /// - @ref generation.h /// - @ref model.h /// - @ref resolution.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 realization identifier from the MARS dictionary. /// /// @section Deduction contract /// - Reads: `mars["realization"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the mandatory `realization` key from the /// MARS dictionary and returns it as a numeric identifier. /// /// The realization identifier is used to distinguish individual /// realizations within ensemble or DestinE products. Its numerical /// semantics are defined by upstream MARS and encoding conventions /// and are not interpreted here. /// /// No inference, defaulting, or validation of the realization value /// is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide the key `realization`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary providing the realization identifier. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The realization identifier as provided by the MARS dictionary. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - the key `realization` is missing from the MARS dictionary /// - the value cannot be converted to `long` /// - any unexpected error occurs during deduction /// /// @note /// - This deduction is fully deterministic. /// - The returned value is passed verbatim to downstream encoding logic. /// template long resolve_Realization_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 realization long marsRealizationVal = get_or_throw(mars, "realization"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`realization` resolved from MARS dictionary: value='"; logMsg += std::to_string(marsRealizationVal); logMsg += "'"; return logMsg; }()); // Success exit point return marsRealizationVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `realization` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/experiment.h0000664000175000017500000001137615232567717025351 0ustar alastairalastair/* * (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 experiment.h /// @brief Deduction of the MARS `experiment` identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **experiment identifier** from MARS metadata. /// /// The deduction retrieves the experiment identifier directly from the /// MARS dictionary and exposes it to the encoding layer without /// transformation or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, normalization, or defaulting /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref destineEncoding.h /// /// Related deductions: /// - @ref activity.h /// - @ref dataset.h /// - @ref generation.h /// - @ref model.h /// - @ref realization.h /// - @ref resolution.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 experiment identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["experiment"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the experiment identifier by retrieving the /// mandatory MARS key `experiment` and returning its value as a /// `std::string`. /// /// No semantic interpretation, normalization, or validation is applied. /// The meaning and allowed values of the experiment identifier are /// defined by upstream MARS conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to /// `experiment` and conversion to `std::string`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the experiment identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved experiment identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `experiment` is missing, cannot be retrieved as a /// string, or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult experiment registries or GRIB tables. /// template std::string resolve_Experiment_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 experiment std::string marsExperimentVal = get_or_throw(mars, "experiment"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`experiment` resolved from input dictionaries: value='" + marsExperimentVal + "'"; return logMsg; }()); // Success exit point return marsExperimentVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `experiment` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/productionStatusOfProcessedData.h0000664000175000017500000001541515232567717031510 0ustar alastairalastair/* * (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 productionStatusOfProcessedData.h /// @brief Deduction of the GRIB `productionStatusOfProcessedData` key (Code Table 1.3). /// /// This header defines the deduction used by the mars2grib backend to resolve /// `productionStatusOfProcessedData`, which characterises the production status /// of the processed data product. /// /// The deduction currently implements a minimal mapping based on MARS metadata: /// - `mars["class"] == "d1"` → `DestinationEarth` /// - `mars["class"] == "e6"` → `ReanalysisProducts` /// - otherwise → `OperationalProducts` (default) /// /// The resolved value is returned as a strongly typed table enum and is intended /// to be consumed by concept operations (deductions do not encode GRIB keys). /// /// Logging policy: /// - RESOLVE: value derived via deduction logic from input dictionaries /// - DEFAULT: value defaulted to a predefined constant when no specific mapping rule applies /// /// Error handling: /// - missing required inputs or unexpected failures throw `Mars2GribDeductionException` /// - underlying errors are preserved via nested exception propagation /// /// @section References /// Concept: /// - @ref dataTypeEncoding.h /// /// Related deductions: /// - @ref typeOfProcessedData.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include // Table includes #include "metkit/mars2grib/backend/tables/productionStatusOfProcessedData.h" #include "metkit/mars2grib/utils/generalUtils.h" // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the GRIB `productionStatusOfProcessedData` key from input dictionaries. /// /// This deduction resolves `productionStatusOfProcessedData` using MARS metadata. /// /// Current rules: /// - If `mars["class"] == "d1"`, return `DestinationEarth`. /// - If `mars["class"] == "e6"`, return `ReanalysisProducts`. /// - Otherwise, return `OperationalProducts`. /// /// @section Deduction contract /// - Reads: `mars["class"]` /// - Writes: none /// - Side effects: logging (RESOLVE,DEFAULT) /// - Failure mode: throws /// /// @tparam MarsDict_t /// Type of the MARS dictionary; must provide key `class`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (currently unused). /// /// @tparam OptDict_t /// Type of the options dictionary (currently unused). /// /// @param[in] mars /// MARS dictionary used to resolve `productionStatusOfProcessedData`. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved `tables::ProductionStatusOfProcessedData` enumeration value. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If required inputs are missing or if any unexpected error occurs during deduction. /// /// @note /// This deduction currently does not implement any override path via `par`. /// If an override mechanism is introduced, successful override must emit an OVERRIDE log entry. /// template tables::ProductionStatusOfProcessedData resolve_ProductionStatusOfProcessedData_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 { // TODO: Here we set the default to operational product; will need to clarify exact logic with DGOV // It will probably need to be inferred from "type", "class", "stream" auto productionStatusOfProcessedDataDefault = tables::ProductionStatusOfProcessedData::OperationalProducts; std::optional productionStatusOfProcessedData = std::nullopt; // Retrieve mandatory MARS class const auto marsClass = get_or_throw(mars, "class"); // Deduce productionStatusOfProcessedData from mars class if (marsClass == "d1") { // This is mandatory for DestinE because it is used inside eccodes to allocate the proper "localUseSection" // template. Setting this keyword reallocate the local use section. productionStatusOfProcessedData = tables::ProductionStatusOfProcessedData::DestinationEarth; } else if (marsClass == "e6") { // Special handling for ERA6 productionStatusOfProcessedData = tables::ProductionStatusOfProcessedData::ReanalysisProducts; } // Emit log entry based on deduction outcome if (productionStatusOfProcessedData.has_value()) { // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`productionStatusOfProcessedData` resolved from input dictionaries: value='"; logMsg += enum2name_ProductionStatusOfProcessedData_or_throw(productionStatusOfProcessedData.value()); logMsg += "'"; return logMsg; }()); // Success exit point return productionStatusOfProcessedData.value(); } else { // Default to operational product productionStatusOfProcessedData = productionStatusOfProcessedDataDefault; // Emit DEFAULT log entry MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`productionStatusOfProcessedData` defaulted to operational product: value='"; logMsg += enum2name_ProductionStatusOfProcessedData_or_throw(productionStatusOfProcessedData.value()); logMsg += "'"; return logMsg; }()); // Success exit point return productionStatusOfProcessedData.value(); } } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribDeductionException( "Failed to resolve `productionStatusOfProcessedData` from input dictionaries", Here())); } // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/perturbationNumber.h0000664000175000017500000000677515232567717027067 0ustar alastairalastair/* * (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 perturbationNumber.h /// @brief Resolve the perturbation (ensemble member) number from the MARS dictionary. /// /// This deduction retrieves the value associated with the MARS key `number`, /// which identifies the perturbation (ensemble member) for the encoded field. /// /// The value is treated as mandatory and is retrieved verbatim from the input /// dictionaries without inference, defaulting, or transformation. /// /// @section Deduction contract /// - Reads: `mars["number"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// @tparam MarsDict_t /// Type of the MARS dictionary; must provide the key `number`. /// /// @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 perturbation number is retrieved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The perturbation number resolved from input dictionaries. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `number` is missing, cannot be converted to `long`, or if any /// unexpected error occurs during deduction. /// /// @note /// This deduction performs no consistency validation against ensemble size. /// Cross-field constraints are handled by other ensemble deductions. /// /// @section References /// Concept: /// - @ref ensembleEncoding.h /// /// Related deductions: /// - @ref numberOfForecastsInEnsemble.h /// - @ref typeOfEnsembleForecast.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 { template long resolve_PerturbationNumber_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 perturbation number from input dictionaries auto perturbationNumber = get_or_throw(mars, "number"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`number` resolved from input dictionaries: value='"; logMsg += std::to_string(perturbationNumber); logMsg += "'"; return logMsg; }()); // Success exit point return perturbationNumber; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `number` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/numberOfComponents.h0000664000175000017500000001144215232567717027006 0ustar alastairalastair/* * (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 numberOfComponents.h /// @brief Deduction of the model-error ensemble size. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **total number of model-error realizations** /// (`numberOfComponents`) from the parameter dictionary. /// /// The value is not derivable from MARS alone. It must be supplied via /// the parameter dictionary by the upstream tool, typically read from /// the input GRIB1 handle being re-encoded. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - enforcing deterministic resolution rules /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply heuristic or data-driven inference /// - do NOT validate against GRIB code tables unless explicitly required /// /// 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 directly from input dictionaries /// /// @section References /// Concept: /// - @ref modelErrorEncoding.h /// /// Related deductions: /// - @ref componentIndex.h /// - @ref modelErrorType.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the total number of model-error realizations. /// /// @section Deduction contract /// - Reads: `par["numberOfComponents"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the total number of realizations in the /// model-error ensemble from the parameter dictionary. /// /// The value is treated as mandatory: it cannot be derived from MARS /// metadata alone and must be supplied by the upstream tool that /// populates the parameter dictionary (typically read from the input /// GRIB1 handle being re-encoded). /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused). /// /// @tparam ParDict_t /// Type of the parameter dictionary. Must provide the key /// `numberOfComponents`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary from which the ensemble size is retrieved. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The total number of model-error realizations. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `numberOfComponents` is missing from the parameter /// dictionary, cannot be converted to `long`, or if any unexpected error /// occurs during deduction. /// /// @note /// This deduction does not infer or default the value. Absence of the /// key in the parameter dictionary is considered a contract violation /// by the upstream tool. /// template long resolve_NumberOfComponents_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 parameter-dictionary numberOfComponents long numberOfComponents = get_or_throw(par, "numberOfComponents"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`numberOfComponents` resolved from input dictionaries: value="; logMsg += std::to_string(numberOfComponents); return logMsg; }()); // Success exit point return numberOfComponents; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `numberOfComponents` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/deductions.md0000664000175000017500000001002715232567717025473 0ustar alastairalastair@page mars2grib_deductions Deductions Deductions are small, single-purpose components used by the MARS-to-GRIB backend to resolve GRIB keys from input metadata. Each deduction reads specific keys from one or more dictionaries and deterministically produces a single GRIB value (or an optional value when the mapping is intentionally conservative). Each deduction is responsible for **exactly one GRIB key**. This one-to-one mapping makes the resolution logic explicit, traceable, and easy to audit. @section mars2grib_deductions_role Role in the pipeline Deductions sit between input metadata (MARS, parameter, and options dictionaries) and GRIB encoding. Their responsibilities are intentionally narrow: - Extract required inputs from dictionaries. - Apply explicit, deterministic mapping rules. - Return resolved values or raise a detailed error when resolution is not possible. - Emit structured diagnostic logs describing how a value was obtained. Deductions do **not**: - Infer missing values unless explicitly defined. - Apply silent fallbacks. - Validate physical or semantic correctness beyond documented rules. - Rely on pre-existing GRIB header state. @section mars2grib_deductions_inputs Input dictionaries Most deductions operate on these dictionary types: - **MARS dictionary**: request metadata (e.g. @c type, @c stream, @c param, @c timespan). - **Parameter dictionary**: user overrides or explicit encoding choices (e.g. @c tablesVersion, @c typeOfProcessedData). - **Options dictionary**: configuration flags controlling optional behavior. Unless a deduction explicitly declares a default or an optional mapping, required inputs must be present. @section mars2grib_deductions_errors Error handling Deductions follow a fail-fast strategy. When a required input is missing or invalid, the deduction throws a nested exception that preserves context. This makes failures traceable while avoiding silent behavior changes. @section mars2grib_deductions_logging Logging policy Deductions emit structured logs for traceability. Typical policy categories include: - **RESOLVE**: value derived from input dictionaries or deterministic mappings. - **OVERRIDE**: value explicitly set by the parameter dictionary. - **DEFAULT**: value supplied by a documented default rule. Logging statements must match the actual path taken in the code. @section mars2grib_deductions_debugging Debugging advantages The one-to-one relationship between a deduction and a GRIB key significantly simplifies debugging. Most error reports have the form: @code grib key "xxx" has the wrong value, it is supposed to be "a" instead is "b" @endcode Because each GRIB key is produced by a single, well-identified deduction: - The faulty component is immediately known from the key name. - The resolution path is localized and deterministic. - The input dictionaries and mapping rules involved are limited and explicit. - The structured logs describe exactly how the incorrect value was obtained. In practice, this makes it almost immediate to identify the logic that generated the error and to reproduce the issue. @section mars2grib_deductions_determinism Determinism and reproducibility Given the same inputs, deductions return the same outputs. Any dependence on runtime state (e.g. reading ecCodes defaults) must be explicit and documented in the deduction that performs it. @section mars2grib_deductions_new How to add a new deduction When implementing a new deduction: 1. Define the required inputs and where they are sourced. 2. Document mapping rules and defaults (if any). 3. Implement a single, deterministic resolution path. 4. Emit the correct log category based on how the value is obtained. 5. Throw a detailed exception on failure. 6. Bind the deduction to the single GRIB key it is responsible for. @section mars2grib_deductions_summary Summary Deductions are the authoritative mapping layer between MARS metadata and GRIB encoding. They are intentionally strict, deterministic, and auditable, ensuring reproducible behavior, clear diagnostics, and fast error localization across the backend.metkit-1.19.2/src/metkit/mars2grib/backend/deductions/firstWavelength.h0000664000175000017500000001161615232567717026342 0ustar alastairalastair/* * (C) Copyright 2026- 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 firstWavelength.h /// @brief Deduction of the first wavelength in meters for composition encoding. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **first wavelength** from MARS metadata. /// /// The deduction retrieves the wavelength from the MARS dictionary /// (expressed in nanometers) and converts it to meters before exposing /// the value to the encoding layer. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying explicit and minimal transformation logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, defaulting, or consistency checks /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref compositionEncoding.h /// /// Related deductions: /// - @ref chemId.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 first wavelength in meters from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["wavelength"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the first wavelength by retrieving the /// mandatory MARS key `wavelength` (expressed in nanometers) and /// converting it to meters. /// /// The conversion follows: /// \f$ \text{wavelength\_m} = \text{wavelength\_nm} \times 10^{-9} \f$ /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `wavelength` /// and conversion to `long`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the wavelength is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved wavelength in meters as a `double`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `wavelength` is missing, cannot be converted to `long`, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction assumes that the MARS `wavelength` value is expressed /// in nanometers. Alternative units are not supported. /// template double resolve_FirstWavelength_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 wavelength (in nanometers) long wavelengthInNanometers = get_or_throw(mars, "wavelength"); // Convert nanometers to meters double wavelengthInMeters = static_cast(wavelengthInNanometers) / 1000000000.0; // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`firstWavelength` deduced from mars dictionary: "; logMsg += std::to_string(wavelengthInMeters) + " [meters]"; logMsg += " (from " + std::to_string(wavelengthInNanometers) + " [nanometers])"; return logMsg; }()); // Success exit point return wavelengthInMeters; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `firstWavelength` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/methodNumber.h0000664000175000017500000001076715232567717025625 0ustar alastairalastair/* * (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 methodNumber.h /// @brief Deduction of the wave processing method identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **wave processing method identifier** (`methodNumber`) /// from MARS metadata. /// /// The deduction retrieves the method identifier explicitly from the /// MARS dictionary and returns it verbatim, without applying inference, /// defaulting, or semantic interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - enforcing deterministic resolution rules /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply heuristic or data-driven inference /// - do NOT validate against GRIB code tables unless explicitly required /// /// 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 directly from input dictionaries /// /// @section References /// Concept: /// - @ref longrangeEncoding.h /// /// Related deductions: /// - @ref systemNumber.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the wave processing method identifier. /// /// @section Deduction contract /// - Reads: `mars["method"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the wave processing method identifier /// from the MARS dictionary. /// /// The value is treated as mandatory and is returned verbatim as a /// numeric identifier. No inference, defaulting, or validation against /// GRIB code tables is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide the key `method`. /// /// @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 method identifier is retrieved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The wave processing method identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `method` is missing, cannot be converted to `long`, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction assumes that the method identifier is explicitly /// provided by the MARS dictionary and does not attempt any semantic /// interpretation or consistency checking. /// template long resolve_MethodNumber_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 method long methodNumber = get_or_throw(mars, "method"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`methodNumber` resolved from input dictionaries: value="; logMsg += std::to_string(methodNumber); return logMsg; }()); // Success exit point return methodNumber; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `method` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/detail/0000775000175000017500000000000015232567717024252 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/backend/deductions/detail/pv_137_be.h0000664000175000017500000015421315232567717026116 0ustar alastairalastair/* * (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 detail/pv_137_be.h /// @brief Predefined PV coefficient table extracted from IFS binary output. /// /// This file contains a statically defined table of PV coefficients used by /// the PV lookup and decoding infrastructure in the mars2grib backend. /// /// The values in this table have been obtained by performing a **binary dump /// in the IFS** of the PV array associated with a specific hybrid vertical /// coordinate configuration. /// /// The dumped binary values were: /// - captured verbatim from IFS runtime memory, /// - interpreted as IEEE754 double-precision floating-point values, /// - serialized into a C++ source representation without any numerical /// transformation or rounding. /// /// ## Encoding format /// - Each coefficient is stored as an **8-byte IEEE754 binary64 value** /// - Byte order is **big-endian**, independent of the host architecture /// - The table is therefore **endianness-neutral** and requires explicit /// decoding at runtime /// /// Endianness handling is performed explicitly by the PV decoding utilities /// in the mars2grib backend, which: /// - detect the host byte order at runtime, /// - apply byte swapping when required, /// - reconstruct native `double` values in a portable and deterministic way /// /// ## Design constraints /// - This file contains **data only** /// - No logic, validation, or interpretation is performed here /// - The numerical meaning of the PV coefficients is **not validated** /// - The data are assumed to be physically correct as provided by IFS /// /// ## Usage /// This table is intended to be: /// - included verbatim into the PV lookup infrastructure, /// - accessed via compile-time lookup tables, /// - decoded into native floating-point values only at the point of use /// /// @note /// The content of this file is tightly coupled to the IFS binary format /// and the specific PV configuration from which it was extracted. /// Any change in IFS vertical coordinate definitions requires regenerating /// this table from a fresh binary dump. /// /// @note /// No attempt must be made to modify, regenerate, or reinterpret the /// coefficients manually. /// /// @ingroup mars2grib_backend_deductions /// #pragma once #include "metkit/mars2grib/utils/generalUtils.h" static constexpr std::array pv_137_1002_be = { {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0xbf, 0x60, 0x00, 0x00, 0x00}, {0x40, 0x08, 0xd1, 0x63, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0x12, 0xaa, 0x11, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0x1b, 0x4f, 0xd9, 0x40, 0x00, 0x00, 0x00}, {0x40, 0x23, 0x7e, 0x72, 0x60, 0x00, 0x00, 0x00}, {0x40, 0x2b, 0x35, 0xfa, 0x20, 0x00, 0x00, 0x00}, {0x40, 0x32, 0x9b, 0xe2, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0x38, 0xfc, 0x58, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x40, 0x7e, 0x2b, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0x45, 0x70, 0x8b, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x4b, 0x7a, 0x4c, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0x51, 0x61, 0x51, 0x20, 0x00, 0x00, 0x00}, {0x40, 0x55, 0xb9, 0x56, 0x20, 0x00, 0x00, 0x00}, {0x40, 0x5a, 0xda, 0x9b, 0x80, 0x00, 0x00, 0x00}, {0x40, 0x60, 0x6d, 0x9d, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0x63, 0xe8, 0xf0, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0x67, 0xea, 0xd5, 0x80, 0x00, 0x00, 0x00}, {0x40, 0x6c, 0x7f, 0x01, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0x70, 0xd8, 0xa2, 0x20, 0x00, 0x00, 0x00}, {0x40, 0x73, 0xc6, 0xbb, 0x60, 0x00, 0x00, 0x00}, {0x40, 0x77, 0x0f, 0xb7, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0x7a, 0xb9, 0x7a, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0x7e, 0xc9, 0xdb, 0x40, 0x00, 0x00, 0x00}, {0x40, 0x81, 0xa3, 0x4e, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0x84, 0x1a, 0xb8, 0x20, 0x00, 0x00, 0x00}, {0x40, 0x86, 0xcd, 0xf4, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x89, 0xbf, 0xbe, 0x20, 0x00, 0x00, 0x00}, {0x40, 0x8c, 0xf2, 0xc2, 0x60, 0x00, 0x00, 0x00}, {0x40, 0x90, 0x34, 0xce, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x92, 0x13, 0x6a, 0x20, 0x00, 0x00, 0x00}, {0x40, 0x94, 0x16, 0x71, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x96, 0x3f, 0x14, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0x98, 0x8e, 0x7d, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0x9b, 0x05, 0xcb, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0x9d, 0xa6, 0x13, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xa0, 0x38, 0x31, 0x20, 0x00, 0x00, 0x00}, {0x40, 0xa1, 0xb2, 0xdd, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xa3, 0x43, 0x8a, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xa4, 0xea, 0xb2, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xa6, 0xa8, 0xc8, 0x60, 0x00, 0x00, 0x00}, {0x40, 0xa8, 0x7e, 0x3d, 0x20, 0x00, 0x00, 0x00}, {0x40, 0xaa, 0x6b, 0x7c, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xac, 0x70, 0xef, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xae, 0x8e, 0xfb, 0x20, 0x00, 0x00, 0x00}, {0x40, 0xb0, 0x62, 0xee, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xb1, 0x8a, 0xd1, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xb2, 0xbf, 0x26, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xb3, 0xff, 0xe5, 0x20, 0x00, 0x00, 0x00}, {0x40, 0xb5, 0x4c, 0xfd, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0xb6, 0xa6, 0x58, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xb8, 0x0c, 0x13, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xb9, 0x7e, 0xf2, 0x60, 0x00, 0x00, 0x00}, {0x40, 0xba, 0xff, 0xde, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0xbc, 0x8f, 0xde, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xbe, 0x2f, 0x69, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xbf, 0xdf, 0x5a, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0xc0, 0xd0, 0x43, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xc1, 0xba, 0x33, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xc2, 0xad, 0x57, 0x60, 0x00, 0x00, 0x00}, {0x40, 0xc3, 0xa8, 0xfd, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xc4, 0xac, 0x50, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0xc5, 0xb6, 0x54, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xc6, 0xc6, 0x08, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0xc7, 0xd9, 0xc6, 0x20, 0x00, 0x00, 0x00}, {0x40, 0xc8, 0xef, 0x6f, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xca, 0x06, 0x55, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0xcb, 0x1c, 0xaa, 0x60, 0x00, 0x00, 0x00}, {0x40, 0xcc, 0x30, 0x11, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0xcd, 0x3f, 0xce, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xce, 0x4a, 0x20, 0xe0, 0x00, 0x00, 0x00}, {0x40, 0xcf, 0x4d, 0x0e, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xd0, 0x23, 0xd4, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0xd0, 0x9c, 0x32, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xd1, 0x0e, 0xe7, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xd1, 0x7b, 0x67, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xd1, 0xe1, 0x1b, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xd2, 0x3f, 0x6e, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xd2, 0x95, 0xd2, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xd2, 0xe3, 0xe0, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0x29, 0x02, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0x64, 0xd9, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0x96, 0xfb, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0xbe, 0xea, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0xdc, 0x77, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0xef, 0x13, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0xf6, 0x85, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0xf2, 0x6e, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0xe2, 0x74, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0xc6, 0x60, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0x9d, 0xc5, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0x68, 0x81, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0xd3, 0x26, 0x24, 0xa0, 0x00, 0x00, 0x00}, {0x40, 0xd2, 0xd6, 0x8e, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xd2, 0x79, 0x5d, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xd2, 0x0e, 0x6d, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xd1, 0x95, 0xbb, 0x40, 0x00, 0x00, 0x00}, {0x40, 0xd1, 0x0f, 0xf5, 0xc0, 0x00, 0x00, 0x00}, {0x40, 0xd0, 0x7e, 0x2c, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xcf, 0xc3, 0x06, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xce, 0x76, 0x59, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xcd, 0x19, 0x3a, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xcb, 0xae, 0xa9, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xca, 0x39, 0xe2, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xc8, 0xbe, 0x21, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xc7, 0x3e, 0xab, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xc5, 0xbe, 0xa7, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xc4, 0x41, 0x16, 0x80, 0x00, 0x00, 0x00}, {0x40, 0xc2, 0xc8, 0xc2, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xc1, 0x58, 0x3a, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xbf, 0xe3, 0x60, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xbd, 0x2e, 0x58, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xba, 0x94, 0x6c, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xb8, 0x18, 0x88, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xb5, 0xbc, 0x62, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xb3, 0x81, 0xcc, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xb1, 0x69, 0x60, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xae, 0xe7, 0xec, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xab, 0x42, 0x78, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xa7, 0xe2, 0x88, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xa4, 0xc6, 0x48, 0x00, 0x00, 0x00, 0x00}, {0x40, 0xa1, 0xec, 0x7c, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x9e, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x99, 0xed, 0xe8, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x95, 0xae, 0x30, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x91, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x8c, 0xf4, 0x10, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x86, 0xf7, 0xf0, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x81, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x7a, 0x86, 0xa0, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x72, 0xe7, 0xa0, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x69, 0x4f, 0x80, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x5e, 0x86, 0x80, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x4f, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x36, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x40, 0x0e, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x3e, 0x64, 0x82, 0x2b, 0x00, 0x00, 0x00, 0x00}, {0x3e, 0xdc, 0x5b, 0x3f, 0x20, 0x00, 0x00, 0x00}, {0x3e, 0xf9, 0x87, 0xdd, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0x0e, 0xe4, 0x60, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0x1d, 0x56, 0x71, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0x2a, 0x07, 0x26, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0x36, 0x4e, 0x9f, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0x42, 0x66, 0xaa, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0x4d, 0x27, 0x53, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0x56, 0x2a, 0x13, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0x60, 0x51, 0x2f, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0x67, 0x67, 0xd2, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0x70, 0x43, 0xd8, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0x76, 0x07, 0x0c, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0x7d, 0x37, 0xe3, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0x82, 0xf7, 0xac, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0x88, 0x2d, 0xc1, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0x8e, 0x57, 0xa8, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0x92, 0xc2, 0x12, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0x96, 0xe4, 0x30, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0x9b, 0x9c, 0x53, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0xa0, 0x79, 0x62, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0xa3, 0x78, 0x32, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0xa6, 0xce, 0xfd, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0xaa, 0x81, 0xfe, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0xae, 0x94, 0xb9, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xb1, 0x85, 0xd3, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0xb3, 0xf5, 0x13, 0x60, 0x00, 0x00, 0x00}, {0x3f, 0xb6, 0x99, 0xe4, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0xb9, 0x76, 0x51, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0xbc, 0x8b, 0x91, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0xbf, 0xdb, 0xd5, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0xc1, 0xb4, 0x3c, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0xc3, 0x99, 0x99, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0xc5, 0x9e, 0xdb, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0xc7, 0xc4, 0xac, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xca, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xcc, 0x75, 0x67, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0xcf, 0x01, 0xda, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xd0, 0xd8, 0xf4, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0xd2, 0x43, 0x3d, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0xd3, 0xc0, 0x13, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xd5, 0x4e, 0xdf, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xd6, 0xed, 0xa3, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xd8, 0x99, 0x68, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0xda, 0x4f, 0xde, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0xdc, 0x0e, 0x9a, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xdd, 0xd2, 0xff, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0xdf, 0x9a, 0x6c, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0xe0, 0xb1, 0x1a, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xe1, 0x93, 0xe0, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xe2, 0x74, 0x43, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0xe3, 0x51, 0x15, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xe4, 0x29, 0x40, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0xe4, 0xfb, 0xca, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xe5, 0xc7, 0xd3, 0xe0, 0x00, 0x00, 0x00}, {0x3f, 0xe6, 0x8c, 0xa6, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xe7, 0x49, 0xa2, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xe7, 0xfe, 0x55, 0x60, 0x00, 0x00, 0x00}, {0x3f, 0xe8, 0xaa, 0x5f, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0xe9, 0x4d, 0x8d, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0xe9, 0xe7, 0xb8, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0xea, 0x78, 0xe1, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xeb, 0x01, 0x13, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xeb, 0x80, 0x77, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0xeb, 0xf7, 0x3a, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0xec, 0x65, 0xa4, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0xec, 0xcb, 0xfc, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0xed, 0x2a, 0x95, 0x60, 0x00, 0x00, 0x00}, {0x3f, 0xed, 0x81, 0xce, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xed, 0xd1, 0xf7, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0xee, 0x1b, 0x85, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0xee, 0x5e, 0xbc, 0x60, 0x00, 0x00, 0x00}, {0x3f, 0xee, 0x9c, 0x0d, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xee, 0xd3, 0xc7, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xef, 0x06, 0x41, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xef, 0x33, 0xd7, 0xa0, 0x00, 0x00, 0x00}, {0x3f, 0xef, 0x5c, 0xbf, 0x20, 0x00, 0x00, 0x00}, {0x3f, 0xef, 0x81, 0x5e, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xef, 0xa1, 0xc9, 0xc0, 0x00, 0x00, 0x00}, {0x3f, 0xef, 0xbe, 0x55, 0x40, 0x00, 0x00, 0x00}, {0x3f, 0xef, 0xd7, 0x0f, 0x80, 0x00, 0x00, 0x00}, {0x3f, 0xef, 0xec, 0x96, 0x00, 0x00, 0x00, 0x00}, {0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x37, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x36, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x34, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x32, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x30, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x2f, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x2d, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x2b, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x29, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x28, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x26, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x24, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x22, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x21, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x1f, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x1d, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x1b, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x1a, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x18, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x16, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x14, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x13, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x11, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x0f, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x0d, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x0c, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x0a, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x08, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x06, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x05, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x03, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x83, 0x01, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xff, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xfe, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xfc, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xfa, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xf8, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xf7, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xf5, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xf3, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xf1, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xf0, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xee, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xec, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xea, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xe9, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xe7, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xe5, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xe3, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xe2, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xe0, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xde, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xdc, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xdb, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xd9, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xd7, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xd5, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xd4, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xd2, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xd0, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xce, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xcd, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xcb, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xc9, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xc7, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xc6, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xc4, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xc2, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xc0, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xbf, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xbd, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xbb, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xb9, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xb8, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xb6, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xb4, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xb2, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xb1, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xaf, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xad, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xab, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xaa, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xa8, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xa6, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xa4, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xa3, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0xa1, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x9f, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x9d, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x9c, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x9a, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x98, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x96, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x95, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x93, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x91, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x8f, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x8e, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x8c, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x8a, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x88, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x87, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x85, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x83, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x81, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x80, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x7e, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x7c, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x7a, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x79, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x77, 0x50}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x75, 0x90}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x73, 0xd0}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x72, 0x10}, {0x00, 0x00, 0x15, 0x3e, 0x5f, 0x5b, 0x76, 0x30}, {0x00, 0x00, 0x15, 0x3d, 0x2c, 0x82, 0x70, 0x50}}}; // } // namespace metkit::mars2grib::backend::deductions::pv_detail::data metkit-1.19.2/src/metkit/mars2grib/backend/deductions/detail/ProductTime.h0000664000175000017500000010401015232567717026656 0ustar alastairalastair/* * (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 detail/ProductTime.h /// @brief Implementation detail of the `ProductTime` deduction. /// /// Defines: /// - `TimespanKind` (§6) /// - `ProductTimeInput` (§6) /// - `ProductTime` (§5) /// - `make_ProductTime_or_throw` (§6) /// - shared helpers (calendar arithmetic, alignment checks, signed-second /// shifts, fmt overloads) /// /// Types extracted to dedicated shared headers: /// - `StatisticalWindow` → `detail/StatisticalWindow.h` (§21) /// - `parse_StatType_or_throw`, /// `ParsedStatTypeBlock` → `detail/StatType.h` (§22) /// /// This is the **detail** half of the `ProductTime` module. The public /// resolver `resolve_ProductTime_or_throw` lives in /// `deductions/productTime.h` (function-primary, lowercase initial per /// §20.1; this file is type-primary and uses UpperCamelCase initial). /// /// All rules in this file are normative; see `deductions/timeProducts.md`. /// /// Error handling follows the mars2grib generic fail-fast pattern: /// - each helper/factory executes its complete body inside a `try` block /// - all exceptions are caught at the function boundary /// - failures are rethrown with `std::throw_with_nested` as /// `Mars2GribGenericException` /// - the original low-level exception is preserved as the nested cause /// - successful, non-exception execution is left unchanged /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include #include #include #include #include #include #include // eckit #include "eckit/exception/Exceptions.h" #include "eckit/types/Date.h" #include "eckit/types/DateTime.h" #include "eckit/types/Time.h" // Project #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/backend/deductions/detail/StatisticalWindow.h" #include "metkit/mars2grib/backend/tables/timeUnits.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions::detail { // ============================================================= // 1. Constants // ============================================================= /// /// @brief Maximum number of statistical windows a `ProductTime` may carry. /// /// Matches the maximum producible by the locked `stattype` grammar /// (§7.7): one `mo` block + one `da` block + one inner `timespan` window. /// inline constexpr std::size_t maxStatisticalWindows = 3; // ============================================================= // 2. ProductTime (§5) // ============================================================= /// /// @brief Canonical, immutable representation of all temporal information /// associated with a single MARS product. /// /// All members are `const`; the struct is copyable but not assignable. /// /// See `deductions/timeProducts.md` §5 for the full contract, including: /// - tri-equivalent instant invariant (§5.1) /// - window-end ordering invariant (§5.2) /// - reference-vs-initial-conditions invariant (§5.3) /// - per-consumer field-access table (§15) /// struct ProductTime { const eckit::DateTime labelDateTime; const eckit::DateTime initialConditionsDateTime; const eckit::DateTime referenceDateTime; /// Internal convention: `[windowStart, windowEnd)` when /// `windowStart < windowEnd`. For instant products /// `windowStart == windowEnd` (the point in time at `windowEnd`). const eckit::DateTime windowStart; const eckit::DateTime windowEnd; /// Valid entries: `statisticalWindows[0 .. statisticalWindowCount)`. /// Ordering: outermost → innermost. const std::array statisticalWindows; const std::size_t statisticalWindowCount; /// Sampling increment of the innermost statistical loop. /// `std::nullopt` for instant products (§9.1). /// Optionally absent for single-window statistical products (AIFS path, /// §9.4). Required and `> 0` for multi-window statistical products. const std::optional timeIncrementInSeconds; }; // ============================================================= // 3. TimespanKind (§6) // ============================================================= /// /// @brief Origin of the MARS `timespan` keyword in a `ProductTimeInput`. /// enum class TimespanKind { Missing, ///< MARS keyword absent. Duration, ///< MARS keyword carries a duration value. None ///< MARS keyword equals literal `"none"` (fakeDoubleLoop, §9.4). }; // ============================================================= // 4. ProductTimeInput (§6) // ============================================================= /// /// @brief Resolver-side input bundle for `make_ProductTime_or_throw`. /// /// The resolver normalizes raw MARS / par keys into this struct before /// invoking the factory. The factory does not consume raw MARS keys. /// struct ProductTimeInput { eckit::DateTime labelDateTime; std::optional initialConditionsDateTime; ///< from `hdate` / `htime` std::optional referenceDateTime; ///< from `fcyear` / `fcmonth` /// Offset from `referenceDateTime` to `ProductTime::windowEnd`. long stepInSeconds{0}; TimespanKind timespanKind{TimespanKind::Missing}; /// Valid only when `timespanKind == TimespanKind::Duration`. StatisticalWindow timespan{}; /// Temporal windows decoded from `stattype` (period part only), /// ordered outermost → innermost. std::array stattypeWindows{}; std::size_t stattypeWindowCount{0}; /// From `deductions::timeIncrementInSeconds_opt(mars, par)`, i.e. /// `par["timeIncrementInSeconds"]`. Absent for instants and for the AIFS /// single-window path (§9.4). std::optional timeIncrementInSeconds; }; // ============================================================= // 5. Conversion helpers (§7.5, §7.6) // ============================================================= /// /// @brief Parse a duration string (`step` / `timespan` syntax) to seconds. /// /// Bare numeric `N` parses as **N hours**. Suffixed numeric supports: /// - `h` hours /// - `m` minutes /// - `s` seconds /// - `d` days /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the duration string is malformed or cannot be converted. The original /// exception is preserved as a nested exception. /// inline long toSeconds_or_throw(std::string_view step) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; if (step.empty()) { throw Mars2GribDeductionException("Empty duration string", Here()); } std::size_t pos = 0; while (pos < step.size() && std::isdigit(static_cast(step[pos]))) { ++pos; } if (pos == 0) { throw Mars2GribDeductionException("Invalid duration format (no numeric part): '" + std::string(step) + "'", Here()); } long value = 0; try { value = std::stol(std::string(step.substr(0, pos))); } catch (...) { throw Mars2GribDeductionException("Invalid numeric value in duration: '" + std::string(step) + "'", Here()); } char unit = 'h'; // default unit if (pos < step.size()) { if (pos + 1 != step.size()) { throw Mars2GribDeductionException( "Invalid duration format (trailing characters): '" + std::string(step) + "'", Here()); } unit = step[pos]; } switch (unit) { case 'h': return value * 3600L; case 'm': return value * 60L; case 's': return value; case 'd': return value * 86400L; default: throw Mars2GribDeductionException( std::string("Unknown duration unit: '") + unit + "', expected={h,m,s,d}", Here()); } } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to convert duration string to seconds", Here())); } mars2gribUnreachable(); } /// /// @brief Convert a MARS-encoded `YYYYMMDD` integer to `eckit::Date`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline eckit::Date convert_YYYYMMDD2Date_or_throw(long YYYYMMDD) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; long YYYY = YYYYMMDD / 10000; long MM = (YYYYMMDD / 100) % 100; long DD = YYYYMMDD % 100; try { return eckit::Date(YYYY, MM, DD); } catch (const eckit::Exception& e) { throw Mars2GribDeductionException("Invalid date value '" + std::to_string(YYYYMMDD) + "': " + e.what(), Here()); } } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to convert YYYYMMDD value to eckit::Date", Here())); } mars2gribUnreachable(); } /// /// @brief Convert a MARS-encoded `HHMMSS` integer to `eckit::Time`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline eckit::Time convert_hhmmss2Time_or_throw(long hhmmss) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; long hh = hhmmss / 10000; long mm = (hhmmss / 100) % 100; long ss = hhmmss % 100; try { return eckit::Time(hh, mm, ss); } catch (const eckit::Exception& e) { throw Mars2GribDeductionException("Invalid time value '" + std::to_string(hhmmss) + "': " + e.what(), Here()); } } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to convert HHMMSS value to eckit::Time", Here())); } mars2gribUnreachable(); } // ============================================================= // 6. Allow-list / classification helpers (§3.1, §3.3) // ============================================================= /// /// @brief Test whether a `tables::TimeUnit` value belongs to the /// `StatisticalWindow` allow-list `{Second, Day, Month}` (§3.1). /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline bool isAllowedWindowUnit(tables::TimeUnit u) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { return u == tables::TimeUnit::Second || u == tables::TimeUnit::Day || u == tables::TimeUnit::Month; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to classify TimeUnit allow-list membership", Here())); } mars2gribUnreachable(); } /// /// @brief Test whether a `tables::TimeUnit` value is calendar-aligned /// (i.e. `Day` or `Month`) per the classification table in §3.3. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline bool isCalendarUnit(tables::TimeUnit u) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { return u == tables::TimeUnit::Day || u == tables::TimeUnit::Month; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to classify TimeUnit calendar alignment", Here())); } mars2gribUnreachable(); } // ============================================================= // 7. Calendar arithmetic (§9.6) // ============================================================= /// /// @brief Subtract `count` calendar months from a day=1, midnight `DateTime`. /// /// Precondition: `dt` is on day=1 at 00:00:00 (verified by alignment check /// (§10.10) before this function is called). /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline eckit::DateTime subtractCalendarMonths(const eckit::DateTime& dt, long count) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { long year = dt.date().year(); long month = dt.date().month(); // Convert to (year * 12 + (month-1)) basis so subtraction is trivial. long total = year * 12L + (month - 1L) - count; long newYear = total / 12L; long newMonthIdx = total % 12L; if (newMonthIdx < 0) { newMonthIdx += 12L; newYear -= 1L; } long newMonth = newMonthIdx + 1L; return eckit::DateTime(eckit::Date(newYear, newMonth, 1), eckit::Time(0, 0, 0)); } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to subtract calendar months from DateTime", Here())); } mars2gribUnreachable(); } /// /// @brief Subtract `count` calendar days from a midnight `DateTime`. /// /// Precondition: `dt` is at 00:00:00 (verified by alignment check (§10.9)). /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline eckit::DateTime subtractCalendarDays(const eckit::DateTime& dt, long count) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { eckit::Date d = dt.date(); d -= count; return eckit::DateTime(d, eckit::Time(0, 0, 0)); } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to subtract calendar days from DateTime", Here())); } mars2gribUnreachable(); } /// /// @brief Subtract `count` seconds from a `DateTime`. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline eckit::DateTime subtractSeconds(const eckit::DateTime& dt, long count) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { // assert(count >= 0); static constexpr long secondsPerDay = 24 * 60 * 60; eckit::Date d = dt.date(); // eckit::Time has operator eckit::Second(), and eckit::Second is double. eckit::Second t = dt.time(); // seconds since midnight const long wholeDays = count / secondsPerDay; const long remSecs = count % secondsPerDay; d -= wholeDays; if (t < static_cast(remSecs)) { d -= 1; t += static_cast(secondsPerDay); } t -= static_cast(remSecs); return eckit::DateTime(d, eckit::Time(t)); } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to subtract seconds from DateTime", Here())); } mars2gribUnreachable(); } /// /// @brief Apply `windowStart := windowEnd - window` per §9.6. /// /// Dispatches on `window.unit`. Precondition: `window.unit` is in the /// allow-list and any required alignment has been verified by the caller. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline eckit::DateTime applyWindowSubtraction(const eckit::DateTime& windowEnd, const StatisticalWindow& window) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; switch (window.unit) { case tables::TimeUnit::Second: return subtractSeconds(windowEnd, window.count); case tables::TimeUnit::Day: return subtractCalendarDays(windowEnd, window.count); case tables::TimeUnit::Month: return subtractCalendarMonths(windowEnd, window.count); default: throw Mars2GribDeductionException("Internal error: applyWindowSubtraction called with disallowed unit", Here()); } mars2gribUnreachable(); } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to apply statistical-window subtraction", Here())); } mars2gribUnreachable(); } // ============================================================= // 8. Alignment checks (§4.2, §4.3, §10.9, §10.10) // ============================================================= /// /// @brief Test whether a `DateTime` is at hh=00, mm=00, ss=00. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline bool isAtMidnight(const eckit::DateTime& dt) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { const eckit::Time& t = dt.time(); return t.hours() == 0 && t.minutes() == 0 && t.seconds() == 0; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to test DateTime midnight alignment", Here())); } mars2gribUnreachable(); } /// /// @brief Test whether a `DateTime` is on day=1 at midnight. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline bool isOnFirstOfMonthMidnight(const eckit::DateTime& dt) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { return isAtMidnight(dt) && dt.date().day() == 1; } catch (...) { std::throw_with_nested( Mars2GribGenericException("Failed to test DateTime first-of-month midnight alignment", Here())); } mars2gribUnreachable(); } // ============================================================= // 9. Inline string formatting (§12, §13) // ============================================================= /// /// @brief Format an `eckit::DateTime` as an ISO-8601 string for logs/errors. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// /// inline std::string fmt(const eckit::DateTime& dt) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { return dt.iso(true); } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to format DateTime", Here())); } mars2gribUnreachable(); } /// /// @brief Format a `tables::TimeUnit` value as a short symbolic name. /// /// Uses the canonical names produced by `tables::enum2name_TimeUnit_or_throw` /// for the allow-list values, with a numeric fallback for any other value. /// /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// inline std::string fmt(tables::TimeUnit u) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { switch (u) { case tables::TimeUnit::Second: return "second"; case tables::TimeUnit::Day: return "day"; case tables::TimeUnit::Month: return "month"; default: return "TimeUnit(" + std::to_string(static_cast(u)) + ")"; } } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to format TimeUnit", Here())); } mars2gribUnreachable(); } /// /// @brief Format a `StatisticalWindow` as `{unit,count}`. /// /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// inline std::string fmt(const StatisticalWindow& w) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { return "{" + fmt(w.unit) + "," + std::to_string(w.count) + "}"; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to format StatisticalWindow", Here())); } mars2gribUnreachable(); } /// /// @brief Format the populated prefix of a `StatisticalWindow` array. /// /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If the helper fails. The original exception is preserved as a nested /// exception. /// inline std::string fmt(const std::array& a, std::size_t count) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { std::string s{"["}; for (std::size_t i = 0; i < count; ++i) { if (i) s += ","; s += fmt(a[i]); } s += "]"; return s; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to format StatisticalWindow array", Here())); } mars2gribUnreachable(); } // ============================================================= // 10. Factory: make_ProductTime_or_throw (§6, §9, §10) // ============================================================= /// /// @brief Validate input invariants and construct an immutable `ProductTime`. /// /// Performs (in order): /// 1. Default resolution for `initialConditionsDateTime` and `referenceDateTime` /// (§7.3, §7.4). /// 2. Computes `windowEnd := referenceDateTime + stepInSeconds` (§7.5). /// 3. Assembles `statisticalWindows` per the case table (§9). /// 4. Computes `windowStart` per §9. /// 5. Validates all invariants (§5.1, §5.2, §5.3) and all hard errors (§10). /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribGenericException /// If any ProductTime rule or invariant is violated, or if any unexpected /// error occurs. The original exception is preserved as a nested exception; /// rule-violation check sites remain tagged with the corresponding §10 entry /// number in the nested cause. /// inline ProductTime make_ProductTime_or_throw(const ProductTimeInput& input) { using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; try { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; // --------------------------------------------------------- // Default resolution (§7.3, §7.4) // --------------------------------------------------------- const eckit::DateTime labelDateTime = input.labelDateTime; // §7.3: hdate / htime defaulting (the resolver has already enforced // §10.2; here we just apply the fall-through to labelDateTime). const eckit::DateTime initialConditionsDateTime = input.initialConditionsDateTime.value_or(labelDateTime); // §7.4: fcyear / fcmonth defaulting. const eckit::DateTime referenceDateTime = input.referenceDateTime.value_or(initialConditionsDateTime); // --------------------------------------------------------- // §5.3: referenceDateTime >= initialConditionsDateTime (§10.4) // --------------------------------------------------------- if (referenceDateTime < initialConditionsDateTime) { throw Mars2GribDeductionException("ProductTime invariant violated [§10.4]: referenceDateTime ('" + fmt(referenceDateTime) + "') < initialConditionsDateTime ('" + fmt(initialConditionsDateTime) + "')", Here()); } // --------------------------------------------------------- // windowEnd (§7.5) // --------------------------------------------------------- const eckit::DateTime windowEnd = referenceDateTime + static_cast(input.stepInSeconds); // --------------------------------------------------------- // §9: window assembly // --------------------------------------------------------- std::array windows{}; std::size_t windowCount = 0; eckit::DateTime windowStart = windowEnd; const bool hasTimespanDuration = (input.timespanKind == TimespanKind::Duration); const bool hasTimespanNone = (input.timespanKind == TimespanKind::None); const bool hasTimespanMissing = (input.timespanKind == TimespanKind::Missing); const std::size_t nStat = input.stattypeWindowCount; // Case dispatch (§9). if (hasTimespanMissing && nStat == 0) { // ----- §9.1: Instant product ----- windowCount = 0; windowStart = windowEnd; } else if (hasTimespanDuration && nStat == 0) { // ----- §9.2: Old-style single-loop statistic ----- windows[0] = input.timespan; windowCount = 1; // windowStart computed after allow-list / positivity / alignment. } else if (hasTimespanDuration && nStat >= 1) { // ----- §9.3: Old-style multi-loop statistic ----- if (nStat + 1 > maxStatisticalWindows) { throw Mars2GribDeductionException("ProductTime invariant violated [§10.15]: statisticalWindowCount (" + std::to_string(nStat + 1) + ") > maxStatisticalWindows (" + std::to_string(maxStatisticalWindows) + ")", Here()); } for (std::size_t i = 0; i < nStat; ++i) { windows[i] = input.stattypeWindows[i]; } windows[nStat] = input.timespan; windowCount = nStat + 1; } else if (hasTimespanNone && nStat == 1) { // ----- §9.4: New-style fakeDoubleLoop ----- windows[0] = input.stattypeWindows[0]; windowCount = 1; } else if (hasTimespanNone && nStat == 0) { // §10.7: timespan = none but stattype missing. throw Mars2GribDeductionException( "ProductTime invariant violated [§10.7]: timespan='none' requires " "exactly one stattype block, got 0", Here()); } else if (hasTimespanNone && nStat > 1) { // §10.8: timespan = none with more than one stattype block. throw Mars2GribDeductionException( "ProductTime invariant violated [§10.8]: timespan='none' requires " "exactly one stattype block, got " + std::to_string(nStat), Here()); } else if (hasTimespanMissing && nStat >= 1) { // §10.6: stattype present but timespan missing. throw Mars2GribDeductionException("ProductTime invariant violated [§10.6]: stattype present (" + std::to_string(nStat) + " block(s)) but timespan is missing", Here()); } else { // Defensive: all combinations should be covered above. throw Mars2GribDeductionException( "ProductTime internal error: unhandled (timespanKind, stattypeCount) combination", Here()); } // ----------------------------------------------------------------- // Per-window validation (§3.1, §3.2 → §10.11a, §10.11b, §10.18 (b)) // ----------------------------------------------------------------- for (std::size_t i = 0; i < windowCount; ++i) { const StatisticalWindow& w = windows[i]; if (w.count <= 0) { throw Mars2GribDeductionException("ProductTime invariant violated [§10.11a]: statisticalWindows[" + std::to_string(i) + "] has non-positive count (" + std::to_string(w.count) + ")", Here()); } if (!isAllowedWindowUnit(w.unit)) { throw Mars2GribDeductionException("ProductTime invariant violated [§10.18(b)]: statisticalWindows[" + std::to_string(i) + "] uses disallowed TimeUnit '" + fmt(w.unit) + "', expected one of {second, day, month}", Here()); } } // --------------------------------------------------------- // Outermost-window alignment (§4.4, §10.9, §10.10) and windowStart // --------------------------------------------------------- if (windowCount > 0) { const StatisticalWindow& outermost = windows[0]; if (outermost.unit == tables::TimeUnit::Day) { if (!isAtMidnight(windowEnd)) { throw Mars2GribDeductionException( "ProductTime invariant violated [§10.9]: outermost window is " "calendar-day-aligned but windowEnd ('" + fmt(windowEnd) + "') is not at hh=00,mm=00,ss=00", Here()); } } else if (outermost.unit == tables::TimeUnit::Month) { if (!isOnFirstOfMonthMidnight(windowEnd)) { throw Mars2GribDeductionException( "ProductTime invariant violated [§10.10]: outermost window is " "calendar-month-aligned but windowEnd ('" + fmt(windowEnd) + "') is not on day=1 at hh=00,mm=00,ss=00", Here()); } } // tables::TimeUnit::Second: no alignment required. windowStart = applyWindowSubtraction(windowEnd, outermost); } // --------------------------------------------------------- // §5.2: windowStart <= windowEnd (defensive) // --------------------------------------------------------- if (windowStart > windowEnd) { throw Mars2GribDeductionException("ProductTime invariant violated [§5.2]: windowStart ('" + fmt(windowStart) + "') > windowEnd ('" + fmt(windowEnd) + "')", Here()); } // --------------------------------------------------------- // timeIncrementInSeconds validation (§7.8, §9.5, §10.13, §10.14) // --------------------------------------------------------- std::optional tInc = input.timeIncrementInSeconds; if (tInc.has_value() && tInc.value() < 0) { throw Mars2GribDeductionException("ProductTime invariant violated [§10.14]: timeIncrementInSeconds < 0 ('" + std::to_string(tInc.value()) + "')", Here()); } if (windowCount >= 2 && !tInc.has_value()) { throw Mars2GribDeductionException("ProductTime invariant violated [§10.13]: statisticalWindowCount (" + std::to_string(windowCount) + ") >= 2 requires timeIncrementInSeconds to be present", Here()); } // --------------------------------------------------------- // §5.1: tri-equivalent instant invariant (§10.5) // --------------------------------------------------------- const bool a = (windowStart == windowEnd); const bool b = (windowCount == 0); const bool c = !tInc.has_value(); if (!((a == b) && (b == c))) { throw Mars2GribDeductionException( std::string("ProductTime invariant violated [§10.5]: tri-equivalence broken: ") + "(windowStart==windowEnd)=" + (a ? "true" : "false") + ", (statisticalWindowCount==0)=" + (b ? "true" : "false") + ", (timeIncrementInSeconds==nullopt)=" + (c ? "true" : "false"), Here()); } /// @todo: Handle the case of zero windows with stepInSeconds == 0 (instant product) /// but timeIncrementInSeconds present. This is technically a violation of the /// tri-equivalence invariant (§10.5) but we may want to allow it as scientists /// wants do avoid 2 requests for fields that have an increasing window. // --------------------------------------------------------- // Construct the immutable ProductTime // --------------------------------------------------------- return ProductTime{ labelDateTime, initialConditionsDateTime, referenceDateTime, windowStart, windowEnd, windows, windowCount, tInc}; } catch (...) { std::throw_with_nested(Mars2GribGenericException("Failed to construct ProductTime", Here())); } mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::deductions::detailmetkit-1.19.2/src/metkit/mars2grib/backend/deductions/detail/StatisticalWindow.h0000664000175000017500000000542415232567717030104 0ustar alastairalastair/* * (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 detail/StatisticalWindow.h /// @brief Shared `StatisticalWindow` type used by `ProductTime` and the /// shared `stattype` parser. /// /// `StatisticalWindow` represents one statistical window as a /// `(unit, count)` pair, reusing the GRIB-aligned `tables::TimeUnit` enum /// (GRIB2 Code Table 4.4) rather than introducing a parallel "kind" enum. /// /// This header is **shared infrastructure**, not a deduction. It is consumed /// by: /// - `deductions/detail/ProductTime.h` (stores values inside `ProductTime`) /// - `deductions/detail/StatType.h` (the shared `stattype` parser) /// /// The two-level allow-list enforcement is documented in /// `deductions/timeProducts.md` §10.18: /// - the parser (§22) enforces the narrow `stattype`-grammar allow-list /// `{Day, Month}` at parse time; /// - the factory `make_ProductTime_or_throw` (§detail/ProductTime.h) /// enforces the extended assembled-window allow-list `{Second, Day, /// Month}` after window assembly. /// /// See `deductions/timeProducts.md` §3, §21 for the full normative /// specification. /// /// @ingroup mars2grib_backend_deductions /// #pragma once // Project #include "metkit/mars2grib/backend/tables/timeUnits.h" namespace metkit::mars2grib::backend::deductions::detail { /// /// @brief One statistical window: a `tables::TimeUnit` + a count. /// /// Reuses the GRIB-aligned `tables::TimeUnit` enum (GRIB2 Code Table 4.4) /// rather than introducing a parallel "kind" enum. /// /// **Allow-list (assembled `ProductTime::statisticalWindows` entry)**: /// only `Second`, `Day`, `Month` are legal. Any other `TimeUnit` value (in /// particular `Hour`, `Minute`, `Hours3/6/12`, `Year`, `Decade`, `Normal`, /// `Century`, `Missing`) is rejected by the factory `make_ProductTime_or_throw` /// (§10.18 (b)). /// /// **Allow-list (parser-produced entry)**: only `Day`, `Month` are legal. /// The narrower `stattype`-grammar allow-list is enforced inside /// `parse_StatType_or_throw` (§10.18 (a)). /// /// **Invariant**: `count > 0` for any window stored inside a `ProductTime` /// (§3.2 / §10.11). The default-constructed value (`count == 0`) is legal at /// the C++ language level but illegal as a `ProductTime` member. /// struct StatisticalWindow { tables::TimeUnit unit{tables::TimeUnit::Second}; long count{0}; }; } // namespace metkit::mars2grib::backend::deductions::detail metkit-1.19.2/src/metkit/mars2grib/backend/deductions/detail/StatType.h0000664000175000017500000002364415232567717026211 0ustar alastairalastair/* * (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 detail/StatType.h /// @brief Shared `stattype` parser used by both `ProductTime` and /// `typeOfStatisticalProcessing`. /// /// This header is **shared infrastructure**, not a deduction. It exposes: /// - the `ParsedStatTypeBlock` value type (per-block, pre-mapped to GRIB /// table values); /// - the `parse_StatType_or_throw` function, which decodes the locked /// `stattype` grammar (§7.7) into a vector of blocks in MARS textual /// order (outermost → innermost). /// /// **Why shared**: both `ProductTime` (which needs the *period* part) and /// `typeOfStatisticalProcessing` (which needs the *operation* part) consume /// the same `stattype` string. A single shared parser eliminates the drift /// risk between them — both deductions size their output arrays in lock-step /// from the same parse result. /// /// **Allow-lists enforced by this parser** (parser-level hard errors): /// - period unit ∈ `{mo, da}` → §10.16 / §10.18 (a) /// - operation ∈ `{av, mn, mx, sd}` → §10.16 /// - block ordering: `mo` precedes `da` → §10.17 /// - block count ∈ `{1, 2}` → §10.16 /// /// The extended `Second`-inclusive allow-list applies only to the assembled /// `ProductTime::statisticalWindows` array (where the innermost window may /// originate from `timespan`); that check lives in /// `make_ProductTime_or_throw`. /// /// See `deductions/timeProducts.md` §22 for the full normative /// specification. /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include #include #include // eckit #include "eckit/exception/Exceptions.h" // Project #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/backend/deductions/detail/StatisticalWindow.h" #include "metkit/mars2grib/backend/tables/timeUnits.h" #include "metkit/mars2grib/backend/tables/typeOfStatisticalProcessing.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions::detail { // ============================================================= // 1. ParsedStatTypeBlock (§22.2) // ============================================================= /// /// @brief One decoded `stattype` block, pre-mapped to GRIB table values. /// /// - `timeWindow` carries the **period** part of the block (the /// stattype-grammar pair `(period_unit, count)`). For locked grammar /// tokens `mo` / `da`, the parser emits `count = 1` and /// `unit = tables::TimeUnit::Month` / `tables::TimeUnit::Day`. /// - `typeOfStatisticalProcessing` carries the **operation** part of the /// block, already mapped to a value of GRIB Code Table 4.10: /// - `av` → `Average` /// - `mn` → `Minimum` /// - `mx` → `Maximum` /// - `sd` → `StandardDeviation` /// struct ParsedStatTypeBlock { StatisticalWindow timeWindow; tables::TypeOfStatisticalProcessing typeOfStatisticalProcessing; }; // ============================================================= // 2. Single-token decoders (private helpers) // ============================================================= namespace impl { /// /// @brief Decode a single period token to a `StatisticalWindow` /// with `count = 1`. /// /// @throws Mars2GribDeductionException on unknown token (§10.16) or /// narrow-allow-list violation (§10.18 (a)). /// inline StatisticalWindow decodePeriod_or_throw(std::string_view s, const std::string& fullStatType) { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; if (s == "da") return StatisticalWindow{tables::TimeUnit::Day, 1}; if (s == "mo") return StatisticalWindow{tables::TimeUnit::Month, 1}; throw Mars2GribDeductionException("Invalid stattype period token [§10.16/§10.18(a)]: actual='" + std::string(s) + "', expected={'da','mo'} (in stattype='" + fullStatType + "')", Here()); } /// /// @brief Decode a single operation token to a /// `tables::TypeOfStatisticalProcessing`. /// /// @throws Mars2GribDeductionException on unknown token (§10.16). /// inline tables::TypeOfStatisticalProcessing decodeOp_or_throw(std::string_view s, const std::string& fullStatType) { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; if (s == "av") return tables::TypeOfStatisticalProcessing::Average; if (s == "mn") return tables::TypeOfStatisticalProcessing::Minimum; if (s == "mx") return tables::TypeOfStatisticalProcessing::Maximum; if (s == "sd") return tables::TypeOfStatisticalProcessing::StandardDeviation; throw Mars2GribDeductionException("Invalid stattype operation token [§10.16]: actual='" + std::string(s) + "', expected={'av','mn','mx','sd'} (in stattype='" + fullStatType + "')", Here()); } } // namespace impl // ============================================================= // 3. parse_StatType_or_throw (§22.3) // ============================================================= /// /// @brief Parse a `stattype` string per the locked grammar (§7.7). /// /// Grammar: /// @code /// stattype := block ('_' block)* /// block := period operation (4 chars) /// period := 'mo' | 'da' /// operation := 'av' | 'mn' | 'mx' | 'sd' /// @endcode /// /// Semantic constraints (parser-level hard errors): /// - block count MUST be in `{1, 2}` (§10.16); /// - if both `mo` and `da` blocks are present, `mo` MUST precede `da` /// (§10.17); /// - all unit and operation tokens MUST belong to the locked allow-lists /// (§10.16, §10.18 (a)). /// /// @param[in] stattype Raw textual value of the MARS `stattype` keyword. /// MUST be non-empty; callers MUST NOT invoke the /// parser when the `stattype` key is absent from the /// MARS dictionary. /// /// @return A vector of size 1 or 2, in MARS textual order (outermost first, /// innermost last). /// /// @throws Mars2GribDeductionException on any grammar (§10.16), /// ordering (§10.17), or narrow-allow-list (§10.18 (a)) violation. /// /// @note Each block's `typeOfStatisticalProcessing` is already mapped to a /// value of GRIB Code Table 4.10; consumers do not need to perform /// any further mapping. /// inline std::vector parse_StatType_or_throw(const std::string& stattype) { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; if (stattype.empty()) { throw Mars2GribDeductionException( "Invalid stattype: empty string (caller MUST NOT invoke " "parse_StatType_or_throw when 'stattype' is absent)", Here()); } std::vector blocks; std::size_t pos = 0; while (pos < stattype.size()) { if (pos + 4 > stattype.size()) { throw Mars2GribDeductionException( "Invalid stattype format [§10.16]: incomplete 4-char block " "at position " + std::to_string(pos) + " in '" + stattype + "'", Here()); } StatisticalWindow window = impl::decodePeriod_or_throw(stattype.substr(pos, 2), stattype); tables::TypeOfStatisticalProcessing op = impl::decodeOp_or_throw(stattype.substr(pos + 2, 2), stattype); blocks.push_back(ParsedStatTypeBlock{window, op}); pos += 4; if (pos < stattype.size()) { if (stattype[pos] != '_') { throw Mars2GribDeductionException("Invalid stattype separator [§10.16] at position " + std::to_string(pos) + " in '" + stattype + "': expected '_'", Here()); } ++pos; } } // Block-count limit: 1 or 2 blocks (§22.4 / §10.16). if (blocks.empty() || blocks.size() > 2) { throw Mars2GribDeductionException("Invalid stattype [§10.16]: block count (" + std::to_string(blocks.size()) + ") outside allowed range {1, 2} (in stattype='" + stattype + "')", Here()); } // Semantic validation (§10.17): at most one mo, at most one da, mo precedes da. int moIndex = -1; int daIndex = -1; for (std::size_t i = 0; i < blocks.size(); ++i) { if (blocks[i].timeWindow.unit == tables::TimeUnit::Month) { if (moIndex != -1) { throw Mars2GribDeductionException( "Invalid stattype [§10.17] '" + stattype + "': more than one 'mo' block", Here()); } moIndex = static_cast(i); } if (blocks[i].timeWindow.unit == tables::TimeUnit::Day) { if (daIndex != -1) { throw Mars2GribDeductionException( "Invalid stattype [§10.17] '" + stattype + "': more than one 'da' block", Here()); } daIndex = static_cast(i); } } if (moIndex != -1 && daIndex != -1 && moIndex > daIndex) { throw Mars2GribDeductionException("Invalid stattype [§10.17] '" + stattype + "': blocks not in outermost-to-innermost order " "('mo' must precede 'da')", Here()); } return blocks; } } // namespace metkit::mars2grib::backend::deductions::detail metkit-1.19.2/src/metkit/mars2grib/backend/deductions/bitsPerValue.h0000664000175000017500000003346015232567717025574 0ustar alastairalastair/* * (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 bitsPerValue.h /// @brief Deduction of the GRIB `bitsPerValue` packing parameter. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **GRIB bits-per-value packing parameter** used by the /// Data Representation Section. /// /// The deduction logic is **explicitly split by data representation**: /// - **Gridded data** use a metadata-driven default mapping derived from /// legacy MultIO behavior, with optional user override. /// - **Spectral data** use a fixed default packing precision, with /// optional user override. /// /// This separation is intentional and reflects fundamentally different /// packing policies for gridded and spectral fields. The two code paths /// are implemented as distinct deductions to avoid hidden coupling, /// conditional complexity, and ambiguous semantics. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying deterministic deduction and validation logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply heuristic or data-driven inference /// - do NOT perform GRIB table validation /// /// 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: /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// - DEFAULT: value defaulted to a predefined constant or derived via default deduction /// logic from input dictionaries due to missing input /// @section References /// Concept: /// - @ref packingEncoding.h /// /// Related deductions: /// - @ref laplacianOperator.h /// - @ref subSetTrunc.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include // Core deduction includes #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { namespace details { /// /// @brief Determine the default GRIB packing precision (bitsPerValue). /// /// This function returns the number of bits per value used for GRIB /// data packing based on: /// - the GRIB parameter identifier (`paramId`) /// - the vertical level prefix (`prefix`) /// - whether compression is enabled (`enableCompression`) /// /// The logic implemented here is directly derived from the legacy function /// `LOOKUP_BITS_PER_VALUE_DEFAULT` code path in **MultIO**, and is preserved to ensure /// bit-for-bit compatibility with existing production workflows. /// - /src/multiom/ifs2mars/ifs2mars/ifs2mars_mod.F90 /// /// @param paramId /// GRIB parameter identifier (discipline/category/number flattened). /// /// @param prefix /// Vertical level prefix: /// - `"pl"` : pressure levels /// - `"ml"` : model levels /// /// @param enableCompression /// If true, reduced precision may be applied for model-level fields. /// /// @return /// Number of bits per value to be used for GRIB packing. /// /// @details /// Decision logic (evaluated in order): /// /// - `paramId == 248` (Cloud cover) /// → 8 bits /// /// - `paramId == 141` (backward compatibility), /// `paramId == 228141` (Snow depth), /// `paramId == 244` (Forecast surface roughness) /// → 24 bits /// /// - `paramId == 246` (Cloud liquid water content) on pressure levels /// → 12 bits /// /// - `paramId == 247` (Cloud ice water content) on pressure levels /// → 12 bits /// /// - `210000 < paramId < 228000` /// → 24 bits /// /// - `paramId == 260510` (Cloudy brightness temperature), /// `paramId == 260511` (Clear-sky brightness temperature) /// → 10 bits /// /// - Compression enabled on model levels /// → 10 bits /// /// - Default case /// → 16 bits /// /// @note /// The function is purely deterministic and has no side effects. /// No validation of `paramId` or `prefix` is performed. /// /// @note /// The logic is not 100% identical to the original MultIO code path. /// Logic for parameters between 80 and 120 has been removed, as it /// requires additional parameters, and in this case the override can /// be used anyway. /// inline long lookup_bitsPerValueGridded_default(long paramId, std::string prefix, bool enableCompression) { // Parameter IDs (hard-coded) constexpr long CLOUD_COVER = 248; constexpr long SNOW_DEPTH_BACKWARD_COMPAT = 141; constexpr long SNOW_DEPTH = 228141; constexpr long FORECAST_SURFACE_ROUGHNESS = 244; constexpr long CLOUD_LIQUID_WATER_CONTENT = 246; constexpr long CLOUD_ICE_WATER_CONTENT = 247; constexpr long CLOUDY_BRIGHTNESS_TEMPERATURE = 260510; constexpr long CLEAR_SKY_BRIGHTNESS_TEMPERATURE = 260511; if (paramId == CLOUD_COVER) { return 8; } else if (paramId == SNOW_DEPTH_BACKWARD_COMPAT || paramId == SNOW_DEPTH || paramId == FORECAST_SURFACE_ROUGHNESS) { return 24; } else if (paramId == CLOUD_LIQUID_WATER_CONTENT && prefix == "pl") { return 12; } else if (paramId == CLOUD_ICE_WATER_CONTENT && prefix == "pl") { return 12; } else if (paramId > 210000 && paramId < 228000) { return 24; } else if (paramId == CLOUDY_BRIGHTNESS_TEMPERATURE || paramId == CLEAR_SKY_BRIGHTNESS_TEMPERATURE) { return 10; } else if (enableCompression && prefix == "ml") { return 10; } else { return 16; } } } // namespace details /// /// @brief Resolve the GRIB `bitsPerValue` packing parameter for gridded data. /// /// @section Deduction contract /// - Reads: /// - `par["bitsPerValue"]` (if present), /// - otherwise `mars["param"]`, `mars["levtype"]`, /// and `opt["enableBitsPerValueCompression"]` /// - Writes: none /// - Side effects: logging (OVERRIDE or DEFAULT) /// - Failure mode: throws /// /// This deduction resolves the number of bits per value used for GRIB /// numeric packing of **gridded fields**. /// /// If `bitsPerValue` is explicitly provided in the parameter dictionary, /// it is taken verbatim and overrides any default deduction logic. /// Otherwise, a deterministic default mapping is applied based on /// MARS metadata and encoder options. /// /// The default mapping logic is delegated to /// `details::lookup_bitsPerValueGridded_default` and is designed to preserve /// bit-for-bit compatibility with legacy MultIO workflows. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide `param` and `levtype` /// if default deduction is required. /// /// @tparam ParDict_t /// Type of the parameter dictionary. May contain `bitsPerValue`. /// /// @tparam OptDict_t /// Type of the options dictionary. May contain /// `enableBitsPerValueCompression`. /// /// @param[in] mars /// MARS dictionary providing metadata for default deduction. /// /// @param[in] par /// Parameter dictionary optionally providing `bitsPerValue`. /// /// @param[in] opt /// Options dictionary controlling default deduction behavior. /// /// @return /// The resolved number of bits per value to be used for GRIB packing /// of gridded data. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If required inputs are missing, if the resolved value is outside /// the supported range, or if any unexpected error occurs. /// /// @note /// This deduction enforces strict numeric validation but does not /// attempt to optimize packing precision based on data statistics /// or parameter semantics. /// template long resolve_BitsPerValueGridded_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { long bitsPerValue; if (has(par, "bitsPerValue")) { // Retrieve mandatory bitsPerValue from parameter dictionary bitsPerValue = get_or_throw(par, "bitsPerValue"); // Emit OVERRIDE log entry MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`bitsPerValue` overridden by parameter dictionary: value='"; logMsg += std::to_string(bitsPerValue) + "'"; return logMsg; }()); } else { // Retrieve auxiliary values for default lookup long param = get_or_throw(mars, "param"); std::string levtype = get_or_throw(mars, "levtype"); bool applyCompression = utils::bitsPerValueCompressionEnabled(opt); // Resolve bitsPerValue from default mapping bitsPerValue = details::lookup_bitsPerValueGridded_default(param, levtype, applyCompression); // Emit DEFAULT log entry MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`bitsPerValue` defaulted to: value='"; logMsg += std::to_string(bitsPerValue) + "'"; return logMsg; }()); } // Validate bits per value if (bitsPerValue < 0 || bitsPerValue > 64) { std::string errMsg = "Invalid `bitsPerValue`: value='"; errMsg += std::to_string(bitsPerValue) + "'"; throw Mars2GribDeductionException(errMsg, Here()); } // Success exit point return bitsPerValue; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `bitsPerValue` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; /// /// @brief Resolve the GRIB `bitsPerValue` packing parameter for spectral data. /// /// @section Deduction contract /// - Reads: `par["bitsPerValue"]` (if present) /// - Writes: none /// - Side effects: logging (OVERRIDE or DEFAULT) /// - Failure mode: throws /// /// This deduction resolves the number of bits per value used for GRIB /// numeric packing of **spectral fields**. /// /// If `bitsPerValue` is explicitly provided in the parameter dictionary, /// it is taken verbatim and overrides any deduction logic. /// Otherwise, a fixed default value of `16` bits is applied. /// /// No MARS metadata is consulted for spectral packing. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused by this deduction). /// /// @tparam ParDict_t /// Type of the parameter dictionary. May contain `bitsPerValue`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary optionally providing `bitsPerValue`. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved number of bits per value to be used for GRIB packing /// of spectral data. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the resolved value is outside the supported range or if any /// unexpected error occurs. /// /// @note /// This deduction applies a fixed default packing precision for /// spectral data and enforces strict numeric validation. /// template long resolve_BitsPerValueSpectral_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { long bitsPerValue; if (has(par, "bitsPerValue")) { // Retrieve mandatory bitsPerValue from parameter dictionary bitsPerValue = get_or_throw(par, "bitsPerValue"); // Emit OVERRIDE log entry MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`bitsPerValue` overridden by parameter dictionary: value='"; logMsg += std::to_string(bitsPerValue) + "'"; return logMsg; }()); } else { // Resolve bitsPerValue from default mapping bitsPerValue = 16; // Emit DEFAULT log entry MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`bitsPerValue` defaulted to: value='"; logMsg += std::to_string(bitsPerValue) + "'"; return logMsg; }()); } // Validate bits per value if (bitsPerValue < 0 || bitsPerValue > 64) { std::string errMsg = "Invalid `bitsPerValue`: value='"; errMsg += std::to_string(bitsPerValue) + "'"; throw Mars2GribDeductionException(errMsg, Here()); } // Success exit point return bitsPerValue; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `bitsPerValue` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/stream.h0000664000175000017500000000765615232567717024472 0ustar alastairalastair/* * (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 stream.h /// @brief Deduction of the GRIB `stream` identifier. /// /// This header defines the deduction responsible for resolving the /// GRIB `stream` key used to describe the MARS data stream associated /// with the encoded product. /// /// The value is not inferred or transformed and must be provided /// explicitly by the MARS dictionary. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - validate stream values against controlled vocabularies /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries /// /// @section References /// Concept: /// - @ref marsEncoding.h /// /// Related deductions: /// - @ref class.h /// - @ref expver.h /// - @ref type.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 GRIB `stream` key. /// /// This deduction retrieves the value of the MARS key `stream` /// from the input MARS dictionary and exposes it directly /// as the GRIB `stream` identifier. /// /// The value is treated as mandatory and no inference, /// defaulting, or validation is performed. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary; must contain the key `stream` /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return The resolved MARS stream identifier /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - `stream` is missing from the MARS dictionary /// - the value cannot be retrieved as `std::string` /// - any unexpected error occurs during deduction /// /// @note /// This deduction is fully deterministic and does not depend on /// any pre-existing GRIB header state. /// template std::string resolve_Stream_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 stream from Mars dictionary std::string marsStreamVal = get_or_throw(mars, "stream"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`stream` resolved from input dictionaries: value='"; logMsg += marsStreamVal; logMsg += "'"; return logMsg; }()); // Success exit point return marsStreamVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `stream` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/componentIndex.h0000664000175000017500000001443715232567717026164 0ustar alastairalastair/* * (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 componentIndex.h /// @brief Deduction of the model-error realization identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **model-error realization identifier** (`componentIndex`) /// from MARS metadata. /// /// The deduction retrieves the realization identifier explicitly from the /// MARS dictionary and returns it verbatim, without applying inference, /// defaulting, or semantic interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - enforcing deterministic resolution rules /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply heuristic or data-driven inference /// - do NOT validate against GRIB code tables unless explicitly required /// /// 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 directly from input dictionaries /// /// @section References /// Concept: /// - @ref modelErrorEncoding.h /// /// Related deductions: /// - @ref numberOfComponents.h /// - @ref modelErrorType.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the model-error realization identifier. /// /// @section Deduction contract /// - Reads: `mars["type"]`, `mars["number"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the model-error realization identifier from /// the MARS dictionary. For requests with `type=eme`, the MARS key /// `number` identifies the realization within the model-error ensemble /// (not an ensemble-forecast member). /// /// The value is treated as mandatory and is returned verbatim as a /// numeric identifier. No inference, defaulting, or validation against /// GRIB code tables is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide the key `number`. /// /// @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 realization identifier is retrieved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The model-error realization identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - the key `type` is missing or not equal to `"eme"` (defence-in-depth: /// `componentIndex` is only meaningful for model-error products), /// - the key `number` is missing, cannot be converted to `long`, /// - or any unexpected error occurs during deduction. /// /// @note /// This deduction assumes that the realization identifier is explicitly /// provided by the MARS dictionary and does not attempt any semantic /// interpretation or consistency checking. /// template long resolve_ComponentIndex_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 { // Defence-in-depth: componentIndex is only meaningful for type=eme // (model-error products). Resolving it for any other request type // indicates a serious upstream contract violation (wrong recipe, // matcher bypass, etc.) and must be surfaced as a hard failure // with an unambiguous diagnostic. const std::string typeVal = get_or_throw(mars, "type"); if (typeVal != "eme" && typeVal != "me") { throw Mars2GribDeductionException( std::string("`componentIndex` requested for a non-`eme` or non-`me` request: " "`mars[\"type\"]` is `") + typeVal + "` but only `eme` or `me` is supported. This is a serious upstream " "contract violation: the model-error deduction was reached " "for a request that is not a model-error product. Check " "recipe selection and matcher dispatch.", Here()); } // Warning due to the hack if (typeVal == "me") { eckit::Log::warning() << "MARS `type` value `me` is treated as a synonym for `eme` to accommodate legacy requests. " << "This is a temporary compatibility hack and support for `me` will be removed in the future. " << std::endl; } // Retrieve mandatory MARS number (model-error realization id) long componentIndex = get_or_throw(mars, "number"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`componentIndex` resolved from input dictionaries: value="; logMsg += std::to_string(componentIndex); return logMsg; }()); // Success exit point return componentIndex; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `componentIndex` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/chemId.h0000664000175000017500000001106315232567717024353 0ustar alastairalastair/* * (C) Copyright 2026- 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 chemId.h /// @brief Deduction of the chemical identifier for composition encoding. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **chemical identifier** from MARS metadata. /// /// The deduction retrieves the identifier directly from the MARS /// dictionary and exposes it to the encoding layer without /// transformation or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, defaulting, or consistency checks /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref compositionEncoding.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 chemical identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["chem"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the chemical identifier by retrieving /// the mandatory MARS key `chem` and returning its value as a `long`. /// /// No semantic interpretation, normalization, or validation is performed /// beyond basic type conversion. The meaning of the chemical identifier is /// defined by upstream metadata conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `chem` /// and conversion to `long`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the chemical identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved chemical identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `chem` is missing, cannot be converted to `long`, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult chemical metadata or GRIB tables. /// template long resolve_ChemId_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 chemical identifier long chemId = get_or_throw(mars, "chem"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`chemId` resolved from input dictionaries: value='"; logMsg += std::to_string(chemId); logMsg += "'"; return logMsg; }()); // Success exit point return chemId; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `chemId` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/periodItMax.h0000664000175000017500000001122415232567717025406 0ustar alastairalastair/* * (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 periodItMax.h /// @brief Deduction of the maximum wave period index (`iTmax`). /// /// This header defines deduction utilities used by the mars2grib backend /// to retrieve the **maximum wave period index** (`iTmax`) from input /// dictionaries. /// /// The deduction treats `iTmax` as an optional parameter: /// - if present in the parameter dictionary, the value is returned /// - if absent, no default is applied and an empty optional is returned /// /// No semantic validation or consistency checking is performed at this /// level. /// /// Error handling follows a strict fail-fast strategy: /// - unexpected access errors 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 presence or absence resolved from input dictionaries /// /// @section References /// Concept: /// - @ref waveEncoding.h /// /// Related deductions: /// - @ref periodItMin.h /// - @ref waveFrequencyGrid.h /// - @ref waveFrequencyNumber.h /// - @ref waveDirectionGrid.h /// - @ref waveDirectionNumber.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #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 optional maximum wave period index (`iTmax`). /// /// @section Deduction contract /// - Reads: `par["iTmax"]` (optional) /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws on unexpected errors /// /// This deduction retrieves the optional wave period index `iTmax` /// from the parameter dictionary. /// /// If the key is present, the value is returned wrapped in a /// `std::optional`. If the key is absent, an empty optional is returned. /// No defaulting or inference is applied. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused). /// /// @tparam ParDict_t /// Type of the parameter dictionary. May contain `iTmax`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary from which `iTmax` may be retrieved. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// An optional containing `iTmax` if present; otherwise an empty /// optional. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If any unexpected error occurs during dictionary access. /// /// @note /// This deduction performs no semantic validation of the retrieved /// value. /// template std::optional resolve_PeriodItMax_opt(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Retrieve optional maximum wave period index from parameter dictionary std::optional itMaxOpt = get_opt(par, "iTmax"); if (itMaxOpt.has_value()) { // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`iTmax` resolved from input dictionaries: value='"; logMsg += std::to_string(itMaxOpt.value()); logMsg += "'"; return logMsg; }()); } else { // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`iTmax` resolved from input dictionaries: value not present"; return logMsg; }()); } // Success exit point return itMaxOpt; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `iTmax` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/satelliteSeries.h0000664000175000017500000001025415232567717026324 0ustar alastairalastair/* * (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 satelliteSeries.h /// @brief Deduction of the GRIB `satelliteSeries` identifier. /// /// This header defines the deduction responsible for resolving the /// GRIB `satelliteSeries` key used in satellite-based products. /// /// The value is not inferable from MARS metadata and must be provided /// explicitly via the parameter dictionary. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - validate against GRIB code tables /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries /// /// @section References /// Concept: /// - @ref satelliteEncoding.h /// /// Related deductions: /// - @ref satelliteNumber.h /// - @ref instrumentType.h /// - @ref channel.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 GRIB `satelliteSeries` identifier. /// /// @section Deduction contract /// - Reads: `par["satelliteSeries"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the mandatory `satelliteSeries` entry from /// the parameter dictionary and returns it verbatim. /// /// No defaulting, inference, or semantic validation is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused). /// /// @tparam ParDict_t /// Type of the parameter dictionary. Must provide `satelliteSeries`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary providing the satellite series identifier. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// Satellite series identifier to be encoded in the GRIB message. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `satelliteSeries` is missing, cannot be retrieved as a /// `long`, or if any unexpected error occurs. /// /// @note /// This deduction is deterministic and does not depend on any /// pre-existing GRIB header state. /// template long resolve_SatelliteSeries_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 satellite series identifier from parameter dictionary long marsSatelliteSeriesVal = get_or_throw(par, "satelliteSeries"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`satelliteSeries` resolved from parameter dictionary: value='"; logMsg += std::to_string(marsSatelliteSeriesVal); logMsg += "'"; return logMsg; }()); // Success exit point return marsSatelliteSeriesVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `satelliteSeries` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/localTablesVersion.h0000664000175000017500000000717615232567717026767 0ustar alastairalastair/* * (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 localTablesVersion.h /// @brief Deduction of the GRIB Local Tables Version Number. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **GRIB Local Tables Version Number** /// (`localTablesVersionNumber`). /// /// At present, the deduction returns a fixed value indicating that /// **no local GRIB tables are in use** and that only standard GRIB /// tables apply. /// /// This deduction exists to provide a single, explicit control point /// for future support of local GRIB tables. /// /// Deductions are responsible for: /// - providing deterministic values for encoding logic /// - centralizing policy decisions related to GRIB metadata /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference or data-driven logic unless explicitly required /// /// Error handling: /// - this deduction is total and cannot fail /// /// Logging follows the mars2grib deduction policy: /// - RESOLVE: value derived via deduction logic (including fixed defaults) /// /// @section References /// Concept: /// - @ref identificationEncoding.h /// /// Related deductions: /// - @ref centre.h /// - @ref subCentre.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 GRIB Local Tables Version Number. /// /// @section Deduction contract /// - Reads: none /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: none /// /// This deduction resolves the GRIB /// `localTablesVersionNumber`. /// /// The value is currently **fixed to `0`**, indicating that /// no local GRIB tables are in use and that only standard /// GRIB tables apply. /// /// This function serves as the single authoritative location /// for future logic related to local GRIB tables. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused). /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused). /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The local tables version number. /// Currently always returns `0`. /// /// @note /// A return value of `0` explicitly signals that /// no local GRIB tables are active. /// template long resolve_LocalTablesVersion_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { // Defaulting local Tables version to 0 long localTablesVersion = 0L; // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`localTablesVersionNumber` resolved from input dictionaries: value='"; logMsg += std::to_string(localTablesVersion) + "'"; return logMsg; }()); // Success exit point return localTablesVersion; }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/dataset.h0000664000175000017500000001127715232567717024616 0ustar alastairalastair/* * (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 dataset.h /// @brief Deduction of the MARS `dataset` identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **dataset identifier** from MARS metadata. /// /// The deduction retrieves the dataset identifier directly from the /// MARS dictionary and exposes it to the encoding layer without /// transformation or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, normalization, or defaulting /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref destineEncoding.h /// /// Related deductions: /// - @ref activity.h /// - @ref experiment.h /// - @ref generation.h /// - @ref model.h /// - @ref realization.h /// - @ref resolution.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 dataset identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["dataset"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the dataset identifier by retrieving the /// mandatory MARS key `dataset` and returning its value as a /// `std::string`. /// /// No semantic interpretation, normalization, or validation is applied. /// The meaning and allowed values of the dataset identifier are defined /// by upstream MARS conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `dataset` /// and conversion to `std::string`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the dataset identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved dataset identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `dataset` is missing, cannot be retrieved as a string, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult dataset registries or GRIB tables. /// template std::string resolve_Dataset_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 dataset std::string marsDatasetVal = get_or_throw(mars, "dataset"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`dataset` resolved from input dictionaries: value='" + marsDatasetVal + "'"; return logMsg; }()); // Success exit point return marsDatasetVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `dataset` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/laplacianOperator.h0000664000175000017500000001130615232567717026622 0ustar alastairalastair/* * (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 laplacianOperator.h /// @brief Deduction of the Laplacian operator coefficient. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **Laplacian operator coefficient** from the parameter /// dictionary. /// /// The deduction retrieves the coefficient explicitly from user-provided /// parameters and exposes it to the encoding layer without transformation /// or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference or defaulting /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref packingEncoding.h /// /// Related deductions: /// - @ref bitsPerValue.h /// - @ref subSetTrunc.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 Laplacian operator coefficient from input dictionaries. /// /// @section Deduction contract /// - Reads: `par["laplacianOperator"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the Laplacian operator coefficient by retrieving /// the mandatory parameter dictionary key `laplacianOperator` and returning /// its value as a `double`. /// /// The value is taken verbatim from the parameter dictionary and overrides /// any implicit or default behavior. No validation beyond type conversion /// is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused by this deduction). /// /// @tparam ParDict_t /// Type of the parameter dictionary. Must support keyed access to /// `laplacianOperator` and conversion to `double`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary from which the Laplacian operator coefficient /// is resolved. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved Laplacian operator coefficient. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `laplacianOperator` is missing, cannot be converted to /// `double`, or if any unexpected error occurs during deduction. /// template double resolve_LaplacianOperator_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 Laplacian operator coefficient double laplacianOperator = get_or_throw(par, "laplacianOperator"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`laplacianOperator` resolved from input dictionaries: value='"; logMsg += std::to_string(laplacianOperator); logMsg += "'"; return logMsg; }()); // Success exit point return laplacianOperator; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `laplacianOperator` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/waveDirectionGrid.h0000664000175000017500000003452515232567717026603 0ustar alastairalastair/* * (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 waveDirectionGrid.h /// @brief Deduction of the GRIB wave direction grid. /// /// This header defines the deduction responsible for resolving the /// wave direction grid used in spectral wave products. /// /// The deduction supports two equivalent input representations: /// - an explicit vector of wave directions (in radians) /// - a reconstruction from the number of wave directions /// /// The resulting grid is converted into a scaled integer /// representation suitable for GRIB encoding. /// /// Deductions: /// - extract values from input dictionaries /// - reconstruct physical wave directions deterministically /// - apply logarithmic scaling /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing wave direction metadata /// - apply implicit defaults beyond documented rules /// - validate physical consistency of wave directions /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: wave direction grid obtained or reconstructed from input dictionaries /// /// @section References /// Concept: /// - @ref waveEncoding.h /// /// Related deductions: /// - @ref periodItMin.h /// - @ref periodItMax.h /// - @ref waveDirectionNumber.h /// - @ref waveFrequencyGrid.h /// - @ref waveFrequencyNumber.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include #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 { namespace math { constexpr long double pi = 3.141592653589793238462643383279502884L; constexpr long double deg2rad = pi / 180.0L; constexpr long double rad2deg = 180.0L / pi; } // namespace math /// /// @brief Metadata and scaled representation of a wave direction grid. /// /// This structure describes a discretized wave direction grid together with /// its scaled integer representation. It is intended for use in contexts where /// wave propagation directions must be stored, transmitted, or encoded as /// integers (e.g. GRIB encoding), while preserving a clear mapping to physical /// directional angles expressed in radians. /// /// The scaling convention is logarithmic and based on powers of ten: /// - `scaleFactorDirections` represents the base-10 logarithm of the real /// scaling factor applied to the physical direction values converted to degrees. /// - For example, a value of `scaleFactorDirections = 2` corresponds to a /// real scaling factor of \f$10^2\f$. /// /// Scaled integer values are obtained as: /// \f[ /// \text{scaledValue}_i = /// \mathrm{round}\!\left( \theta_i \times 10^{\text{scaleFactorDirections}} \times \text{rad2deg} \right) /// \f] /// where \f$\theta_i\f$ is the physical wave direction expressed in radians /// and \f$\text{rad2deg} = 180/\pi\f$ converts radians to degrees. /// /// The inverse operation, used to reconstruct physical directions, is: /// \f[ /// \theta_i = /// \frac{\text{scaledValue}_i}{10^{\text{scaleFactorDirections}} \cdot \text{rad2deg}} /// \f] /// /// This structure is a plain data container and does not enforce internal /// consistency between the number of directions and the size of the associated /// value vector. Such validation is expected to be performed by the caller or /// by higher-level deduction logic. /// /// @note /// The directional angles are assumed to span the interval \f$[0, 2\pi)\f$ /// and to follow the discretization conventions of spectral wave models. /// struct WaveDirectionGrid { /// /// @brief Number of discrete wave directions in the grid. /// long numDirections; /// /// @brief Base-10 logarithm of the real direction scaling factor. /// /// This value defines the scaling applied to physical wave direction /// values after conversion to degrees. For example: /// - `scaleFactorDirections = 2` implies a real scaling factor of /// \f$10^2\f$. /// long scaleFactorDirections; /// /// @brief Scaled integer representation of wave directions. /// /// Each element corresponds to a wave direction encoded as an integer, /// obtained by converting the physical direction from radians to degrees, /// then scaling by \f$10^{\text{scaleFactorDirections}}\f$ and rounding. std::vector scaledValuesDirections; }; namespace wave_direction_detail { /// /// @brief Compute a uniformly discretized wave direction grid. /// /// This function computes the angular discretization of wave propagation /// directions over the interval \f$[0, 2\pi)\f$ using a midpoint rule. /// The full circle is divided into `numberOfWaveDirections` equal angular /// sectors, and each returned direction corresponds to the center of a sector. /// /// The discretization follows: /// \f[ /// \Delta\theta = \frac{2\pi}{N}, \qquad /// \theta_k = k\,\Delta\theta + \tfrac{1}{2}\Delta\theta, /// \quad k = 0,\dots,N-1 /// \f] /// /// This implementation is a direct C++ translation of the directional /// discretization logic used in the ECMWF ECWAM wave model, originally /// implemented in Fortran in: /// - `ecwam/src/ecwam/mfredir.F90` /// /// (see section "2. COMPUTATION OF DIRECTIONS, BANDWIDTH, SIN AND COS", /// around line ~100 in the original source), /// from the ECMWF ECWAM repository: /// - https://github.com/ecmwf-ifs/ecwam.git /// /// The numerical behavior of the original Fortran implementation is preserved /// exactly, with the exception that only the directional angles are returned. /// The computation of sine and cosine terms is intentionally omitted, as these /// can be derived trivially by the caller if needed. /// /// @param[in] numberOfWaveDirections /// Total number of wave propagation directions used to discretize the /// directional space. /// /// @return /// Vector of size `numberOfWaveDirections` containing the wave directions /// in radians, uniformly distributed over \f$(0, 2\pi)\f$. /// /// @note /// The returned angles correspond to the centers of the angular bins /// (midpoint discretization), which is standard in spectral wave models. /// /// @note /// This function assumes a full directional coverage of \f$2\pi\f$ and /// does not support partial angular sectors. /// inline std::vector compute_WaveDirectionGrid(long numberOfWaveDirections) { std::vector th; th.resize(numberOfWaveDirections); const double delth = 2.0 * M_PI / static_cast(numberOfWaveDirections); for (long k = 0; k < numberOfWaveDirections; ++k) { th[k] = static_cast(k) * delth + 0.5 * delth; } return th; } /// /// @brief Construct a scaled wave direction grid from physical direction values. /// /// This function builds a `WaveDirectionGrid` structure from a vector of /// physical wave propagation directions expressed in radians. The physical /// direction values are converted to an integer representation using a /// base-10 logarithmic scaling factor after conversion to degrees. /// /// The scaling convention is defined as follows: /// - `scaleFactorOfWaveDirections` represents the base-10 logarithm of the /// real scaling factor applied to the physical direction values. /// - A value of `scaleFactorOfWaveDirections = 6` corresponds to a real /// scaling factor of \f$10^6\f$. /// /// Each scaled integer value is computed as: /// \f[ /// \text{scaledValue}_i = /// \mathrm{round}\!\left( \theta_i \times 10^{\text{scaleFactorOfWaveDirections}} \times rad2deg\right) /// \f] /// where \f$\theta_i\f$ is the physical wave direction in radians. /// /// The resulting structure stores: /// - the total number of wave directions, /// - the logarithmic scaling factor, /// - the vector of scaled integer direction values. /// /// This representation is typically used for serialization or encoding /// purposes (e.g. GRIB), where integer storage with a known scaling factor /// is required. /// /// @param[in] waveDirectionsInRadians /// Vector of physical wave propagation directions expressed in radians. /// /// @param[in] scaleFactorOfWaveDirections /// Base-10 logarithm of the scaling factor applied to the direction values /// prior to integer encoding. /// /// @return /// A `WaveDirectionGrid` structure containing the scaled integer /// representation of the input direction grid. /// /// @note /// No validation is performed on the input direction values (e.g. range /// checks within \f$[0, 2\pi)\f$) or on the scaling factor. The caller is /// responsible for ensuring that the provided values are physically /// meaningful and that the scaled values fit within the range of the /// target integer type. /// inline WaveDirectionGrid compute_WaveScaledDirectionGrid(const std::vector& waveDirectionsInRadians, long scaleFactorOfWaveDirections) { WaveDirectionGrid out{}; out.numDirections = static_cast(waveDirectionsInRadians.size()); out.scaleFactorDirections = scaleFactorOfWaveDirections; out.scaledValuesDirections.resize(waveDirectionsInRadians.size()); for (long i = 0; i < static_cast(waveDirectionsInRadians.size()); ++i) { out.scaledValuesDirections[i] = static_cast( std::round(waveDirectionsInRadians[i] * std::pow(10.0, scaleFactorOfWaveDirections) * math::rad2deg)); } return out; } } // namespace wave_direction_detail /// /// @brief Resolve the wave direction grid. /// /// This deduction resolves the wave direction grid using the parameter /// dictionary (`par`) and returns a scaled integer representation suitable /// for GRIB encoding. /// /// Resolution follows a strict precedence order: /// /// 1. **Explicit wave directions** /// If `par::waveDirections` is present, it is interpreted as a vector /// of physical wave directions expressed in radians. /// /// 2. **Reconstruction from direction count** /// If `par::numberOfWaveDirections` is present, the wave direction /// grid is reconstructed deterministically using a uniform midpoint /// discretization over the interval \f$[0, 2\pi)\f$. /// /// The scaling factor applied to wave directions is taken from /// `par::scaleFactorOfWaveDirections` and defaults explicitly to `2` /// if not provided. /// /// @tparam OptDict_t Type of the options dictionary (unused) /// @tparam MarsDict_t Type of the MARS dictionary (unused) /// @tparam ParDict_t Type of the parameter dictionary /// /// @param[in] opt Options dictionary (unused) /// @param[in] mars MARS dictionary (unused) /// @param[in] par Parameter dictionary providing wave direction metadata /// /// @return A `WaveDirectionGrid` containing: /// - number of directions /// - scaling factor /// - scaled integer direction values /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - neither `waveDirections` nor `numberOfWaveDirections` is present /// - dictionary access fails /// - any unexpected error occurs during deduction /// template WaveDirectionGrid resolve_WaveDirectionGrid_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { WaveDirectionGrid out{}; std::vector waveDirectionsInRadians; // Retrieve optional scale factor from parameter dictionary long scaleFactorOfWaveDirections = get_opt(par, "scaleFactorOfWaveDirections").value_or(2L); // Check presence of explicit wave directions bool hasWaveDirections = has(par, "waveDirections"); // Check presence of number of wave directions bool hasNumberOfWaveDirections = has(par, "numberOfWaveDirections"); if (hasWaveDirections) { // Retrieve mandatory wave directions from parameter dictionary waveDirectionsInRadians = get_or_throw>(par, "waveDirections"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`waveDirectionGrid` resolved from input dictionaries"; return logMsg; }()); } else if (hasNumberOfWaveDirections) { // Retrieve mandatory number of wave directions from parameter dictionary long numberOfWaveDirections = get_or_throw(par, "numberOfWaveDirections"); waveDirectionsInRadians = wave_direction_detail::compute_WaveDirectionGrid(static_cast(numberOfWaveDirections)); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`waveDirectionGrid` reconstructed from input dictionaries with parameters={"; logMsg += "numberOfWaveDirections=" + std::to_string(numberOfWaveDirections) + "}"; return logMsg; }()); } else { /// @todo Need a defaulting strategy for `numberOfWaveDirections` throw Mars2GribDeductionException("Default value NOT IMPLEMENTED for `numberOfWaveDirections`", Here()); } // Build the scaled direction grid out = wave_direction_detail::compute_WaveScaledDirectionGrid(waveDirectionsInRadians, scaleFactorOfWaveDirections); // Success exit point return out; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `waveDirectionGrid` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); } } // namespace metkit::mars2grib::backend::deductionsmetkit-1.19.2/src/metkit/mars2grib/backend/deductions/numberOfFrequencies.h0000664000175000017500000000644715232567717027143 0ustar alastairalastair/* * (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 numberOfFrequencies.h /// @brief Deduction of the GRIB `numberOfFrequencies` key. /// /// Resolve the wave spectrum discretization size (`numberOfFrequencies`). /// /// @section Deduction contract /// - Reads: `par["numberOfFrequencies"]` (optional) /// - Writes: none /// - Side effects: logging (OVERRIDE or DEFAULT) /// - Failure mode: throws on unexpected errors /// /// If the parameter dictionary provides `numberOfFrequencies`, the value is /// used verbatim. Otherwise a documented default (`54`) is applied. /// /// @ingroup mars2grib_backend_deductions /// #pragma once #include #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 `numberOfFrequencies` key. /// /// @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; may contain `numberOfFrequencies`. /// @param[in] opt Options dictionary (unused). /// /// @return Resolved number of frequencies. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If an unexpected error occurs while accessing the parameter dictionary. /// template long resolve_NumberOfFrequencies_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { if (has(par, "numberOfFrequencies")) { long numberOfFrequencies = get_or_throw(par, "numberOfFrequencies"); MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`numberOfFrequencies` resolved from input dictionaries: value="; logMsg += std::to_string(numberOfFrequencies); return logMsg; }()); return numberOfFrequencies; } else { long numberOfFrequencies = 54; MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`numberOfFrequencies` resolved from input dictionaries: value="; logMsg += std::to_string(numberOfFrequencies); return logMsg; }()); return numberOfFrequencies; } } catch (...) { std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `numberOfFrequencies` from input dictionaries", Here())); }; mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/tablesVersion.h0000664000175000017500000001372715232567717026013 0ustar alastairalastair/* * (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 tablesVersion.h /// @brief Deduction of the GRIB tables version identifiers. /// /// This header defines the deductions responsible for resolving the /// GRIB tables version identifiers used during GRIB encoding. /// /// Two resolution strategies are provided: /// - automatic resolution of the latest tables version supported by ecCodes /// - explicit user override via the parameter dictionary /// /// Deductions: /// - extract values from input dictionaries or runtime environment /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply silent fallbacks /// - validate semantic correctness against GRIB specifications /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries or runtime /// - OVERRIDE: value explicitly provided by the user /// /// @section References /// Concept: /// - @ref tablesEncoding.h /// /// Related deductions: /// - @ref localTablesVersion.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include // Core deduction includes #include "metkit/codes/api/CodesAPI.h" #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 latest GRIB tables version supported by ecCodes. /// /// This deduction resolves the GRIB `tablesVersionLatest` identifier by /// querying the ecCodes runtime environment. /// /// Resolution rules: /// - the value is obtained directly from an ecCodes GRIB2 sample /// - no MARS or parameter input is used /// - no defaulting or inference is applied /// /// @tparam MarsDict_t Type of the MARS dictionary (unused) /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary (unused) /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return The latest GRIB tables version supported by ecCodes /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the value cannot be resolved from the runtime environment /// /// @note /// The returned value is deterministic for a given ecCodes installation. /// template long resolve_TablesVersionLatest_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Retrieve mandatory tablesVersionLatest from ecCodes runtime long tablesVersionLatestVal = metkit::codes::codesHandleFromSample("GRIB2")->getLong("tablesVersionLatest"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`tablesVersionLatest` resolved from ecCodes runtime: value='"; logMsg += std::to_string(tablesVersionLatestVal); logMsg += "'"; return logMsg; }()); // Success exit point return tablesVersionLatestVal; } catch (...) { std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `tablesVersionLatest` from ecCodes runtime", Here())); } }; /// /// @brief Resolve a user-defined GRIB tables version. /// /// This deduction resolves the GRIB `tablesVersion` identifier from the /// parameter dictionary. /// /// Resolution rules: /// - `par::tablesVersion` MUST be present /// - the value is treated as an explicit user override /// - no validation against ecCodes capabilities is performed /// /// @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; must contain `tablesVersion` /// @param[in] opt Options dictionary (unused) /// /// @return The GRIB tables version explicitly requested by the user /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the value cannot be resolved /// /// @note /// Callers requiring strict reproducibility must ensure compatibility /// with the ecCodes runtime environment. /// template long resolve_TablesVersionCustom_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 tablesVersion from parameter dictionary long tablesVersionCustomVal = get_or_throw(par, "tablesVersion"); // Emit OVERRIDE log entry MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`tablesVersion` overridden from parameter dictionary: value='"; logMsg += std::to_string(tablesVersionCustomVal); logMsg += "'"; return logMsg; }()); // Success exit point return tablesVersionCustomVal; } catch (...) { std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `tablesVersion` from parameter dictionary", Here())); } }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/iterationNumber.h0000664000175000017500000001030315232567717026325 0ustar alastairalastair/* * (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 metkit-1.19.2/src/metkit/mars2grib/backend/deductions/resolution.h0000664000175000017500000001036615232567717025372 0ustar alastairalastair/* * (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 resolution.h /// @brief Deduction of the MARS spatial resolution identifier. /// /// This header defines the deduction responsible for resolving the /// spatial resolution identifier from the MARS dictionary. /// /// The resolved value is passed unchanged to downstream concept /// encoders and is not interpreted or validated at this stage. /// /// Deductions: /// - extract values from MARS, parameter, or option dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - perform semantic interpretation /// - apply defaults or inference /// - validate GRIB table correctness /// /// Error handling follows a fail-fast strategy with nested exception /// propagation to preserve diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained through deduction logic from input dictionaries /// /// @section References /// Concept: /// - @ref destineEncoding.h /// /// Related deductions: /// - @ref activity.h /// - @ref experiment.h /// - @ref generation.h /// - @ref model.h /// - @ref realization.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 spatial resolution identifier from the MARS dictionary. /// /// @section Deduction contract /// - Reads: `mars["resolution"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the mandatory `resolution` entry from the /// MARS dictionary and returns it verbatim. /// /// No inference, defaulting, normalization, or validation of the /// resolution semantics is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must provide the key `resolution`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused). /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary providing the spatial resolution identifier. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// Spatial resolution identifier as provided by the MARS dictionary. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `resolution` is missing, cannot be retrieved as a string, /// or if any unexpected error occurs. /// /// @note /// This deduction is deterministic and does not depend on any /// pre-existing GRIB header state. /// template std::string resolve_Resolution_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 resolution identifier from MARS dictionary std::string marsResolutionVal = get_or_throw(mars, "resolution"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`resolution` resolved from MARS dictionary: value='"; logMsg += marsResolutionVal; logMsg += "'"; return logMsg; }()); // Success exit point return marsResolutionVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `resolution` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/pvArray.h0000664000175000017500000005513015232567717024611 0ustar alastairalastair/* * (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 pvArray.h /// @brief Deduction of the GRIB vertical coordinate PV array (`pv`). /// /// This file defines the deduction responsible for resolving the /// **GRIB PV array** (`pv`), which encodes the vertical coordinate /// transformation parameters used by hybrid vertical level definitions. /// /// The deduction supports two mutually exclusive input mechanisms: /// /// 1. **Explicit override** /// - The full PV array is provided directly via the parameter /// dictionary (`par["pv"]`). /// /// 2. **Table-based construction** /// - The PV array is constructed from a declared size /// (`par["pvSize"]`) using a predefined lookup table. /// /// 3. **Defaulting** /// - If neither `par["pv"]` nor `par["pvSize"]` is provided, a default /// PV array is constructed using a predefined lookup with a fixed /// default size (currently 137). /// /// ### Responsibilities /// Deductions in this file are responsible for: /// - extracting PV-related metadata from input dictionaries /// - applying deterministic construction rules /// - validating presence and structural consistency /// - returning a fully constructed PV array to the encoder /// /// Deductions: /// - do NOT infer PV values from GRIB templates /// - do NOT modify or validate physical meaning of PV coefficients /// - do NOT depend on pre-existing GRIB header state /// /// Error handling follows a strict fail-fast policy: /// - missing or ambiguous inputs cause immediate failure /// - errors are reported using Mars2Grib deduction exceptions /// - original errors are preserved via nested exception propagation /// /// Logging follows the mars2grib deduction policy: /// - OVERRIDE: PV array explicitly provided by the parameter dictionary /// - RESOLVE: PV array constructed via deterministic lookup /// - DEFAULT: PV array defaulted to a predefined size due to missing input /// /// @section References /// Concept: /// - @ref levelEncoding.h /// /// Related deductions: /// - @ref level.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #include #include #include #include #include #include #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 { namespace pv_detail { /// /// @brief Fixed-size hexadecimal representation of an IEEE754 double value. /// /// This alias represents the raw byte layout of a double-precision floating-point /// value encoded as 8 bytes. It is used to store predefined floating-point /// constants in a portable, byte-exact form, independent of the host endianness. /// /// The interpretation of the byte order is handled explicitly by decoding /// utilities elsewhere in the code. /// using HexDouble = std::array; namespace data { // This is pure data hence I just put the include here #include "metkit/mars2grib/backend/deductions/detail/pv_137_be.h" } // namespace data /// /// @brief Entry describing a predefined PV coefficient table. /// /// This structure defines the metadata and storage required to associate a /// logical lookup key with a statically defined array of PV coefficients. /// /// Members: /// - `key` : Logical identifier used to select the PV table (e.g. value /// provided by the caller). This key is not required to match the /// number of coefficients stored. /// - `size` : Number of PV coefficients available in the referenced data array. /// - `data` : Pointer to the first element of a statically allocated array of /// `HexDouble` values encoding the PV coefficients. /// /// The pointer stored in `data` is non-owning and is expected to reference /// memory with static storage duration. /// struct PvEntry { long key; long size; const HexDouble* data; }; /// /// @brief Compile-time lookup table mapping PV logical keys to predefined PV data arrays. /// /// This table defines the association between a logical PV lookup key and a /// statically defined array of PV coefficients encoded as hexadecimal /// IEEE754 double-precision values. /// /// Each entry in the table specifies: /// - a logical lookup key (`PvEntry::key`) used to identify the PV array, /// - the number of PV coefficients stored in the array (`PvEntry::size`), /// - a pointer to the first element of the corresponding static data array. /// /// The data arrays referenced by this table are expected to: /// - have static storage duration, /// - contain exactly `size` elements, /// - store each coefficient as an 8-byte IEEE754 double in big-endian byte order. /// /// This table is defined as `constexpr` to ensure zero runtime initialization /// overhead and to guarantee that the mapping is immutable. /// /// @todo [owner: mival,dgov][scope: deduction][reason: completeness][prio: low] /// - Extend this table with additional PV arrays as required by the /// application domain. /// /// @note /// The logical lookup key (`key`) is not required to match the number of /// coefficients (`size`). This allows decoupling of external identifiers /// from the physical layout of the PV data. /// /// @note /// The lifetime of the referenced data arrays is guaranteed for the entire /// program execution, and the pointers stored in this table are non-owning. /// /// @note /// Data that are currently injected in the table are for demonstration and /// testing purposes only. /// constexpr std::array pv_tables = {{{137, 1002, data::pv_137_1002_be.data()}}}; /// /// @brief Decode a double value from an 8-byte sequence in native byte order. /// /// This helper function converts a sequence of 8 bytes into a native /// double-precision floating-point value by copying the raw byte representation /// directly into a `double` object. /// /// The conversion is performed using `std::memcpy` to avoid strict-aliasing /// violations and undefined behavior associated with pointer reinterpreting. /// /// @param[in] p /// Pointer to an array of 8 bytes representing an IEEE754 double-precision /// floating-point value in the host native byte order. /// /// @return /// The decoded double-precision floating-point value. /// /// @note /// This function assumes: /// - `sizeof(double) == 8` /// - IEEE754 binary64 floating-point format /// /// @note /// The caller is responsible for ensuring that `p` points to at least /// `sizeof(double)` valid bytes. /// inline double bytesToDouble(const uint8_t* p) { double v; std::memcpy(&v, p, sizeof(double)); return v; } /// /// @brief Decode a double value from an 8-byte sequence with reversed byte order. /// /// This helper function converts an array of 8 bytes into a native /// double-precision floating-point value, first reversing the byte order. /// It is intended to handle conversion from a byte sequence whose endianness /// differs from the host representation. /// /// The input byte sequence is copied into a temporary buffer with its byte /// order reversed, after which it is decoded using `bytesToDouble()`. /// /// @param[in] p /// Pointer to an array of 8 bytes representing an IEEE754 double-precision /// floating-point value in non-native byte order. /// /// @return /// The decoded double-precision floating-point value in native representation. /// /// @note /// This function assumes: /// - `sizeof(double) == 8` /// - IEEE754 binary64 floating-point format /// /// @note /// No validation of the input byte sequence is performed. The caller is /// responsible for ensuring that `p` points to at least 8 valid bytes. /// inline double bytesToDoubleSwapped(const uint8_t* p) { uint8_t tmp[8]; std::reverse_copy(p, p + 8, tmp); return bytesToDouble(tmp); } /// /// @brief Determine the host byte order for double-precision floating-point values. /// /// This function detects the native endianness of the host system by interpreting /// a known IEEE754 double-precision value encoded in big-endian byte order. /// The detection is performed by decoding the sentinel byte sequence both with /// native byte order and with reversed byte order, and comparing the results /// against the known reference value. /// /// The function returns whether the host system uses little-endian byte order /// for `double` values. If neither interpretation matches the reference value, /// the host platform is assumed to be unsupported (e.g. non-IEEE754 or unexpected /// `double` layout), and an exception is thrown. /// /// @return /// `true` if the host uses little-endian representation for `double`, /// `false` if the host uses big-endian representation. /// /// @throws Mars2GribDeductionException /// If the host floating-point representation is incompatible with IEEE754 /// binary64 or cannot be reliably interpreted. /// /// @note /// This function assumes: /// - `sizeof(double) == 8` /// - IEEE754 binary64 floating-point format /// /// @note /// The detection is runtime-based and should typically be executed once and /// cached, as the result is invariant for the lifetime of the process. /// inline bool hostIsLittleEndian_or_throw() { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; constexpr double sentinel = 1.23456789; constexpr std::array sentinel_be = {0x3F, 0xF3, 0xC0, 0xCA, 0x42, 0x83, 0xDE, 0x1B}; const double v0 = bytesToDouble(sentinel_be.data()); if (v0 == sentinel) return false; // host BE const double v1 = bytesToDoubleSwapped(sentinel_be.data()); if (v1 == sentinel) return true; // host LE throw Mars2GribDeductionException("Unsupported floating-point representation (non IEEE754 double?)", Here()); // Remove compiler warning mars2gribUnreachable(); } /// /// @brief Decode a double value from an 8-byte hexadecimal representation, with optional byte swapping. /// /// This helper function converts a fixed-size array of 8 bytes representing an /// IEEE754 double-precision floating-point value into a native `double`. /// The input byte sequence is interpreted either directly or with reversed byte /// order depending on the `swap` flag. /// /// The function is typically used when decoding statically defined, big-endian /// hexadecimal tables on hosts whose native endianness may differ. /// /// @param[in] p /// Array of 8 bytes containing the raw IEEE754 representation of a double value. /// /// @param[in] swap /// If `true`, the byte order of `p` is reversed before decoding. If `false`, /// the byte order is used as-is. /// /// @return /// The decoded double-precision floating-point value in native representation. /// /// @note /// This function assumes: /// - `sizeof(double) == 8` /// - IEEE754 binary64 floating-point format /// /// @note /// No validation of the floating-point representation is performed here; the /// correctness of the input bytes is assumed. /// inline double readDoubleMaybeSwapped(const std::array& p, bool swap) { return swap ? bytesToDoubleSwapped(p.data()) : bytesToDouble(p.data()); } /// /// @brief Lookup and decode a predefined PV coefficient array from its logical size. /// /// This function performs a lookup in a compile-time table of predefined PV /// coefficient arrays using the provided logical key (`pvArraySize`). Each table /// entry maps a logical key to a statically-defined array of IEEE754 double values /// encoded as big-endian hexadecimal bytes. /// /// The function: /// 1. Searches the PV lookup table for an entry whose key matches `pvArraySize`. /// 2. Determines the host endianness at runtime using a sentinel-based check. /// 3. Decodes the corresponding hexadecimal byte arrays into native `double` /// values, applying byte-swapping if required. /// 4. Returns the decoded PV coefficients as a `std::vector`. /// /// If the lookup fails or the host floating-point representation is unsupported, /// a domain-specific exception is thrown. All errors are wrapped using /// `std::throw_with_nested` to preserve the full error context. /// /// @param[in] pvArraySize /// Logical lookup key identifying the PV array to retrieve. This key does not /// necessarily correspond to the number of coefficients stored in the array. /// /// @return /// A vector containing the decoded PV coefficients associated with the given /// lookup key. /// /// @throws Mars2GribDeductionException /// - If no PV array is associated with the provided lookup key. /// - If the host floating-point representation is unsupported (non-IEEE754 or /// incompatible endianness). /// - If any error occurs during lookup or decoding; in this case, the original /// exception is preserved via nested exceptions. /// /// @note /// The PV tables are assumed to be stored in big-endian IEEE754 format. /// Host endianness is detected at runtime and decoding is adapted accordingly. /// /// @note /// This function is intended for use at API or backend boundaries and follows /// a fail-fast strategy with rich error context propagation. /// inline std::vector lookup_PvArrayFromSize_or_throw(long pvArraySize) { using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // 1) Lookup const PvEntry* entry = nullptr; for (const auto& e : pv_tables) { if (e.key == pvArraySize) { entry = &e; break; } } // Not found if (!entry) { std::string errMsg = "No PV array found for size: " + std::to_string(pvArraySize); errMsg += ". Supported sizes is: {137}"; throw Mars2GribDeductionException(errMsg, Here()); } // 2) Sentinel endian detection (need to be done only once, hence `static`) static const bool swap = hostIsLittleEndian_or_throw(); // 3) Decode std::vector out; out.reserve(entry->size); for (long i = 0; i < entry->size; ++i) { out.push_back(readDoubleMaybeSwapped(entry->data[i], swap)); } // RReturn decoded sentinel return out; } catch (...) { std::throw_with_nested(Mars2GribDeductionException("Unable to lookup PV array from size", Here())); } // Remove compiler warning mars2gribUnreachable(); } /// /// @brief Convert a vector of double values into big-endian hexadecimal form. /// /// This function converts each input double into its IEEE754 binary64 /// representation encoded in big-endian byte order, independently of the /// host native endianness. /// /// @param[in] values /// Vector of double-precision floating-point values. /// /// @return /// Vector of HexDouble values encoded in big-endian byte order. /// /// @throws Mars2GribDeductionException /// If the host floating-point representation is unsupported. /// inline std::vector toHexDoubleBE(const std::vector& values) { static_assert(sizeof(double) == 8, "Unsupported double size"); // Determine host endian once static const bool host_is_le = hostIsLittleEndian_or_throw(); std::vector out; out.reserve(values.size()); for (double v : values) { uint8_t raw[8]; std::memcpy(raw, &v, 8); HexDouble h{}; if (host_is_le) { // native LE → write BE for (int i = 0; i < 8; ++i) { h[i] = raw[7 - i]; } } else { // native BE → already BE for (int i = 0; i < 8; ++i) { h[i] = raw[i]; } } out.push_back(h); } return out; } /// /// @brief Generate a C++ include file containing a constexpr HexDouble table. /// /// This function writes a header file defining a `static constexpr` /// `std::array` initialized with the provided hexadecimal data. /// /// The generated file is intended to be included by the PV lookup /// infrastructure and should contain data only (no logic). /// /// @param[in] hex_data /// Vector of HexDouble values to emit. /// /// @param[in] array_name /// Name of the generated C++ array (e.g. "pv_137_be"). /// /// @param[in] filename /// Path to the output include file. /// /// @throws std::runtime_error /// If the output file cannot be created or written. /// inline void writeHexTableInclude(const std::vector& hex_data, const std::string& array_name, const std::string& filename) { std::ofstream os(filename); if (!os) { throw std::runtime_error("Cannot open output file: " + filename); } os << "#pragma once\n\n"; os << "#include \n"; os << "#include \n\n"; os << "using HexDouble = std::array;\n\n"; os << "static constexpr std::array " << array_name << " = {{\n"; os << std::hex << std::setfill('0'); for (const auto& h : hex_data) { os << " {"; for (size_t i = 0; i < 8; ++i) { os << "0x" << std::setw(2) << static_cast(h[i]); if (i != 7) os << ", "; } os << "},\n"; } os << "}};\n"; } } // namespace pv_detail /// /// @brief Resolve the GRIB vertical coordinate PV array (`pv`). /// /// @section Deduction contract /// - Reads: /// - `par["pv"]` (explicit override), OR /// - `par["pvSize"]` (table-based construction) /// - Writes: none /// - Side effects: logging (OVERRIDE, RESOLVE, DEFAULT) /// - Failure mode: throws /// /// This deduction resolves the PV array defining the vertical /// coordinate transformation used for hybrid vertical levels. /// /// Resolution follows a strict precedence order: /// /// 1. **Explicit override** /// If `par["pv"]` is present, the PV array is taken verbatim /// from the parameter dictionary and treated as authoritative. /// /// 2. **Deterministic construction** /// If `par["pv"]` is absent and `par["pvSize"]` is present, /// the PV array is constructed using a predefined lookup /// based solely on the requested size. /// /// 3. **Defaulting** /// If neither `par["pv"]` nor `par["pvSize"]` is provided, a default /// PV array is constructed using a predefined lookup with a fixed /// default size (currently 137). /// /// 4. **Ambiguity error** /// If both `par["pv"]` and `par["pvSize"]` are present, this is treated /// as an error due to conflicting input, and an exception is thrown. /// /// No attempt is made to validate the physical meaning, /// monotonicity, or numerical consistency of the PV values. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused by this deduction). /// /// @tparam ParDict_t /// Type of the parameter dictionary providing `pv` or `pvSize`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary providing PV configuration. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// Fully resolved PV array as a vector of double-precision values. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - the PV array cannot be retrieved or constructed /// - any unexpected error occurs during deduction /// /// @note /// - This deduction is deterministic for a given parameter dictionary. /// - The returned PV array is passed verbatim to GRIB encoding logic. /// template std::vector resolve_PvArray_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Checks for presence of keys bool hasPV = has(par, "pv"); bool hasPVSize = has(par, "pvSize"); std::vector pvArrayVal; if (hasPV && !hasPVSize) { // Get the pv array directly pvArrayVal = get_or_throw>(par, "pv"); // Emit OVERRIDE log entry MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`pvArray` overridden from input dictionaries: size='"; logMsg += std::to_string(pvArrayVal.size()); logMsg += "'"; return logMsg; }()); } else if (!hasPV && hasPVSize) { // Get the pvArray size for lookup long pvArraySize = get_or_throw(par, "pvSize"); // Lookup of the pv array from size pvArrayVal = pv_detail::lookup_PvArrayFromSize_or_throw(pvArraySize); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`pvArray` resolved from input dictionaries: size='"; logMsg += std::to_string(pvArrayVal.size()); logMsg += "' (lookup from `pvSize`=" + std::to_string(pvArraySize) + ")"; return logMsg; }()); } else if (!hasPV && !hasPVSize) { // Get the pvArray size for lookup // Value 137 is the default value in simulations and must always be present in the lookup table long pvArraySize = 137; // Lookup of the pv array from size pvArrayVal = pv_detail::lookup_PvArrayFromSize_or_throw(pvArraySize); // Emit DEFAULT log entry MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`pvArray` defaulted to size='"; logMsg += std::to_string(pvArrayVal.size()); logMsg += "' (lookup from `pvSize`=" + std::to_string(pvArraySize) + ")"; return logMsg; }()); } else { // Both `pv` and `pvSize` are present: this is an error due to ambiguity std::string logMsg = "Ambiguous PV array configuration: both `pv` and `pvSize` are present in the parameter dictionary"; throw Mars2GribDeductionException(logMsg, Here()); } // Exit with success return pvArrayVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `pvArray` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/generatingProcessIdentifier.h0000664000175000017500000001330415232567717030647 0ustar alastairalastair/* * (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 generatingProcessIdentifier.h /// @brief Optional deduction of the GRIB `generatingProcessIdentifier` key. /// /// This header provides an optional passthrough deduction for the GRIB /// `generatingProcessIdentifier` key. /// /// @section Deduction contract /// - Reads: `par["generatingProcessIdentifier"]` (optional) /// - Writes: none /// - Side effects: logging (RESOLVE when present, otherwise DEFAULT-style skip message) /// - Failure mode: returns `std::nullopt` when absent; throws on unexpected errors /// /// @ingroup mars2grib_backend_deductions /// #pragma once #include #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/enableOptions.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Optionally resolve the GRIB `generatingProcessIdentifier` key from parameters. /// /// This deduction provides an **optional passthrough resolution** for the GRIB /// `generatingProcessIdentifier` key. /// /// When present, the value is read **verbatim** from the parameter dictionary /// (`par`) and returned without modification or validation. /// If the key is not present, the function returns `std::nullopt`. /// /// @important /// This function performs **no deduction logic** and **no semantic validation**. /// It exists solely to allow expert or legacy workflows to explicitly inject /// a GRIB `generatingProcessIdentifier` value via the parameter dictionary. /// /// The use of this mechanism is **discouraged** for production workflows, as it /// may lead to inconsistent or non-reproducible GRIB headers if not coordinated /// with the rest of the encoding logic. /// /// @section Semantics /// - Input source: parameter dictionary (`par`) /// - Resolution type: optional passthrough /// - Validation: none /// - Defaulting: none /// /// @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; may contain `generatingProcessIdentifier` /// @param[in] opt Options dictionary (unused) /// /// @return An optional `long`: /// - the value of `generatingProcessIdentifier` if present in `par` /// - `std::nullopt` otherwise /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If any unexpected error occurs while accessing the parameter dictionary. /// /// @warning /// This deduction must **not** be relied upon as the primary mechanism for setting /// `generatingProcessIdentifier`. /// A proper, deterministic deduction based on MARS semantics or encoder policy /// should be preferred whenever possible. /// /// @todo [owner: mds,dgov][scope: deduction][reason: legacy][prio: medium] /// - Need to define a proper table and a proper logic to deduce the `generatingProcessIdentifier` /// - Evaluate whether this passthrough deduction can be removed once all /// generating process identifiers are derived deterministically. /// - Consider replacing this with a validated, table-driven deduction. /// /// template std::optional resolve_GeneratingProcessIdentifier_opt([[maybe_unused]] const MarsDict_t& mars, const ParDict_t& par, [[maybe_unused]] const OptDict_t& opt) { using metkit::mars2grib::utils::dict_traits::get_opt; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // Get generatingProcessIdentifier from mars dictionary std::optional generatingProcessIdentifierVal = get_opt(par, "generatingProcessIdentifier"); if (generatingProcessIdentifierVal.has_value()) { // Logging of the par::generatingProcessIdentifier MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`generatingProcessIdentifier`: mapped from `par::generatingProcessIdentifier`: actual='"; logMsg += std::to_string(generatingProcessIdentifierVal.value()) + "'"; return logMsg; }()); // Return the generatingProcessIdentifier from the parameter dictionary return {generatingProcessIdentifierVal}; } else { // Logging of the par::generatingProcessIdentifier absence MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`generatingProcessIdentifier`: `par::generatingProcessIdentifier` not present, return " "std::nullopt to skip deduction"; return logMsg; }()); // Key not present; return std::nullopt return std::nullopt; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribDeductionException( "Unable to get `generatingProcessIdentifier` from parameter dictionary", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/type.h0000664000175000017500000000735715232567717024156 0ustar alastairalastair/* * (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 type.h /// @brief Deduction of the MARS `type` identifier. /// /// This header defines the deduction responsible for resolving the /// MARS `type` key used to classify the nature of a field /// (e.g. analysis, forecast, ensemble member). /// /// The value is retrieved directly from the MARS dictionary and is /// treated as mandatory. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - validate semantic correctness of the returned value /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries /// /// @section References /// Concept: /// - @ref marsEncoding.h /// /// Related deductions: /// - @ref class.h /// - @ref stream.h /// - @ref expver.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 MARS `type` identifier. /// /// This deduction resolves the MARS `type` key from the MARS dictionary. /// /// Resolution rules: /// - `mars::type` MUST be present /// - the value is retrieved verbatim as a string /// - no inference, defaulting, or validation is applied /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary; must contain `type` /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return The resolved MARS `type` identifier /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the value cannot be resolved /// /// @note /// The returned value is not interpreted by this deduction and is /// assumed to follow MARS conventions. /// template std::string resolve_Type_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 type from MARS dictionary std::string marsTypeVal = get_or_throw(mars, "type"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`type` resolved from input dictionaries: value='"; logMsg += marsTypeVal; logMsg += "'"; return logMsg; }()); // Success exit point return marsTypeVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribDeductionException("Failed to resolve `type` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/scaleFactorOfCentralWaveNumber.h0000664000175000017500000001050615232567717031203 0ustar alastairalastair/* * (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 scaleFactorOfCentralWaveNumber.h /// @brief Deduction of the GRIB `scaleFactorOfCentralWaveNumber` key. /// /// This file defines the deduction responsible for resolving the GRIB /// `scaleFactorOfCentralWaveNumber` key used in satellite-based products. /// /// Together with `scaledValueOfCentralWaveNumber`, this value is used /// to encode the central wave number according to the GRIB specification. /// /// The deduction: /// - reads exclusively from the parameter dictionary /// - performs no inference or defaulting /// - emits structured diagnostic logging /// /// Error handling follows a fail-fast strategy with nested exception /// propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries /// /// @section References /// Concept: /// - @ref satelliteEncoding.h /// /// Related deductions: /// - @ref scaledValueOfCentralWaveNumber.h /// - @ref channel.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 GRIB `scaleFactorOfCentralWaveNumber` identifier. /// /// @section Deduction contract /// - Reads: `par["scaleFactorOfCentralWaveNumber"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the scale factor applied to the central wave /// number in satellite products. /// /// The value is retrieved verbatim from the parameter dictionary. /// No inference from MARS metadata and no validation of the numerical /// range is performed. /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused). /// /// @tparam ParDict_t /// Type of the parameter dictionary. Must provide /// `scaleFactorOfCentralWaveNumber`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary containing the scale factor. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// Scale factor of the central wave number. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `scaleFactorOfCentralWaveNumber` is missing, cannot be /// retrieved as a `long`, or if any unexpected error occurs. /// /// @note /// This deduction is deterministic and independent of GRIB header state. /// template long resolve_ScaleFactorOfCentralWaveNumber_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 scale factor of central wave number from parameter dictionary auto scaleFactorOfCentralWaveNumberVal = get_or_throw(par, "scaleFactorOfCentralWaveNumber"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`scaleFactorOfCentralWaveNumber` resolved from parameter dictionary: value='"; logMsg += std::to_string(scaleFactorOfCentralWaveNumberVal); logMsg += "'"; return logMsg; }()); // Success exit point return scaleFactorOfCentralWaveNumberVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2GribDeductionException( "Failed to resolve `scaleFactorOfCentralWaveNumber` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/instrumentType.h0000664000175000017500000001151215232567717026233 0ustar alastairalastair/* * (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 instrumentType.h /// @brief Deduction of the instrument type identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **instrument type identifier** from MARS metadata. /// /// The deduction retrieves the instrument identifier directly from the /// MARS dictionary and exposes it to the encoding layer without /// transformation or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, normalization, or defaulting /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref satelliteEncoding.h /// /// Related deductions: /// - @ref channel.h /// - @ref satelliteNumber.h /// - @ref satelliteSeries.h /// - @ref scaleFactorOfCentralWaveNumber.h /// - @ref scaledValueOfCentralWaveNumber.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 instrument type identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["instrument"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the instrument type identifier by retrieving /// the mandatory MARS key `instrument` and returning its value as a /// `long`. /// /// No semantic interpretation, normalization, or validation is applied. /// The meaning and allowed values of the instrument type identifier are /// defined by upstream MARS conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to /// `instrument` and conversion to `long`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the instrument type identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved instrument type identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `instrument` is missing, cannot be retrieved as a /// `long`, or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult instrument registries or GRIB tables. /// template long resolve_InstrumentType_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 instrument type long instrumentType = get_or_throw(mars, "instrument"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`instrumentType` resolved from input dictionaries: value='"; logMsg += std::to_string(instrumentType) + "'"; return logMsg; }()); // Success exit point return instrumentType; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `instrumentType` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/class.h0000664000175000017500000001115015232567717024264 0ustar alastairalastair/* * (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 class.h /// @brief Deduction of the MARS `class` identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **MARS data class identifier** from input dictionaries. /// /// The deduction retrieves the class identifier directly from the MARS /// dictionary and exposes it to the encoding layer without transformation /// or interpretation. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - applying minimal, explicit deduction logic /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply inference, normalization, or defaulting /// - 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 via deduction logic from input dictionaries /// - OVERRIDE: value provided by parameter dictionary overriding deduction logic /// /// @section References /// Concept: /// - @ref marsEncoding.h /// /// Related deductions: /// - @ref expver.h /// - @ref stream.h /// - @ref type.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 MARS data class identifier from input dictionaries. /// /// @section Deduction contract /// - Reads: `mars["class"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction resolves the MARS data class identifier by retrieving /// the mandatory MARS key `class` and returning its value as a /// `std::string`. /// /// No semantic interpretation, normalization, or defaulting is applied. /// The meaning of the class identifier is defined by upstream MARS /// conventions. /// /// @tparam MarsDict_t /// Type of the MARS dictionary. Must support keyed access to `class` /// and conversion to `std::string`. /// /// @tparam ParDict_t /// Type of the parameter dictionary (unused by this deduction). /// /// @tparam OptDict_t /// Type of the options dictionary (unused by this deduction). /// /// @param[in] mars /// MARS dictionary from which the data class identifier is resolved. /// /// @param[in] par /// Parameter dictionary (unused). /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The resolved MARS data class identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `class` is missing, cannot be retrieved as a string, /// or if any unexpected error occurs during deduction. /// /// @note /// This deduction performs presence-only validation and does not /// consult GRIB tables or apply semantic constraints. /// template std::string resolve_Class_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 class std::string marsClassVal = get_or_throw(mars, "class"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`class` resolved from input dictionaries: value='" + marsClassVal + "'"; return logMsg; }()); // Success exit point return marsClassVal; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `class` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/systemNumber.h0000664000175000017500000000726015232567717025663 0ustar alastairalastair/* * (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 systemNumber.h /// @brief Deduction of the GRIB wave system identifier. /// /// This header defines the deduction responsible for resolving the /// GRIB wave system identifier used in wave-related products. /// /// The value is obtained directly from MARS metadata and is treated /// as mandatory. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - validate semantic correctness /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries /// /// @section References /// Concept: /// - @ref longrangeEncoding.h /// /// Related deductions: /// - @ref methodNumber.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 GRIB wave system identifier. /// /// This deduction resolves the wave system identifier by retrieving /// the mandatory MARS key `system`. /// /// Resolution rules: /// - `mars::system` MUST be present /// - No defaulting or inference is applied /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary; must contain `system` /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return The resolved wave system identifier /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the system identifier cannot be resolved /// /// @note /// This deduction is deterministic and does not rely on any /// pre-existing GRIB header state. /// template long resolve_SystemNumber_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 systemNumber from MARS dictionary auto systemNumber = get_or_throw(mars, "system"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`systemNumber` resolved from input dictionaries: value='"; logMsg += std::to_string(systemNumber); logMsg += "'"; return logMsg; }()); // Success exit point return systemNumber; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `systemNumber` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/waveDirectionNumber.h0000664000175000017500000000761415232567717027145 0ustar alastairalastair/* * (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 waveDirectionNumber.h /// @brief Deduction of the GRIB wave direction number. /// /// This header defines the deduction responsible for resolving the /// GRIB wave direction number used in spectral wave products. /// /// The value is obtained directly from MARS metadata and represents /// the wave direction index associated with the encoded field. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Deductions do NOT: /// - infer missing values /// - apply defaults or fallbacks /// - validate semantic correctness /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - RESOLVE: value obtained directly from input dictionaries /// /// @section References /// Concept: /// - @ref waveEncoding.h /// /// Related deductions: /// - @ref periodItMin.h /// - @ref periodItMax.h /// - @ref waveDirectionGrid.h /// - @ref waveFrequencyGrid.h /// - @ref waveFrequencyNumber.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 GRIB wave direction number. /// /// This deduction retrieves the wave direction number from the /// MARS dictionary using the mandatory key `direction`. /// /// The resolved value represents the wave direction index used /// in spectral wave products and GRIB encoding. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary (unused) /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary; must contain the key `direction` /// @param[in] par Parameter dictionary (unused) /// @param[in] opt Options dictionary (unused) /// /// @return The resolved wave direction number /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If: /// - the key `direction` is missing /// - the value cannot be converted to `long` /// - any unexpected error occurs /// template long resolve_WaveDirectionNumber_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 wave direction number from MARS dictionary auto waveDirectionNumber = get_or_throw(mars, "direction"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`waveDirectionNumber` resolved from input dictionaries: value='"; logMsg += std::to_string(waveDirectionNumber); logMsg += "'"; return logMsg; }()); // Success exit point return waveDirectionNumber; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `waveDirectionNumber` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/modelErrorType.h0000664000175000017500000001126315232567717026140 0ustar alastairalastair/* * (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 modelErrorType.h /// @brief Deduction of the model-error type identifier. /// /// This header defines deduction utilities used by the mars2grib backend /// to resolve the **model-error type identifier** (`modelErrorType`) from /// the parameter dictionary. /// /// The value is not derivable from MARS alone. It must be supplied via /// the parameter dictionary by the upstream tool, typically read from /// the input GRIB1 handle being re-encoded. /// /// Deductions are responsible for: /// - extracting values from MARS, parameter, and option dictionaries /// - enforcing deterministic resolution rules /// - returning strongly typed values to concept operations /// /// Deductions: /// - do NOT encode GRIB keys directly /// - do NOT apply heuristic or data-driven inference /// - do NOT validate against GRIB code tables unless explicitly required /// /// 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 directly from input dictionaries /// /// @section References /// Concept: /// - @ref modelErrorEncoding.h /// /// Related deductions: /// - @ref componentIndex.h /// - @ref numberOfComponents.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once #include #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/logUtils.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::backend::deductions { /// /// @brief Resolve the model-error type identifier. /// /// @section Deduction contract /// - Reads: `par["modelErrorType"]` /// - Writes: none /// - Side effects: logging (RESOLVE) /// - Failure mode: throws /// /// This deduction retrieves the model-error type identifier from the /// parameter dictionary. /// /// The value is treated as mandatory: it cannot be derived from MARS /// metadata alone and must be supplied by the upstream tool that /// populates the parameter dictionary (typically read from the input /// GRIB1 handle being re-encoded). /// /// @tparam MarsDict_t /// Type of the MARS dictionary (unused). /// /// @tparam ParDict_t /// Type of the parameter dictionary. Must provide the key /// `modelErrorType`. /// /// @tparam OptDict_t /// Type of the options dictionary (unused). /// /// @param[in] mars /// MARS dictionary (unused). /// /// @param[in] par /// Parameter dictionary from which the model-error type is retrieved. /// /// @param[in] opt /// Options dictionary (unused). /// /// @return /// The model-error type identifier. /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// If the key `modelErrorType` is missing from the parameter dictionary, /// cannot be converted to `long`, or if any unexpected error occurs /// during deduction. /// /// @note /// This deduction does not infer or default the value. Absence of the /// key in the parameter dictionary is considered a contract violation /// by the upstream tool. /// template long resolve_ModelErrorType_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 parameter-dictionary modelErrorType long modelErrorType = get_or_throw(par, "modelErrorType"); // Emit RESOLVE log entry MARS2GRIB_LOG_RESOLVE([&]() { std::string logMsg = "`modelErrorType` resolved from input dictionaries: value="; logMsg += std::to_string(modelErrorType); return logMsg; }()); // Success exit point return modelErrorType; } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `modelErrorType` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/backend/deductions/subSetTrunc.h0000664000175000017500000001410515232567717025443 0ustar alastairalastair/* * (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 subSetTrunc.h /// @brief Deduction of the spectral subset truncation parameter. /// /// This header defines the deduction responsible for resolving the /// spectral subset truncation parameter used in spectral packing /// configurations. /// /// The value is obtained from the parameter dictionary when provided. /// If absent, a deterministic default is applied. /// /// Deductions: /// - extract values from input dictionaries /// - apply deterministic resolution logic /// - emit structured diagnostic logging /// /// Error handling follows a strict fail-fast strategy with nested /// exception propagation to preserve full diagnostic context. /// /// Logging policy: /// - OVERRIDE: value overridden from input dictionaries /// - DEFAULT: value defaulted due to missing input /// /// @section References /// Concept: /// - @ref packingEncoding.h /// /// Related deductions: /// - @ref bitsPerValue.h /// - @ref laplacianOperator.h /// /// @ingroup mars2grib_backend_deductions /// #pragma once // System includes #include #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 GRIB spectral subset truncation parameter. /// /// This deduction resolves the spectral subset truncation parameter /// used to define a reduced set of spectral coefficients. /// /// Resolution rules: /// - If `par::subSetTruncation` is present and valid, its value is used directly. /// - If `par::subSetTruncation` is absent, the value defaults explicitly to the /// smaller of the MARS truncation (`mars::truncation`) and a fixed maximum of 20. /// /// @tparam MarsDict_t Type of the MARS dictionary /// @tparam ParDict_t Type of the parameter dictionary /// @tparam OptDict_t Type of the options dictionary (unused) /// /// @param[in] mars MARS dictionary providing `truncation` for defaulting and validation /// @param[in] par Parameter dictionary; may contain `subSetTruncation` /// @param[in] opt Options dictionary (unused) /// /// @return The resolved spectral subset truncation value /// /// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException /// - If an unexpected error occurs during dictionary access /// - If `par::subSetTruncation` is provided but exceeds the MARS truncation or is negative /// - If MARS truncation is invalid when needed for defaulting /// /// @note /// This deduction is fully deterministic and does not depend on /// any pre-existing GRIB header state. /// template long resolve_SubSetTruncation_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::dict_traits::has; using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; try { // subSetTruncation must not be larger than any pentagonalResolutionParameter // NOTE: Mars keyword truncation is equivalent to pentagonalResolutionParameter{J,K,M} // At ECMWF we cannot produce spherical harmonics with different values for J/K/M const auto marsTruncation = get_or_throw(mars, "truncation"); if (marsTruncation < 0) { std::string logMsg = "Invalid MARS truncation: value='" + std::to_string(marsTruncation) + "' is negative"; throw Mars2GribDeductionException(logMsg, Here()); } long defaultSubSetTrunc = std::min(20L, marsTruncation); if (has(par, "subSetTruncation")) { // Retrieve subSetTruncation from parameter dictionary long subSetTrunc = get_or_throw(par, "subSetTruncation"); // Validate that subSetTruncation does not exceed MARS truncation if (subSetTrunc < 0) { std::string logMsg = "Invalid `subSetTruncation`:"; logMsg += " value='" + std::to_string(subSetTrunc); logMsg += "' is negative"; throw Mars2GribDeductionException(logMsg, Here()); } if (subSetTrunc > marsTruncation) { std::string logMsg = "Invalid `subSetTruncation`:"; logMsg += " value='" + std::to_string(subSetTrunc) + "'"; logMsg += " exceeds MARS truncation='" + std::to_string(marsTruncation) + "'"; throw Mars2GribDeductionException(logMsg, Here()); } // Emit OVERRIDE log entry MARS2GRIB_LOG_OVERRIDE([&]() { std::string logMsg = "`subSetTruncation` overridden from parameter dictionary: value='"; logMsg += std::to_string(subSetTrunc); logMsg += "'"; return logMsg; }()); // Success exit point return subSetTrunc; } else { // Emit DEFAULT log entry for defaulting MARS2GRIB_LOG_DEFAULT([&]() { std::string logMsg = "`subSetTruncation` defaulted: value='"; logMsg += std::to_string(defaultSubSetTrunc); logMsg += "'"; return logMsg; }()); // Success exit point return defaultSubSetTrunc; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2GribDeductionException("Failed to resolve `subSetTruncation` from input dictionaries", Here())); }; // Remove compiler warning mars2gribUnreachable(); }; } // namespace metkit::mars2grib::backend::deductions metkit-1.19.2/src/metkit/mars2grib/copilot-instructions.md0000664000175000017500000000623315232567717024021 0ustar alastairalastair# Copilot Instructions for mars2grib These instructions apply to AI-assisted changes under `src/metkit/mars2grib`. ## Concept Or Variant Decision Before implementing any modification in the mars2grib concept system, determine whether the requested behavior is a new concept or a new variant of an existing concept. - Use a new concept when the feature is an independent semantic axis that must be composable with other concepts. - Use a new variant when the feature is an alternative realization inside an existing semantic axis and does not need independent composability. - This distinction is usually not reliably deducible from code structure alone. - If the user has not explicitly said whether the change is a new concept or a variant, ask the user before implementing. ## Level Concept Guardrail The `level` concept is intentionally constrained. In the GRIB header, vertical level information is ultimately represented by these six low-level fixed-surface keys: - `typeOfFirstFixedSurface` - `scaleFactorOfFirstFixedSurface` - `scaledValueOfFirstFixedSurface` - `typeOfSecondFixedSurface` - `scaleFactorOfSecondFixedSurface` - `scaledValueOfSecondFixedSurface` Do not set these keys directly as a shortcut or workaround. mars2grib encodes only official level definitions by relying on `typeOfLevel` plus, when needed, `level`, `topLevel`, `bottomLevel`, and PV-array data. Each supported `typeOfLevel` maps to a prescribed fixed-surface configuration. Some virtual `typeOfLevel` values exist because they cannot be introduced in ecCodes for backward-compatibility reasons. If a requested change appears to require direct writes to fixed-surface keys, do not implement that approach. Instead, add or adjust the appropriate `LevelType` variant, matcher mapping, or deduction so the level remains encoded through the official level abstraction. ## Documentation synchronization rule When working on a pull request: 1. Determine the set of files modified by the PR. 2. From that set, consider only files under: - `src/metkit/mars2grib` - Changes under `tests/mars2grib` require documentation updates only if they affect documented public behaviour 3. For each of those files: - Verify that the related documentation is present in the concrete doc locations for mars2grib, including (where applicable): - `src/metkit/mars2grib/docs/**` - Any module-level `.md` files or Doxygen pages associated with the modified code - Verify that the documentation in these locations is up to date with the code changes - If documentation is missing or outdated in these locations, propose the required updates 4. Do not request documentation changes for files outside these paths. ## Definition of “documentation in sync” Documentation must: - Describe the current public behavior and interfaces - Reflect any new parameters, options, or outputs - Remove references to deleted functionality ## PR review behavior During PR reviews: - Explicitly list the impacted files in the two target directories - State whether documentation is: - ✅ in sync - ❌ missing - ❌ outdated - Suggest concrete doc patches when needed, referencing the specific lines or sections that require updates.metkit-1.19.2/src/metkit/mars2grib/CoreOperations.h0000664000175000017500000006172515232567717022400 0ustar alastairalastair/* * (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 CoreOperations.h /// @brief High-level service layer for GRIB encoding and resolution orchestration. /// /// This header defines the `CoreOperations` suite, providing the primary /// functional building blocks for the mars2grib library. /// /// These operations facilitate a staged translation pipeline: /// 1. **Sanitization**: Normalizing input dictionaries against the language definition. /// 2. **Header Resolution**: Determining the GRIB structural layout and encoding metadata. /// 3. **Value Injection**: Physical realization of the GRIB data section. /// 4. **Diagnostic Capture**: Generating regression data for structural validation. /// /// /// --- /// /// ## Transitional cache preparation /// /// This header also contains a temporary staged-encoding path based on /// `prepare()` and `finaliseEncoding()`. /// /// This code is a preparatory step toward a future implementation where /// cache lifecycle and reuse are fully internalized inside the core layer. /// /// At the current stage, the staged API exists to support benchmarking, /// comparison, and incremental integration of the future cache design. /// /// @ingroup mars2grib_core /// #pragma once // System includes #include #include #include /// Project includes /// @note: clang-format needs to be off here to preserve the logical grouping of /// includes and avoid unnecessary reordering that breaks the layering and dependencies. // clang-format off #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/backend/concepts/GeneralRegistry.h" #include "metkit/mars2grib/backend/encodeValues.h" #include "metkit/mars2grib/frontend/header/SpecializedEncoder.h" #include "metkit/mars2grib/frontend/make_HeaderLayout.h" #include "metkit/mars2grib/frontend/normalization/normalization.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" // clang-format on namespace metkit::mars2grib { /// /// @brief Internal engine providing atomic encoding and diagnostic services. /// struct CoreOperations { /// /// @brief Normalize input dictionaries against the library language definition. /// /// This operation performs key-value sanitization for both MARS and Parameter /// metadata. It utilizes a **reference-redirection strategy**: if no /// modification is required, the returned references point to the original /// inputs; otherwise, they point to the provided scratch buffers. /// /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter dictionary type /// @tparam OptDict_t Encoding options dictionary type /// /// @param[in] inputMars Original MARS request /// @param[in] inputMisc Original Parameter metadata /// @param[in] opt Encoding options /// @param[in] lang Language definition (eckit::Value) /// @param[out] scratchMars Scratch buffer for MARS sanitization /// @param[out] scratchMisc Scratch buffer for Parameter sanitization /// /// @return A tuple containing const references to the active (sanitized) data /// template static std::tuple normalize_if_enabled( const MarsDict_t& inputMars, const ParDict_t& inputMisc, const OptDict_t& opt, const eckit::Value& lang, MarsDict_t& scratchMars, ParDict_t& scratchMisc) { try { const MarsDict_t& activeMars = frontend::normalization::normalize_MarsDict_if_enabled(inputMars, opt, lang, scratchMars); const ParDict_t& activePar = frontend::normalization::normalize_MiscDict_if_enabled(inputMisc, opt, lang, scratchMisc); return {activeMars, activePar}; } catch (...) { // Wrap any exception in a CoreOperations-specific exception to provide context std::throw_with_nested( mars2grib::utils::exceptions::Mars2GribGenericException("Error during metadata normalization", Here())); } } /// /// @brief Resolve and encode GRIB header metadata. /// /// Executes the structural resolution phase to determine the GRIB layout /// and triggers the specialized metadata encoder to populate the header /// sections of the output object. /// /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter dictionary type /// @tparam OptDict_t Encoding options dictionary type /// @tparam OutDict_t Output GRIB handle/dictionary type /// template static std::unique_ptr encodeHeader(const MarsDict_t& mars, const ParDict_t& misc, const OptDict_t& opt) { try { using metkit::mars2grib::frontend::make_HeaderLayout_or_throw; using metkit::mars2grib::frontend::header::SpecializedEncoder; auto layout = make_HeaderLayout_or_throw(mars, opt); return SpecializedEncoder{std::move(layout)}.encode(mars, misc, opt); } catch (...) { // Wrap any exception in a CoreOperations-specific exception to provide context std::throw_with_nested( mars2grib::utils::exceptions::Mars2GribGenericException("Error during header encoding", Here())); } } /// /// @brief Inject numeric field values into a GRIB handle. /// /// A procedural operation that handles bitmap generation and physical /// data compression. Utilizes spans for zero-copy data passing. /// /// @tparam Val_t Numeric precision (float or double) /// @tparam ParDict_t Parameter dictionary type /// @tparam OptDict_t Encoding options dictionary type /// @tparam OutDict_t Output GRIB handle/dictionary type /// template static std::unique_ptr encodeValues(backend::Span values, const MarsDict_t& mars, const ParDict_t& misc, const OptDict_t& opt, std::unique_ptr handle) { try { if (metkit::mars2grib::utils::skipSection3(opt)) { metkit::mars2grib::backend::encodeValuesGridSpec(values, mars, misc, opt, *handle); return handle; } else { metkit::mars2grib::backend::encodeValues(values, misc, opt, *handle); return handle; } } catch (...) { // Wrap any exception in a CoreOperations-specific exception to provide context std::throw_with_nested( mars2grib::utils::exceptions::Mars2GribGenericException("Error during value encoding", Here())); } } /// /// @brief Encode a value field into a GRIB message. /// /// This function performs the complete encoding pipeline: /// - optional metadata normalization, /// - GRIB header construction, /// - value injection. /// /// The function is exception-safe and returns an output dictionary. This can be a fully initialized `CodesHandle` /// owning the encoded GRIB message. /// /// ----------------------------------------------------------------------------- /// Normalization and lifetime semantics (CRITICAL) /// ----------------------------------------------------------------------------- /// /// Metadata normalization is **conditionally enabled** based on runtime /// options. /// /// The normalization step does **not** return new objects. Instead, it /// returns **references** to the *active* metadata dictionaries: /// /// - If normalization is **disabled**: /// - references alias the input objects (`inputMars`, `inputMisc`) /// /// - If normalization is **enabled**: /// - references alias local scratch objects (`scratchMars`, `scratchMisc`) /// - the scratch objects contain normalized copies of the inputs /// /// The returned references must be treated as **borrowed**: /// - they must not be stored, /// - they must not escape this function, /// - their lifetime is strictly limited to this scope. /// /// This contract allows the pipeline to avoid unnecessary allocations when /// normalization is disabled, while preserving correctness when it is enabled. /// /// ----------------------------------------------------------------------------- /// @tparam Val_t /// Numeric type of the values to be encoded. /// /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameterization dictionary type /// @tparam OptDict_t Options dictionary type /// @tparam OutDict_t Output dictionary type /// /// @param values /// Contiguous span of values to encode. /// /// @param inputMars /// Input MARS description of the data (read-only). /// /// @param inputMisc /// Input miscellaneous description of the data (read-only). /// /// @param options /// Encoding options controlling behavior such as validation, /// logging, or feature toggles. /// /// @param language /// MARS language definition /// /// @return /// A `std::unique_ptr` with GRIB keys set /// /// @throws mars2grib::Exception /// If normalization, header encoding, or value encoding fails. /// template static std::unique_ptr encode(const metkit::codes::Span& values, const MarsDict_t& inputMars, const ParDict_t& inputMisc, const OptDict_t& options, const eckit::Value& language) { using metkit::mars2grib::utils::exceptions::printExtendedStack; // 1. Prepare Scratches for Normalization MarsDict_t scratchMars; ParDict_t scratchMisc; try { // 2. Normalize Metadata (conditionally) // ----------------------------------------------------------------- // IMPORTANT: Normalization returns *references*, not values. // // Depending on runtime options: // - normalization DISABLED -> activeMars / activeMisc alias inputs // - normalization ENABLED -> activeMars / activeMisc alias scratch // // The returned references are BORROWED and MUST NOT escape this scope. // Their lifetime is bounded by `scratchMars` / `scratchMisc`. // ----------------------------------------------------------------- auto [activeMars, activeMisc] = normalize_if_enabled(inputMars, inputMisc, options, language, scratchMars, scratchMisc); // 3. Encode Header (SpecializedEncoder creates the CodesHandle here) auto gribHeader = encodeHeader(activeMars, activeMisc, options); // 4. Inject Values return encodeValues(values, activeMars, activeMisc, options, std::move(gribHeader)); } catch (const std::exception& e) { printExtendedStack(e); throw; } catch (...) { // Fallback for non-standard exceptions throw metkit::mars2grib::utils::exceptions::Mars2GribGenericException("Unknown error during encoding", Here()); } } // ------------------------------------------------------------------------- // Temporary staged-cache preparation path // ------------------------------------------------------------------------- // // The following types and functions implement a transitional staged // preparation/finalisation workflow. // // This code exists as a preparatory step toward a future version where // cache construction, reuse, and lifecycle are fully internalized in the // core encoding layer. // /// /// @brief Immutable staged-encoding cache entry. /// /// This object stores the reusable state required to split the encoding /// pipeline into two phases: /// /// 1. **Preparation** /// - metadata normalization /// - header layout resolution /// - encoder specialization /// - creation of a reusable prepared sample /// /// 2. **Finalisation** /// - reuse of the prepared structural state /// - injection of dynamic metadata if needed /// - value encoding into a fresh output object /// /// The cache entry is intentionally immutable: /// - copy is disabled /// - move is disabled /// - the prepared sample is owned through an immutable smart pointer /// /// This guarantees that once constructed, the cache entry represents a /// stable reusable encoding context. /// /// @note /// This staged cache path is a preparatory step toward a future /// implementation where cache management is fully internalized in the /// core encoding layer. /// /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter/misc dictionary type /// @tparam OptDict_t Encoding options dictionary type /// @tparam OutDict_t Output GRIB handle/dictionary type /// template struct CacheEntry { using Encoder = metkit::mars2grib::frontend::header::SpecializedEncoder; using Layout = metkit::mars2grib::frontend::GribHeaderLayoutData; /// /// @brief Construct a cache entry from resolved layout and active metadata. /// /// This constructor: /// - internalizes the resolved header layout into a specialized encoder /// - prepares an immutable reusable sample from the active metadata /// /// The input metadata is expected to already represent the active /// dictionaries selected after any optional normalization step. /// /// @param[in] layout /// Resolved GRIB header layout to internalize. /// /// @param[in] inputMars /// Active MARS metadata dictionary. /// /// @param[in] inputMisc /// Active parameter/misc metadata dictionary. /// /// @param[in] options /// Encoding options controlling specialization behavior. /// /// @note /// This constructor is part of the temporary staged-cache path and /// prepares the structure required for a future internal cache design. /// CacheEntry(Layout&& layout, const MarsDict_t& inputMars, const ParDict_t& inputMisc, const OptDict_t& options) : encoder_{std::move(layout)}, preparedSample_{encoder_.prepare(inputMars, inputMisc, options)} {}; CacheEntry(const CacheEntry&) = delete; CacheEntry& operator=(const CacheEntry&) = delete; CacheEntry(CacheEntry&&) = delete; CacheEntry& operator=(CacheEntry&&) = delete; ~CacheEntry() = default; /// /// @brief Fully specialized immutable encoder. /// /// This encoder owns the resolved layout and the precomputed execution /// plan used during staged finalisation. /// const Encoder encoder_; /// /// @brief Immutable prepared sample used as finalisation template. /// /// This object represents the reusable structural sample produced /// during the preparation phase. It is treated as read-only input /// during `finaliseEncoding()`, which derives a fresh output handle /// from it. /// const std::unique_ptr preparedSample_; }; /// /// @brief Prepare a reusable staged-encoding cache. /// /// This function performs the preparation phase of the staged encoding /// pipeline: /// - optional metadata normalization /// - header layout resolution /// - construction of a specialized encoder /// - creation of an immutable reusable sample /// /// The returned cache entry can later be passed to /// `finaliseEncoding()` to complete the encoding with concrete field /// values. /// /// ----------------------------------------------------------------------------- /// Normalization and lifetime semantics (CRITICAL) /// ----------------------------------------------------------------------------- /// /// As in `encode()`, normalization returns borrowed references to the /// active metadata dictionaries: /// - either the original inputs /// - or local scratch objects containing normalized copies /// /// These references must not escape this function except through /// immediate materialization into owned cache state. /// /// The resulting `CacheEntry` stores only owned immutable state and /// does not retain references to the scratch buffers. /// /// ----------------------------------------------------------------------------- /// /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter/misc dictionary type /// @tparam OptDict_t Encoding options dictionary type /// @tparam OutDict_t Output GRIB handle/dictionary type /// /// @param[in] inputMars /// Input MARS description of the data. /// /// @param[in] inputMisc /// Input miscellaneous description of the data. /// /// @param[in] options /// Encoding options controlling specialization and normalization. /// /// @param[in] language /// MARS language definition. /// /// @return /// A unique pointer owning an immutable staged cache entry. /// /// @throws mars2grib::Exception /// If normalization, layout resolution, or sample preparation fails. /// /// @note /// This staged cache path is a preparatory step toward a future /// implementation where cache lifecycle and reuse are fully /// internalized inside the core encoding layer. /// template static std::unique_ptr> prepare( const MarsDict_t& inputMars, const ParDict_t& inputMisc, const OptDict_t& options, const eckit::Value& language) { // 1. Prepare Scratches for Normalization MarsDict_t scratchMars; ParDict_t scratchMisc; try { using metkit::mars2grib::frontend::make_HeaderLayout_or_throw; auto [activeMars, activeMisc] = normalize_if_enabled(inputMars, inputMisc, options, language, scratchMars, scratchMisc); auto layout = make_HeaderLayout_or_throw(activeMars, options); return std::make_unique>( std::move(layout), activeMars, activeMisc, options); } catch (...) { // Wrap any exception in a CoreOperations-specific exception to provide context std::throw_with_nested( mars2grib::utils::exceptions::Mars2GribGenericException("Error during cache preparation", Here())); } } /// /// @brief Complete an encoding from a previously prepared cache entry. /// /// This function performs the finalisation phase of the staged encoding /// pipeline: /// - optional metadata normalization /// - reuse of the immutable specialized encoder /// - reuse of the immutable prepared sample /// - creation of a fresh header object /// - injection of the field values into the resulting output handle /// /// The provided cache entry is treated as read-only and is not consumed. /// It may therefore be reused across multiple finalisation calls. /// /// ----------------------------------------------------------------------------- /// Normalization and lifetime semantics (CRITICAL) /// ----------------------------------------------------------------------------- /// /// As in `encode()` and `prepare()`, normalization yields borrowed /// references to the active metadata dictionaries. These references are /// used only within this function and are not stored. /// /// The cache entry itself is fully owned and immutable, and no references /// to local scratch objects escape the function. /// /// ----------------------------------------------------------------------------- /// /// @tparam Val_t Numeric type of the values to be encoded /// @tparam MarsDict_t MARS dictionary type /// @tparam ParDict_t Parameter/misc dictionary type /// @tparam OptDict_t Encoding options dictionary type /// @tparam OutDict_t Output GRIB handle/dictionary type /// /// @param[in] cacheEntry /// Immutable staged cache entry previously produced by `prepare()`. /// /// @param[in] values /// Contiguous span of values to encode. /// /// @param[in] inputMars /// Input MARS description of the data. /// /// @param[in] inputMisc /// Input miscellaneous description of the data. /// /// @param[in] options /// Encoding options controlling behavior such as validation, /// logging, or feature toggles. /// /// @param[in] language /// MARS language definition. /// /// @return /// A `std::unique_ptr` owning the fully encoded output object. /// /// @throws mars2grib::Exception /// If normalization, staged header finalisation, or value encoding fails. /// /// @note /// This staged cache path is a preparatory step toward a future /// implementation where cache lifecycle and reuse are fully /// internalized inside the core encoding layer. /// template static std::unique_ptr finaliseEncoding( const CacheEntry& cacheEntry, const metkit::codes::Span& values, const MarsDict_t& inputMars, const ParDict_t& inputMisc, const OptDict_t& options, const eckit::Value& language) { // 1. Prepare Scratches for Normalization MarsDict_t scratchMars; ParDict_t scratchMisc; try { auto [activeMars, activeMisc] = normalize_if_enabled(inputMars, inputMisc, options, language, scratchMars, scratchMisc); auto gribHeader = cacheEntry.encoder_.finaliseEncoding(*(cacheEntry.preparedSample_), activeMars, activeMisc, options); return encodeValues(values, activeMars, activeMisc, options, std::move(gribHeader)); } catch (...) { // Wrap any exception in a CoreOperations-specific exception to provide context std::throw_with_nested( mars2grib::utils::exceptions::Mars2GribGenericException("Error during encoding finalisation", Here())); } } /// /// @brief Capture a structural test point for regression analysis. /// /// Serializes the current resolution state (GRIB Blueprint) /// into a JSON format suitable for external validation tools. /// /// @tparam MarsDict_t MARS dictionary type /// @tparam OptDict_t Encoding options dictionary type /// // template // static std::string dumpHeaderTest(const MarsDict_t& mars, const OptDict_t& opt) { // try { // using metkit::mars2grib::frontend::make_HeaderLayout_or_throw; // using metkit::mars2grib::frontend::debug::debug_convert_GribHeaderLayoutData_to_json; // // auto layout = make_HeaderLayout_or_throw(mars, opt); // // return debug_convert_GribHeaderLayoutData_to_json(layout); // } // catch (...) { // // Wrap any exception in a CoreOperations-specific exception to provide context // std::throw_with_nested( // mars2grib::utils::exceptions::Mars2GribGenericException("Error during header test dump", Here())); // } // } }; } // namespace metkit::mars2grib metkit-1.19.2/src/metkit/mars2grib/utils/0000775000175000017500000000000015232567717020420 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/utils/configConverter.h0000664000175000017500000002456615232567717023743 0ustar alastairalastair#include #include #include #include #include #include "eckit/config/LocalConfiguration.h" #include "eckit/log/Log.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/backend/concepts/concept_registry.h" #include "metkit/mars2grib/backend/sections/section_registry.h" #include "metkit/mars2grib/backend/sections/sections_recipes.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" namespace metkit::mars2grib::utils::cfg { // Namespaces shortcuts using metkit::mars2grib::backend::cnpts::NUM_SECTIONS; using metkit::mars2grib::utils::exceptions::Mars2GribGenericException; // Namespaces shortcuts inline constexpr std::array sectionNames = { {"indicator-section", "identification-section", "local-use-section", "grid-definition-section", "product-definition-section", "data-representation-section"}}; inline std::string strip_descriptor(std::string_view name) { constexpr std::string_view suffix = "-configurator"; if (name.size() >= suffix.size() && name.substr(name.size() - suffix.size()) == suffix) { return std::string{name.substr(0, name.size() - suffix.size())}; } return std::string{name}; } struct Section { uint16_t templateNumber_; std::map concepts_; }; struct EncoderCfg { std::array sec_; }; Section lookupCfgSection(const eckit::LocalConfiguration& cfg, long SecId) { // Get section name from Id std::string sectionName = std::string(sectionNames[SecId]); // Verify the section is present if (!cfg.has(sectionName)) { throw Mars2GribGenericException(sectionName + " is missing", Here()); } // Get section configuration eckit::LocalConfiguration sectionCfg = cfg.getSubConfiguration(sectionName); // Initialize the Section Section sec; // Verify template number is present if (!sectionCfg.has("template-number")) { throw Mars2GribGenericException(sectionName + " has no template number", Here()); } // Get template number sec.templateNumber_ = static_cast(sectionCfg.getLong("template-number")); // Populate concepts for (auto& name : sectionCfg.keys()) { // Skip template number if (name == "template-number") { continue; // Skip template number }; // Get the key in the concept map std::string key = strip_descriptor(name); // Get the type eckit::LocalConfiguration conceptCfg = sectionCfg.getSubConfiguration(name); if (!conceptCfg.has("type")) { throw Mars2GribGenericException("No type found for concept " + name, Here()); } std::string type = conceptCfg.getString("type"); // ========================================================================================= // Modify type if (key == "model") { sec.concepts_.insert(std::make_pair("generatingProcess", std::string(type))); } else if (key == "data-type") { sec.concepts_.insert(std::make_pair("dataType", std::string(type))); } else if (key == "reference-time") { if (cfg.has("product-definition-section")) { auto tmp = cfg.getSubConfiguration("product-definition-section"); if (tmp.has("template-number")) { long tmplNum = tmp.getLong("template-number"); if (tmplNum == 60 || tmplNum == 61) { type = "reforecast"; } else { type = "standard"; } } else { throw Mars2GribGenericException("No product definition template number in configuration", Here()); } } else { throw Mars2GribGenericException("No product definition template number in configuration", Here()); } sec.concepts_.insert(std::make_pair("referenceTime", std::string(type))); } else if (key == "direction-frequency") { sec.concepts_.insert(std::make_pair("wave", "spectra")); } else if (key == "period") { sec.concepts_.insert(std::make_pair("wave", "period")); } else if (key == "ensemble") { sec.concepts_.insert(std::make_pair("ensemble", "individual")); } else if (key == "point-in-time") { sec.concepts_.insert(std::make_pair("pointInTime", std::string(type))); } else if (key == "chemistry") { sec.concepts_.insert(std::make_pair("composition", std::string(type))); } else if (key == "param") { sec.concepts_.insert(std::make_pair("param", "default")); } else if (key == "time-statistics") { std::string typeOfStatisticalProcess = conceptCfg.getString("type-of-statistical-processing"); if (typeOfStatisticalProcess == "average") { // Insert into the map sec.concepts_.insert(std::make_pair("statistics", "average")); } else if (typeOfStatisticalProcess == "accumul") { // Insert into the map sec.concepts_.insert(std::make_pair("statistics", "accumulation")); } else if (typeOfStatisticalProcess == "max") { // Insert into the map sec.concepts_.insert(std::make_pair("statistics", "maximum")); } else if (typeOfStatisticalProcess == "min") { // Insert into the map sec.concepts_.insert(std::make_pair("statistics", "minimum")); } else if (typeOfStatisticalProcess == "stddev") { // Insert into the map sec.concepts_.insert(std::make_pair("statistics", "standardDeviation")); } else if (typeOfStatisticalProcess == "mode") { // Insert into the map sec.concepts_.insert(std::make_pair("statistics", "mode")); } else if (typeOfStatisticalProcess == "severity") { // Insert into the map sec.concepts_.insert(std::make_pair("statistics", "severity")); } else { throw Mars2GribGenericException( "Unsupported type-of-statistical-processing " + typeOfStatisticalProcess + " for concept " + name, Here()); } } else { sec.concepts_.insert(std::make_pair(key, type)); } } // for concept name return sec; }; // lookupCfgSection Section lookupExpectedSection(const eckit::LocalConfiguration& cfg, long SecId) { using metkit::mars2grib::backend::sections::ConceptList; using metkit::mars2grib::backend::sections::resolveSectionTemplateConcepts; // Get section name from Id std::string sectionName = std::string(sectionNames[SecId]); // Verify the section is present if (!cfg.has(sectionName)) { throw Mars2GribGenericException(sectionName + " is missing", Here()); } // Get section configuration eckit::LocalConfiguration sectionCfg = cfg.getSubConfiguration(sectionName); // Initialize the Section Section sec; // Verify template number is present if (!sectionCfg.has("template-number")) { throw Mars2GribGenericException(sectionName + " has no template number", Here()); } // Get template number sec.templateNumber_ = static_cast(sectionCfg.getLong("template-number")); // Populate concepts const std::optional concepts = resolveSectionTemplateConcepts(SecId, sec.templateNumber_); // Insert into the map if (concepts) { for (const auto& concept : *concepts) { std::string key = std::string(concept.name); std::string type = concept.type ? std::string(*concept.type) : "default"; sec.concepts_.insert(std::make_pair(key, type)); } } else { throw Mars2GribGenericException( "No concepts found for " + sectionName + " template number " + std::to_string(sec.templateNumber_), Here()); } return sec; }; EncoderCfg parseEncoderCfg(const eckit::LocalConfiguration& cfg) { // Initialize EncoderCfg EncoderCfg encoderCfg; EncoderCfg expectedCfg; EncoderCfg combinedCfg; // Populate all sections for (size_t i = 0; i < NUM_SECTIONS; ++i) { encoderCfg.sec_[i] = lookupCfgSection(cfg, i); expectedCfg.sec_[i] = lookupExpectedSection(cfg, i); } // Combine configurations for (size_t i = 0; i < NUM_SECTIONS; ++i) { // Combine template number if (encoderCfg.sec_[i].templateNumber_ != expectedCfg.sec_[i].templateNumber_) { throw Mars2GribGenericException("Template number mismatch for section ", Here()); } combinedCfg.sec_[i].templateNumber_ = encoderCfg.sec_[i].templateNumber_; // Combine concepts for (const auto& [key, type] : expectedCfg.sec_[i].concepts_) { auto it = encoderCfg.sec_[i].concepts_.find(key); if (it != encoderCfg.sec_[i].concepts_.end()) { // Verify type matches if (type != "default" && it->second != type) { throw Mars2GribGenericException("Concept type mismatch for concept ", Here()); } combinedCfg.sec_[i].concepts_.insert(std::make_pair(key, type)); } else { combinedCfg.sec_[i].concepts_.insert(std::make_pair(key, type)); } } auto it = combinedCfg.sec_[4].concepts_.find("referenceTime"); if (it != combinedCfg.sec_[4].concepts_.end()) { combinedCfg.sec_[1].concepts_["referenceTime"] = combinedCfg.sec_[4].concepts_["referenceTime"]; } } // Validate concepts against expected return combinedCfg; }; void print_encoder_cfg(const EncoderCfg& cfg) { for (uint32_t i = 0; i < NUM_SECTIONS; ++i) { std::cout << "Section " << i << " (template " << cfg.sec_[i].templateNumber_ << "):" << std::endl; for (const auto& [key, type] : cfg.sec_[i].concepts_) { std::cout << " Concept: " << key << ", Type: " << type << std::endl; } } }; } // namespace metkit::mars2grib::utils::cfg metkit-1.19.2/src/metkit/mars2grib/utils/mars2gribExceptions.h0000664000175000017500000004455515232567717024540 0ustar alastairalastair/* * (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 mars2gribExceptions.h /// @brief Unified exception hierarchy for the mars2grib framework. /// /// This header defines the complete exception model used across /// mars2grib, covering: /// /// - Generic infrastructure errors /// - Layer-specific failures (matcher, rules, validation, tables, deduction) /// - Concept execution failures (with contextual metadata) /// - Encoder failures (with serialized dictionary state) /// /// The hierarchy is designed with the following goals: /// /// - Strong contextual diagnostics /// - Support for nested exception propagation /// - Structured debug frame printing /// - Clear separation between backend and frontend layers /// /// All exceptions ultimately derive from `eckit::Exception`, /// ensuring compatibility with the broader ECMWF ecosystem. /// /// Nested exception support allows propagation chains to be /// printed in a structured stack-like format. /// /// @ingroup mars2grib_utils #pragma once // System includes #include #include #include #include // Project includes #include "eckit/exception/Exceptions.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::utils::exceptions { /// @brief Base exception for mars2grib. /// /// This is the root exception type for most mars2grib failures. /// It: /// /// - Inherits from `eckit::Exception` /// - Supports nested exceptions via `std::nested_exception` /// - Provides structured frame printing /// /// Derived exceptions typically extend this class with /// additional contextual metadata. /// /// The `printFrame()` method is designed to be used by /// extended stack printers. class Mars2GribGenericException : public eckit::Exception, public std::nested_exception { public: Mars2GribGenericException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : eckit::Exception(reason, loc) {} virtual ~Mars2GribGenericException() = default; virtual void printFrame(const std::string& pad) const { const auto& loc = location(); LOG_DEBUG_LIB(LibMetkit) << pad << "+ file: " << loc.file() << "\n" << pad << "+ function: " << loc.func() << "\n" << pad << "+ line: " << loc.line() << "\n" << pad << "+ link: " << loc.file() << ":" << loc.line() << "\n" << pad << "+ message: " << what() << "\n"; } }; /// @brief Exception raised during GRIB section layout resolution. /// /// Used when the structural resolution layer fails while building /// the layout for a specific GRIB section. /// /// This exception extends the generic exception with section-specific /// diagnostic context, including: /// /// - The GRIB section identifier being resolved /// - A serialized snapshot of the active concepts state /// /// The additional metadata is emitted via `printFrame()` to support /// structured debugging of section selection failures. class Mars2GribSectionResolutionException : public Mars2GribGenericException { public: Mars2GribSectionResolutionException(std::string reason, std::size_t sectionId, const std::string& activeConceptsJson, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc), sectionId_(sectionId), activeConceptsJson_(activeConceptsJson) {} virtual ~Mars2GribSectionResolutionException() = default; virtual void printFrame(const std::string& pad) const { const auto& loc = location(); Mars2GribGenericException::printFrame(pad); LOG_DEBUG_LIB(LibMetkit) << pad << "+ section: " << sectionId_ << "\n" << pad << "+ activeConcepts: " << activeConceptsJson_ << "\n"; } private: std::size_t sectionId_; std::string activeConceptsJson_; }; /// @brief Exception raised during top-level header layout construction. /// /// Used when the frontend resolution pipeline fails while assembling /// the complete GRIB header layout from the input MARS request. /// /// This exception extends the generic exception with a serialized /// representation of the originating MARS request for diagnostics. /// /// The captured request payload is emitted via `printFrame()` to aid /// debugging of end-to-end layout resolution failures. class Mars2GribHeaderLayoutException : public Mars2GribGenericException { public: Mars2GribHeaderLayoutException(std::string reason, const std::string& marsJson, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc), marsJson_(marsJson) {} virtual ~Mars2GribHeaderLayoutException() = default; virtual void printFrame(const std::string& pad) const { const auto& loc = location(); Mars2GribGenericException::printFrame(pad); LOG_DEBUG_LIB(LibMetkit) << pad << "+ marsJson: " << marsJson_ << "\n"; } private: std::string marsJson_; }; /// @brief Exception raised during matcher evaluation. /// /// This exception is used when resolving whether a concept /// should be activated based on MARS input. /// /// It may optionally carry: /// /// - `param` : parameter identifier /// - `levtype` : level type /// /// All metadata fields are optional and printed only if defined. /// /// This exception extends the generic exception with matcher-specific /// diagnostic context. class Mars2GribMatcherException : public Mars2GribGenericException { public: Mars2GribMatcherException(long param, const std::string& levtype, std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc), param_{std::to_string(param)}, levtype_{levtype} {} Mars2GribMatcherException(const std::string& levtype, std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc), param_{std::nullopt}, levtype_{levtype} {} Mars2GribMatcherException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc), param_{std::nullopt}, levtype_{std::nullopt} {} Mars2GribMatcherException(long param, std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc), param_{std::to_string(param)}, levtype_{std::nullopt} {} const std::string levtype() const { return levtype_.has_value() ? levtype_.value() : "undefined"; } const std::string param() const { return param_.has_value() ? param_.value() : "undefined"; } void printFrame(const std::string& pad) const override { Mars2GribGenericException::printFrame(pad); if (param_) { LOG_DEBUG_LIB(LibMetkit) << pad << "+ param: " << param() << std::endl; } if (levtype_) { LOG_DEBUG_LIB(LibMetkit) << pad << "+ levtype: " << levtype() << std::endl; } }; private: std::optional param_; std::optional levtype_; }; /// @brief Exception raised in the rules layer. /// /// Used when evaluating rule-based logic fails. /// /// This class derives directly from `eckit::Exception` and /// supports nested exceptions. class Mars2GribRulesException : public eckit::Exception, public std::nested_exception { public: Mars2GribRulesException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : eckit::Exception(reason, loc) {} }; /// @brief Exception raised in the dictionary access layer. /// /// Used when dictionary validation or access fails. /// Inherits structured printing from the generic exception. class Mars2GribDictException : public Mars2GribGenericException { public: Mars2GribDictException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc) {} }; /// @brief Exception raised in the validation layer. /// /// Intended for semantic validation errors. /// Currently does not add additional metadata beyond the base class. class Mars2GribValidationException : public Mars2GribGenericException { public: Mars2GribValidationException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc) {} }; /// @brief Exception raised in table resolution logic. /// /// Used when GRIB table lookup or interpretation fails. class Mars2GribTableException : public Mars2GribGenericException { public: Mars2GribTableException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc) {} }; /// @brief Exception raised in the deduction layer. /// /// Used when derived values cannot be computed or inferred /// from the provided dictionaries. class Mars2GribDeductionException : public Mars2GribGenericException { public: Mars2GribDeductionException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc) {} }; /// @brief Exception raised during concept execution. /// /// This is the most context-rich exception in the hierarchy. /// It carries: /// /// - Concept name /// - Concept variant /// - Encoding stage /// - GRIB section /// /// This allows precise identification of: /// /// - Which concept failed /// - Under which stage /// - In which section /// /// The metadata is optional and printed only if present. /// /// This exception is typically constructed via helper macros: /// /// - `MARS2GRIB_CONCEPT_THROW` /// - `MARS2GRIB_CONCEPT_RETHROW` /// /// Those macros automatically inject compile-time metadata. class Mars2GribConceptException : public Mars2GribGenericException { public: Mars2GribConceptException(std::string name, std::string variant, std::string stage, std::string section, std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc), conceptName_(std::move(name)), conceptVariant_(std::move(variant)), stage_(std::move(stage)), section_(std::move(section)) {} const std::optional& conceptName() const { return conceptName_; } const std::optional& conceptVariant() const { return conceptVariant_; } const std::optional& stage() const { return stage_; } const std::optional& section() const { return section_; } void printFrame(const std::string& pad) const override { Mars2GribGenericException::printFrame(pad); auto print_opt = [&](const char* k, const std::optional& v) { if (v) LOG_DEBUG_LIB(LibMetkit) << pad << "+ " << k << ": " << *v << "\n"; }; print_opt("concept", conceptName_); print_opt("variant", conceptVariant_); print_opt("stage", stage_); print_opt("section", section_); } private: std::optional conceptName_; std::optional conceptVariant_; std::optional stage_; std::optional section_; }; /// @brief Exception raised in the encoder layer. /// /// This exception captures serialized diagnostic state, /// including JSON representations of: /// /// - MARS dictionary /// - Parameter dictionary /// - Options dictionary /// - Encoder configuration /// /// It is intended for high-level failure reporting where /// complete encoding context must be preserved. /// /// The diagnostic information is printed in structured form /// via `printFrame()`. class Mars2GribEncoderException : public Mars2GribGenericException { public: Mars2GribEncoderException(std::string reason, std::string marsDict_json, std::string parDict_json, std::string optDict_json, std::string encoderCfg_json, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2GribGenericException(reason, loc), marsDict_json_(std::move(marsDict_json)), parDict_json_(std::move(parDict_json)), optDict_json_(std::move(optDict_json)), encoderCfg_json_(std::move(encoderCfg_json)) {} const std::string& marsDict_json() const { return marsDict_json_; } const std::string& geoDict_json() const { return geoDict_json_; } const std::string& parDict_json() const { return parDict_json_; } const std::string& optDict_json() const { return optDict_json_; } const std::string& encoderCfg_json() const { return encoderCfg_json_; } void printFrame(const std::string& pad) const override { Mars2GribGenericException::printFrame(pad); LOG_DEBUG_LIB(LibMetkit) << pad << "+ marsDict: " << marsDict_json_ << "\n" << pad << "+ parDict: " << parDict_json_ << "\n" << pad << "+ optDict: " << optDict_json_ << "\n" << pad << "+ encoderCfg: " << encoderCfg_json_ << "\n"; } private: const std::string marsDict_json_; const std::string geoDict_json_; const std::string parDict_json_; const std::string optDict_json_; const std::string encoderCfg_json_; }; /// @brief Recursively print nested exception stack. /// /// Prints: /// /// - Exception type /// - Exception message /// - Nested exceptions (if any) /// /// The structure is indented according to nesting level. /// /// This function does not use the structured frame printer. /// For detailed frames, use `printExtendedStack`. /// /// @param e Root exception /// @param os Output stream /// @param level Initial indentation level inline void printExceptionStack(const std::exception& e, std::ostream& os, std::size_t level = 0) { const std::string indent(level * 2, ' '); // stampa tipo + messaggio os << indent << "- [" << typeid(e).name() << "] " << e.what() << '\n'; // verifica eccezione annidata try { std::rethrow_if_nested(e); } catch (const std::exception& nested) { printExceptionStack(nested, os, level + 1); } catch (...) { os << indent << " - [unknown non-std exception]\n"; } }; inline constexpr int tabSize = 4; inline constexpr int lineSize = 120; inline std::string indent(std::size_t level) { return std::string(level * tabSize, ' '); } /// @brief Print structured exception stack with detailed frames. /// /// For each nested exception frame: /// /// - Prints file, function, line, and message (if available) /// - Prints additional metadata for specialized exceptions /// /// This function detects `Mars2GribGenericException` /// via `dynamic_cast` and calls `printFrame()` /// to extract structured information. /// /// Nested exceptions are recursively printed. /// /// @param e Root exception /// @param level Indentation level /// @param frame Frame counter inline void printExtendedStack(const std::exception& e, std::size_t level = 0, std::size_t frame = 1) { const std::string pad = indent(level); LOG_DEBUG_LIB(LibMetkit) << pad << "+ " << std::string(lineSize, '=') << std::endl << pad << "+ frame " << frame << std::endl << pad << "+ " << std::string(lineSize, '-') << std::endl; if (const auto* me = dynamic_cast(&e)) { me->printFrame(pad); } else { LOG_DEBUG_LIB(LibMetkit) << pad << "+ message: " << e.what() << std::endl; } LOG_DEBUG_LIB(LibMetkit) << pad << "+ " << std::string(lineSize, '+') << std::endl; try { std::rethrow_if_nested(e); } catch (const std::exception& nested) { printExtendedStack(nested, level + 1, frame + 1); } } /// @brief Join a vector of long values into a formatted string. /// /// Output format: /// `{v1, v2, v3}` /// /// Intended for diagnostic message construction. inline std::string joinNumbers(const std::vector& vec) { std::string s{"{"}; for (size_t i = 0; i < vec.size(); ++i) { s += std::to_string(vec[i]); if (i + 1 < vec.size()) { s += ", "; } } s += "}"; return s; } /// @brief Join a vector of double values into a formatted string. /// /// Output format: /// `{v1, v2, v3}` /// /// Intended for diagnostic message construction. inline std::string joinNumbersDouble(const std::vector& vec) { std::string s{"{"}; for (size_t i = 0; i < vec.size(); ++i) { s += std::to_string(vec[i]); if (i + 1 < vec.size()) { s += ", "; } } s += "}"; return s; } /// @brief Rethrow a concept exception with full compile-time metadata. /// /// This macro captures: /// /// - Concept name /// - Variant name /// - Encoding stage /// - Section /// /// and rethrows a `Mars2GribConceptException` while preserving /// the nested exception chain. #define MARS2GRIB_CONCEPT_RETHROW(CONCEPTNAME, MESSAGE) \ std::throw_with_nested(Mars2GribConceptException(std::string(CONCEPTNAME##Name), \ std::string(CONCEPTNAME##TypeName()), \ std::to_string(Stage), std::to_string(Section), MESSAGE, Here())) /// @brief Throw a concept exception with full compile-time metadata. /// /// Same as `MARS2GRIB_CONCEPT_RETHROW`, but without nesting. /// Intended for initial throw sites. #define MARS2GRIB_CONCEPT_THROW(CONCEPTNAME, MESSAGE) \ do { \ throw Mars2GribConceptException(std::string(CONCEPTNAME##Name), std::string(CONCEPTNAME##TypeName()), \ std::to_string(Stage), std::to_string(Section), MESSAGE, Here()); \ } while (0) } // namespace metkit::mars2grib::utils::exceptions metkit-1.19.2/src/metkit/mars2grib/utils/paramMatcher.h0000664000175000017500000000172615232567717023203 0ustar alastairalastair/* * (C) Copyright 2026- 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. */ #pragma once namespace metkit::mars2grib::util::param_matcher { struct Range { int first; int last; bool contains(int x) const { return x >= first && x <= last; } }; inline Range range(int first, int last) { return {first, last}; } inline bool matchSingle(int x, const Range& arg) { return arg.contains(x); } inline bool matchSingle(int x, int y) { return x == y; } template bool matchAny(int value, T... arg) { return (matchSingle(value, arg) || ...); } } // namespace metkit::mars2grib::util::param_matcher metkit-1.19.2/src/metkit/mars2grib/utils/logUtils.h0000664000175000017500000000470315232567717022377 0ustar alastairalastair#pragma once #include "eckit/log/Log.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/generalUtils.h" #define MARS2GRIB_LOG_CHECK(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2GRIB-CHECK: " << msg << std::endl; \ } while (0) #define MARS2GRIB_LOG_MATCH(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2GRIB-MATCH: " << msg << std::endl; \ } while (0) #define MARS2GRIB_LOG_RESOLVE(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2GRIB-RESOLVE: " << msg << std::endl; \ } while (0) #define MARS2GRIB_LOG_OVERRIDE(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2GRIB-OVERRIDE: " << msg << std::endl; \ } while (0) #define MARS2GRIB_LOG_DEFAULT(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2GRIB-DEFAULT: " << msg << std::endl; \ } while (0) #define MARS2GRIB_LOG_CONCEPT(CONCEPTNAME) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2GRIB-CONCEPT:" \ << "[Concept " << std::string(CONCEPTNAME##Name) << "] " \ << "Op called: " \ << "Stage=" << Stage << ", " \ << "Section=" << Section << ", " \ << "Variant=" << std::string(CONCEPTNAME##TypeName()) << std::endl; \ } while (0) metkit-1.19.2/src/metkit/mars2grib/utils/generalUtils.h0000664000175000017500000000010415232567717023222 0ustar alastairalastair#pragma once #define mars2gribUnreachable() __builtin_unreachable()metkit-1.19.2/src/metkit/mars2grib/utils/enableOptions.h0000664000175000017500000000273515232567717023402 0ustar alastairalastair/* * (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. */ #pragma once #include "metkit/mars2grib/api/Options.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::utils { template inline bool checksEnabled(const Options& opt) { using metkit::mars2grib::utils::dict_traits::dict_supports_checks_v; if constexpr (!dict_supports_checks_v) { return false; } return opt.applyChecks; } inline bool skipSection3(const Options& opt) { return opt.skipSection3; } // inline bool skipSection3(const eckit::LocalConfiguration& opt) { // return false; // } inline bool overrideEnabled(const Options& opt) { return opt.enableOverride; } inline bool bitsPerValueCompressionEnabled(const Options& opt) { return opt.enableBitsPerValueCompression; } inline bool normalizeMarsEnabled(const Options& opt) { return opt.normalizeMars; } inline bool normalizeMiscEnabled(const Options& opt) { return opt.normalizeMisc; } inline bool fixMarsGridEnabled(const Options& opt) { return opt.fixMarsGrid; } } // namespace metkit::mars2grib::utils metkit-1.19.2/src/metkit/mars2grib/utils/type_traits_name.h0000664000175000017500000000752315232567717024147 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include "metkit/codes/api/CodesTypes.h" #include "metkit/mars2grib/utils/generalUtils.h" namespace metkit::mars2grib::utils { // ============================================================ // Primary template (deferred to fallback demangling) // ============================================================ template constexpr std::string_view type_name(); // ============================================================ // Base type specializations // ============================================================ template <> constexpr std::string_view type_name() { return "string"; } template <> constexpr std::string_view type_name() { return "bool"; } template <> constexpr std::string_view type_name() { return "char"; } template <> constexpr std::string_view type_name() { return "signed char"; } template <> constexpr std::string_view type_name() { return "unsigned char"; } template <> constexpr std::string_view type_name() { return "int"; } template <> constexpr std::string_view type_name() { return "long"; } template <> constexpr std::string_view type_name() { return "long long"; } template <> constexpr std::string_view type_name() { return "unsigned int"; } template <> constexpr std::string_view type_name() { return "unsigned long"; } template <> constexpr std::string_view type_name() { return "unsigned long long"; } template <> constexpr std::string_view type_name() { return "float"; } template <> constexpr std::string_view type_name() { return "double"; } template <> constexpr std::string_view type_name() { return "long double"; } // ============================================================ // std::vector specializations // ============================================================ template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector_view"; } template <> constexpr std::string_view type_name>() { return "vector_view"; } } // namespace metkit::mars2grib::utils metkit-1.19.2/src/metkit/mars2grib/utils/dictionary_traits/0000775000175000017500000000000015232567717024153 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/utils/dictionary_traits/dictaccess_eckit_configuration.h0000664000175000017500000005074415232567717032551 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include #include #include #include #include "eckit/config/LocalConfiguration.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/type_traits_name.h" // ============================================================================ // Helper macros to reduce boilerplate (similar to LocalConfiguration version) // ============================================================================ // ============================================================== // GET_OR_THROW // ============================================================== #define M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(CTYPE, ISFUNC, GETFUNC) \ template <> \ struct DictGetOrThrow { \ \ static CTYPE get_or_throw(const eckit::LocalConfiguration& cfg, std::string_view key) noexcept(false) { \ const std::string k{key}; \ \ try { \ /* Check key exists */ \ if (!cfg.has(k)) { \ throw exceptions::Mars2GribDictException("Missing key `"s + k + "` in dictionary type `"s + \ std::string(type_name()) + \ "`", \ Here()); \ } \ \ /* Check type */ \ if (hacks::ISFUNC(cfg, k)) { \ return cfg.GETFUNC(k); \ } \ else { \ throw exceptions::Mars2GribDictException( \ "Key `"s + k + "` is not of expected type `"s + std::string(type_name()) + \ "` for dictionary type `"s + std::string(type_name()) + "`", \ Here()); \ } \ } \ catch (const exceptions::Mars2GribGenericException&) { \ throw; \ } \ catch (...) { \ std::throw_with_nested(exceptions::Mars2GribDictException( \ "Internal error while reading key `"s + k + "` as `" + std::string(type_name()) + \ "` from dictionary type `"s + std::string(type_name()) + "`", \ Here())); \ } \ mars2gribUnreachable(); \ } \ }; // ============================================================== // GET_OPT // ============================================================== #define M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(CTYPE, ISFUNC, GETFUNC) \ template <> \ struct DictGetOpt { \ static std::optional get_opt(const eckit::LocalConfiguration& cfg, \ std::string_view key) noexcept(false) { \ const std::string k{key}; \ \ try { \ if (!cfg.has(k)) { \ return std::nullopt; \ } \ \ if (hacks::ISFUNC(cfg, k)) { \ return cfg.GETFUNC(k); \ } \ else { \ return std::nullopt; \ } \ } \ catch (...) { \ return std::nullopt; \ } \ mars2gribUnreachable(); \ } \ }; // ============================================================== // SET_OR_THROW // ============================================================== #define M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(CTYPE, SETFUNC) \ template <> \ struct DictSetOrIgnore { \ static void set_or_ignore(eckit::LocalConfiguration& cfg, std::string_view key, \ const CTYPE& value) noexcept(false) { \ const std::string k{key}; \ \ try { \ cfg.SETFUNC(k, value); \ return; \ } \ catch (...) { \ } \ mars2gribUnreachable(); \ } \ }; // ============================================================== // SET_OR_IGNORE // ============================================================== #define M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(CTYPE, SETFUNC) \ template <> \ struct DictSetOrThrow { \ \ static void set_or_throw(eckit::LocalConfiguration& cfg, std::string_view key, \ const CTYPE& value) noexcept(false) { \ const std::string k{key}; \ \ try { \ cfg.SETFUNC(k, value); \ return; \ } \ catch (const exceptions::Mars2GribGenericException&) { \ throw; \ } \ catch (...) { \ std::throw_with_nested(exceptions::Mars2GribDictException( \ "Unable to set key `"s + k + "` with type `"s + std::string(type_name()) + \ "` in dictionary type `"s + std::string(type_name()) + "`", \ Here())); \ } \ mars2gribUnreachable(); \ } \ }; namespace metkit::mars2grib::utils { // ----------------------------------------------------------------------------- // type_name specialisation // ----------------------------------------------------------------------------- template <> constexpr std::string_view type_name() { return "eckit::LocalConfiguration"; } template <> constexpr std::string_view type_name>() { return "vector"; } } // namespace metkit::mars2grib::utils namespace metkit::mars2grib::utils::dict_traits { using std::operator""s; namespace hacks { inline bool isIntegral(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isIntegral(std::string{key}); } inline bool isFloatingPoint(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isFloatingPoint(std::string{key}) || conf.isIntegral(std::string{key}); } inline bool isBoolean(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isBoolean(std::string{key}); } inline bool isString(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isString(std::string{key}); } inline bool isSubConfiguration(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isSubConfiguration(std::string{key}); } inline bool isIntegralList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isIntegralList(std::string{key}); } inline bool isFloatingPointList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isFloatingPointList(std::string{key}) || conf.isIntegralList(std::string{key}); } inline bool isStringList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isStringList(std::string{key}); } inline bool isSubConfigurationList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isSubConfigurationList(std::string{key}); } } // namespace hacks // ----------------------------------------------------------------------------- // to_json // ----------------------------------------------------------------------------- template <> struct DictToJsonTraits { static std::string to_json(const eckit::LocalConfiguration& cfg) noexcept(true) { try { std::ostringstream os; os << cfg; return os.str(); } catch (...) { return "[to_json failed for eckit::LocalConfiguration]"; } } }; // ----------------------------------------------------------------------------- // DictCreateFromSample / Clone / NeedsChecks // ----------------------------------------------------------------------------- template <> struct DictTraits { static constexpr bool support_checks = false; static std::unique_ptr make_from_sample_or_throw(std::string_view name) { auto cfg = std::make_unique(); cfg->set("SampleName", std::string(name)); return cfg; } static std::unique_ptr clone_or_throw(const eckit::LocalConfiguration& cfg) { return std::make_unique(cfg); } }; // ----------------------------------------------------------------------------- // DictHas // ----------------------------------------------------------------------------- template <> struct DictHas { static bool has(const eckit::LocalConfiguration& cfg, std::string_view key) noexcept(false) { const std::string k{key}; try { return cfg.has(k); } catch (const exceptions::Mars2GribGenericException&) { throw; } catch (...) { std::throw_with_nested(exceptions::Mars2GribDictException( "Internal error while checking presence of key `"s + k + "` in dictionary type `"s + std::string(type_name()) + "`", Here())); } mars2gribUnreachable(); } }; // ============================================================ // eckit::LocalConfiguration specializations via macros // ============================================================ //------------------------------------------------------------------------------ // Scalar types //------------------------------------------------------------------------------ // bool M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(bool, isBoolean, getBool) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(bool, isBoolean, getBool) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(bool, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(bool, set) // int M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(int, isIntegral, getInt) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(int, isIntegral, getInt) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(int, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(int, set) // long M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(long, isIntegral, getLong) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(long, isIntegral, getLong) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(long, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(long, set) // float M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(float, isFloatingPoint, getFloat) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(float, isFloatingPoint, getFloat) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(float, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(float, set) // double M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(double, isFloatingPoint, getDouble) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(double, isFloatingPoint, getDouble) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(double, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(double, set) // std::string M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::string, isString, getString) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::string, isString, getString) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::string, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::string, set) // eckit::LocalConfiguration (sub-configuration) M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(eckit::LocalConfiguration, isSubConfiguration, getSubConfiguration) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(eckit::LocalConfiguration, isSubConfiguration, getSubConfiguration) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(eckit::LocalConfiguration, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(eckit::LocalConfiguration, set) //------------------------------------------------------------------------------ // Vector types //------------------------------------------------------------------------------ // std::vector M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isIntegralList, getIntVector) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isIntegralList, getIntVector) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isIntegralList, getLongVector) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isIntegralList, getLongVector) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isFloatingPointList, getFloatVector) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isFloatingPointList, getFloatVector) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isFloatingPointList, getDoubleVector) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isFloatingPointList, getDoubleVector) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isStringList, getStringVector) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isStringList, getStringVector) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2G_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isSubConfigurationList, getSubConfigurations) M2G_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isSubConfigurationList, getSubConfigurations) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) } // namespace metkit::mars2grib::utils::dict_traits metkit-1.19.2/src/metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h0000664000175000017500000001675715232567717031420 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "metkit/mars2grib/utils/generalUtils.h" // Exceptions #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/type_traits_name.h" namespace metkit::mars2grib::utils::dict_traits { using std::operator""s; template struct dependent_false : std::false_type {}; template struct DictToJsonTraits { static std::string to_json(const Dict&) { return std::string{"[to_json not supported for this dictionary type]"}; } static void dump_or_ignore(const Dict&, const std::string&) { LOG_DEBUG_LIB(LibMetkit) << to_json(std::declval()); } }; template struct DictTraits { static constexpr bool support_checks = false; static std::unique_ptr make_from_sample_or_throw(std::string_view) { static_assert(dependent_false::value, "DictTraits::make_from_sample_or_throw not specialized"); } static std::unique_ptr clone_or_throw(const Dict&) { static_assert(dependent_false::value, "DictTraits::clone_or_throw not specialized"); } }; template struct DictHas { static bool has(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictHas not specialized for this Dict"); mars2gribUnreachable(); } }; template struct DictMissing { static bool isMissing(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictMissing not specialized for this Dict"); mars2gribUnreachable(); } static void setMissing(Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictMissing not specialized for this Dict"); mars2gribUnreachable(); } }; template struct DictGetOpt { static std::optional get_opt(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictGetOpt not specialized for this Dict and type"); mars2gribUnreachable(); } }; template struct DictGetOrThrow { static T get_or_throw(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictGetOrThrow not specialized for this Dict and type"); mars2gribUnreachable(); } }; template struct DictSetOrIgnore { static void set_or_ignore(Dict&, std::string_view, const T&) noexcept(false) { static_assert(dependent_false::value, "DictSetOrIgnore not specialized for this Dict and type"); mars2gribUnreachable(); } }; template struct DictSetOrThrow { static void set_or_throw(Dict&, std::string_view, const T&) noexcept(false) { static_assert(dependent_false::value, "DictSetOrThrow not specialized for this Dict and type"); mars2gribUnreachable(); } }; // ============================================================ // dict_to_json // ============================================================ template std::string dict_to_json(const Dict& d) { return DictToJsonTraits::to_json(d); } // ============================================================ // clone / make_from_sample / needs_checks // ============================================================ template inline constexpr bool dict_supports_checks_v = DictTraits::support_checks; template std::unique_ptr make_from_sample_or_throw(std::string_view name) { return DictTraits::make_from_sample_or_throw(name); } template std::unique_ptr clone_or_throw(const Dict& d) { return DictTraits::clone_or_throw(d); } template void dump_or_ignore(const Dict& d, const std::string& f) { DictToJsonTraits::dump_or_ignore(d, f); } // ============================================================ // has / isMissing / setMissing // ============================================================ // has(dict,key) template inline bool has(const Dict& dict, std::string_view key) { return DictHas::has(dict, key); } // has(dict,key) template inline bool has(const Dict& dict, std::string_view key) { return DictGetOpt::get_opt(dict, key).has_value(); } // isMissing(dict,key) template inline bool isMissing(const Dict& dict, std::string_view key) { return DictMissing::isMissing(dict, key); } // setMissing(dict,key) template inline void setMissing_or_throw(Dict& dict, std::string_view key) { DictMissing::setMissing(dict, key); return; } // check(dict,key,cond) -> bool template inline bool check(const Dict& dict, std::string_view key, Cond&& condition) { if (auto v = DictGetOpt::get_opt(dict, key); v.has_value()) { return std::forward(condition)(*v); } return false; } // ============================================================ // GET UTILITIES // ============================================================ // get_or_throw(dict,key) -> T template inline T get_or_throw(const Dict& dict, std::string_view key) { try { return DictGetOrThrow::get_or_throw(dict, key); } catch (...) { std::throw_with_nested( exceptions::Mars2GribDictException("Forwarding errors while getting key `"s + std::string(key) + "` as `" + std::string(type_name()) + "` from dictionary`"s, Here())); mars2gribUnreachable(); } mars2gribUnreachable(); } // get(dict,key) -> std::optional template inline std::optional get_opt(const Dict& dict, std::string_view key) { try { return DictGetOpt::get_opt(dict, key); } catch (...) { return std::nullopt; } mars2gribUnreachable(); } // ============================================================ // SET UTILITIES // ============================================================ // set(dict,key,value) template inline void set_or_throw(Dict& dict, std::string_view key, const T& value) { try { DictSetOrThrow::set_or_throw(dict, key, value); return; } catch (...) { std::throw_with_nested(exceptions::Mars2GribDictException("Forwarding errors while setting key `"s + std::string(key) + "` as `" + std::string(type_name()) + "` to dictionary`"s, Here())); mars2gribUnreachable(); } mars2gribUnreachable(); } template inline void set_or_ignore(Dict& dict, std::string_view key, const T& value) { try { DictSetOrIgnore::set_or_ignore(dict, key, value); return; } catch (...) { // ignore exceptions mars2gribUnreachable(); } mars2gribUnreachable(); } } // namespace metkit::mars2grib::utils::dict_traits metkit-1.19.2/src/metkit/mars2grib/utils/dictionary_traits/dictaccess_codes_handle.h0000664000175000017500000004674315232567717031137 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include #include #include #include "eckit/io/Buffer.h" #include "metkit/mars2grib/utils/generalUtils.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/codes/api/CodesTypes.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2grib/utils/mars2gribExceptions.h" #include "metkit/mars2grib/utils/type_traits_name.h" // ============================================================================ // Helper macros to reduce boilerplate (similar to LocalConfiguration version) // ============================================================================ // ============================================================== // GET_OR_THROW // ============================================================== #define M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(CTYPE, CHECK_NATIVE, GETFUNC) \ template <> \ struct DictGetOrThrow { \ \ static CTYPE get_or_throw(const metkit::codes::CodesHandle& h, std::string_view key) noexcept(false) { \ const std::string k{key}; \ try { \ /* key exists? */ \ if (!h.isDefined(k)) { \ throw exceptions::Mars2GribDictException( \ "Missing key `"s + k + "` in dictionary `"s + \ std::string(type_name()) + "`", \ Here()); \ } \ \ /* check type */ \ auto t = h.type(k); \ if (!(CHECK_NATIVE)) { \ throw exceptions::Mars2GribDictException( \ "Key `"s + k + "` is not of expected type `"s + std::string(type_name()) + \ "` in dictionary `"s + std::string(type_name()) + "`", \ Here()); \ } \ \ return h.GETFUNC(k); \ } \ catch (const exceptions::Mars2GribGenericException&) { \ throw; \ } \ catch (...) { \ std::throw_with_nested(exceptions::Mars2GribDictException( \ "Internal error while reading key `"s + k + "` as `" + std::string(type_name()) + \ "` from dictionary `"s + std::string(type_name()) + "`", \ Here())); \ } \ mars2gribUnreachable(); \ } \ }; // ============================================================== // GET_OPT // ============================================================== #define M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(CTYPE, CHECK_NATIVE, GETFUNC) \ template <> \ struct DictGetOpt { \ \ static std::optional get_opt(const metkit::codes::CodesHandle& h, \ std::string_view key) noexcept(false) { \ const std::string k{key}; \ try { \ if (!h.isDefined(k)) \ return std::nullopt; \ \ auto t = h.type(k); \ if (!(CHECK_NATIVE)) \ return std::nullopt; \ \ return h.GETFUNC(k); \ } \ catch (...) { \ return std::nullopt; \ } \ mars2gribUnreachable(); \ } \ }; // ============================================================== // SET_OR_THROW // ============================================================== #define M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(CTYPE, SETFUNC) \ template <> \ struct DictSetOrThrow { \ \ static void set_or_throw(metkit::codes::CodesHandle& h, std::string_view key, \ const CTYPE& v) noexcept(false) { \ const std::string k{key}; \ try { \ h.SETFUNC(k, v); \ return; \ } \ catch (const exceptions::Mars2GribGenericException&) { \ throw; \ } \ catch (...) { \ std::throw_with_nested(exceptions::Mars2GribDictException( \ "Unable to set key `"s + k + "` with type `"s + std::string(type_name()) + \ "` in dictionary `"s + std::string(type_name()) + "`", \ Here())); \ } \ mars2gribUnreachable(); \ } \ }; // ============================================================== // SET_OR_IGNORE // ============================================================== #define M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(CTYPE, SETFUNC) \ template <> \ struct DictSetOrIgnore { \ \ static void set_or_ignore(metkit::codes::CodesHandle& h, std::string_view key, \ const CTYPE& v) noexcept(false) { \ const std::string k{key}; \ try { \ h.SETFUNC(k, v); \ return; \ } \ catch (...) { \ return; \ } \ mars2gribUnreachable(); \ } \ }; namespace metkit::mars2grib::utils { // ----------------------------------------------------------------------------- // type_name specialisation // ----------------------------------------------------------------------------- template <> constexpr std::string_view type_name() { return "metkit::codes::CodesHandle"; } } // namespace metkit::mars2grib::utils namespace metkit::mars2grib::utils::dict_traits { using std::operator""s; // ----------------------------------------------------------------------------- // DictToJsonTraits // ----------------------------------------------------------------------------- template <> struct DictToJsonTraits { static std::string to_json(const metkit::codes::CodesHandle& sample) { return std::string{"[to_json not supported for codeHandle this dictionary type]"}; } static void dump_or_ignore(const metkit::codes::CodesHandle& sample, const std::string& fname) { try { eckit::Buffer buf{sample.messageSize()}; sample.copyInto(reinterpret_cast(buf.data()), buf.size()); std::ofstream out(fname, std::ios::binary | std::ios::out); if (!out) { return; // fail silently } out.write(static_cast(buf.data()), buf.size()); out.flush(); // ofstream destructor closes the file } catch (...) { LOG_DEBUG_LIB(LibMetkit) << "dump_or_ignore: unable to dump CodesHandle to file " << fname << std::endl; // nothrow guarantee: swallow everything } } }; // ----------------------------------------------------------------------------- // DictCreateFromSample / Clone / NeedsChecks // ----------------------------------------------------------------------------- template <> struct DictTraits { static constexpr bool support_checks = true; static std::unique_ptr make_from_sample_or_throw(std::string_view name) { auto h = metkit::codes::codesHandleFromSample(std::string(name)); if (!h) { throw eckit::SeriousBug("codesHandleFromSample failed", Here()); } return h; } static std::unique_ptr clone_or_throw(const metkit::codes::CodesHandle& h) { return h.clone(); } }; // ----------------------------------------------------------------------------- // DictHas // ----------------------------------------------------------------------------- template <> struct DictHas { static bool has(const metkit::codes::CodesHandle& h, std::string_view key) noexcept(false) { try { return h.has(std::string(key)); } catch (const exceptions::Mars2GribGenericException&) { throw; } catch (...) { std::throw_with_nested(exceptions::Mars2GribDictException( "Internal error while checking presence of key `"s + std::string(key) + "` in dictionary type `"s + std::string(type_name()) + "`", Here())); } mars2gribUnreachable(); } }; // ----------------------------------------------------------------------------- // DictMissing // ----------------------------------------------------------------------------- template <> struct DictMissing { static bool isMissing(const metkit::codes::CodesHandle& h, std::string_view key) noexcept(false) { try { return h.isMissing(std::string(key)); } catch (...) { std::throw_with_nested(exceptions::Mars2GribDictException( "Internal error while checking missing state of key `"s + std::string(key) + "` in dictionary type `"s + std::string(type_name()) + "`", Here())); } mars2gribUnreachable(); } static void setMissing(metkit::codes::CodesHandle& h, std::string_view key) noexcept(false) { try { h.setMissing(std::string(key)); return; } catch (...) { std::throw_with_nested(exceptions::Mars2GribDictException( "Unable to set key `"s + std::string(key) + "` to missing in dictionary type `"s + std::string(type_name()) + "`", Here())); } mars2gribUnreachable(); } }; // ============================================================================ // SCALAR TYPES // ============================================================================ // bool (ECCODES does not have a native boolean; treat as long {0,1}) M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(bool, (t == metkit::codes::NativeType::Long && h.size(k) == 1), getLong) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(bool, (t == metkit::codes::NativeType::Long && h.size(k) == 1), getLong) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(bool, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(bool, set) // int M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(int, (t == metkit::codes::NativeType::Long && h.size(k) == 1), getLong) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(int, (t == metkit::codes::NativeType::Long && h.size(k) == 1), getLong) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(int, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(int, set) // long M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(long, (t == metkit::codes::NativeType::Long && h.size(k) == 1), getLong) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(long, (t == metkit::codes::NativeType::Long && h.size(k) == 1), getLong) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(long, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(long, set) // double M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(double, (t == metkit::codes::NativeType::Double && h.size(k) == 1), getDouble) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(double, (t == metkit::codes::NativeType::Double && h.size(k) == 1), getDouble) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(double, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(double, set) // string M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(std::string, (t == metkit::codes::NativeType::String), getString) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(std::string, (t == metkit::codes::NativeType::String), getString) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(std::string, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(std::string, set) // ============================================================================ // VECTOR TYPES // ============================================================================ // std::vector M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(std::vector, (t == metkit::codes::NativeType::Long), getLongArray) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(std::vector, (t == metkit::codes::NativeType::Long), getLongArray) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(std::vector, (t == metkit::codes::NativeType::Double), getDoubleArray) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(std::vector, (t == metkit::codes::NativeType::Double), getDoubleArray) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(metkit::codes::Span, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(metkit::codes::Span, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(std::vector, (t == metkit::codes::NativeType::String), getStringArray) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(std::vector, (t == metkit::codes::NativeType::String), getStringArray) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2G_DEFINE_CODESHANDLE_DICT_GET_OR_THROW(std::vector, (t == metkit::codes::NativeType::Bytes), getBytes) M2G_DEFINE_CODESHANDLE_DICT_GET_OPT(std::vector, (t == metkit::codes::NativeType::Bytes), getBytes) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_THROW(std::vector, set) M2G_DEFINE_CODESHANDLE_DICT_SET_OR_IGNORE(std::vector, set) } // namespace metkit::mars2grib::utils::dict_traits metkit-1.19.2/src/metkit/mars2grib/docs/0000775000175000017500000000000015232567717020210 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/docs/diagrams/0000775000175000017500000000000015232567717021777 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/docs/diagrams/concept.svg0000664000175000017500000002702315232567717024157 0ustar alastairalastair concept_model cluster_stages Abstract priority slots cluster_sections GRIB header (abstract sections) concept Concept Canonical, orthogonal representation of one semantic aspect expansion Concept materialization Sparse expansion across: - multiple sections - multiple priority slots (not compact in space nor priority) concept->expansion logical definition note A Concept is logically compact but expands into a sparse, distributed set of key/value assignments in the GRIB header concept->note allocate allocate expansion->allocate preset preset expansion->preset override override expansion->override runtime runtime expansion->runtime secA Section A allocate->secA key/value secC Section C allocate->secC key/value secB Section B preset->secB key/value override->secA key/value secD Section D override->secD key/value runtime->secC key/value metkit-1.19.2/src/metkit/mars2grib/docs/diagrams/architecture.svg0000664000175000017500000002772715232567717025221 0ustar alastairalastair encoder_overall_structure cluster_inputs Inputs cluster_frontend frontend/ (pure functions) cluster_backend backend/ (header construction) cluster_api api/ (orchestration) mars MARS dictionary frontend Frontend (derive EncoderCfg from MARS) mars->frontend input raii RAII Encoder (uses cfg + dictionaries) (build consistent header) mars->raii use par PAR / MISC / EXTRA dictionary par->raii use opt OPT dictionaries opt->raii use vals Values (data payload) api API (call frontend + backend, then encode values, return GRIB message) vals->api input encfg EncoderCfg (sections + ordered concepts) (used once; stored const for debug) frontend->encfg generate encfg->raii instantiate encfg->raii (cacheable design) gribHeader Grib Header (valid GRIB message) (values = 0) raii->gribHeader produce gribHeader->api encode values on grib header outmsg GRIB message (valid header + values) api->outmsg return metkit-1.19.2/src/metkit/mars2grib/docs/diagrams/concept.dot0000664000175000017500000000540215232567717024143 0ustar alastairalastairdigraph concept_model { rankdir=TB; fontsize=10; nodesep=0.6; ranksep=1.0; // ========================================================= // Concept (canonical representation) // ========================================================= concept [ shape=box, style=rounded, label="Concept\n\nCanonical, orthogonal\nrepresentation of\none semantic aspect" ]; // ========================================================= // Abstract expansion: stages x sections // ========================================================= expansion [ shape=box, style=rounded, label="Concept materialization\n\nSparse expansion across:\n- multiple sections\n- multiple priority slots\n\n(not compact in space nor priority)" ]; // ========================================================= // Abstract priority slots // ========================================================= subgraph cluster_stages { label="Abstract priority slots"; style=rounded; allocate [label="allocate"]; preset [label="preset"]; override [label="override"]; runtime [label="runtime"]; { rank=same; allocate; preset; override; runtime; } } // ========================================================= // Abstract GRIB header sections // ========================================================= subgraph cluster_sections { label="GRIB header (abstract sections)"; style=rounded; secA [label="Section A"]; secB [label="Section B"]; secC [label="Section C"]; secD [label="Section D"]; { rank=same; secA; secB; secC; secD; } } // ========================================================= // Relationships // ========================================================= concept -> expansion [label="logical definition"]; expansion -> allocate [style=dashed]; expansion -> preset [style=dashed]; expansion -> override [style=dashed]; expansion -> runtime [style=dashed]; // Sparse, non-uniform contributions allocate -> secA [label="key/value"]; allocate -> secC [label="key/value"]; preset -> secB [label="key/value"]; override -> secA [label="key/value"]; override -> secD [label="key/value"]; runtime -> secC [label="key/value"]; // ========================================================= // Conceptual note (design intent) // ========================================================= note [ shape=note, label="A Concept is logically compact\nbut expands into a sparse,\ndistributed set of key/value\nassignments in the GRIB header" ]; concept -> note [style=dotted, arrowhead=none]; } metkit-1.19.2/src/metkit/mars2grib/docs/diagrams/architecture.dot0000664000175000017500000000632715232567717025201 0ustar alastairalastairdigraph encoder_overall_structure { rankdir=TB; compound=true; fontsize=10; nodesep=0.6; ranksep=1.2; // ========================================================= // Inputs (two rows to reduce horizontal span) // ========================================================= subgraph cluster_inputs { label="Inputs"; style=rounded; // first row mars [label="MARS dictionary"]; par [label="PAR / MISC / EXTRA dictionary"]; // second row opt [label="OPT dictionaries"]; vals [label="Values (data payload)"]; { rank=same; mars; par; } { rank=same; opt; vals; } } // ========================================================= // Frontend (pure functions) // ========================================================= subgraph cluster_frontend { label="frontend/ (pure functions)"; style=rounded; frontend [shape=box, label="Frontend\n(derive EncoderCfg from MARS)"]; encfg [shape=box, label="EncoderCfg\n(sections + ordered concepts)\n(used once; stored const for debug)"]; } // ========================================================= // Backend (RAII encoder) // ========================================================= subgraph cluster_backend { label="backend/ (header construction)"; style=rounded; raii [shape=box, label="RAII Encoder\n(uses cfg + dictionaries)\n(build consistent header)"]; gribHeader [shape=box, label="Grib Header\n(valid GRIB message)\n(values = 0)"]; } // ========================================================= // API (orchestration) // ========================================================= subgraph cluster_api { label="api/ (orchestration)"; style=rounded; api [shape=box, label="API\n(call frontend + backend,\nthen encode values,\nreturn GRIB message)"]; outmsg [shape=box, label="GRIB message\n(valid header + values)"]; } // --------------------------------------------------------- // Main vertical flow // --------------------------------------------------------- mars -> frontend [label="input"]; frontend -> encfg [label="generate"]; encfg -> raii [label="instantiate"]; raii -> gribHeader [label="produce"]; gribHeader -> api [label="encode values on grib header"]; api -> outmsg [label="return"]; // --------------------------------------------------------- // Backend dictionary usage (orthogonal inputs) // --------------------------------------------------------- mars -> raii [label="use", style=dashed]; par -> raii [label="use", style=dashed]; opt -> raii [label="use", style=dashed]; // Values applied only at API level vals -> api [label="input"]; // --------------------------------------------------------- // Design intent (future caching) // --------------------------------------------------------- encfg -> raii [label="(cacheable design)", style=dotted, arrowhead=none]; } metkit-1.19.2/src/metkit/mars2grib/docs/doxygen/0000775000175000017500000000000015232567717021665 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/docs/doxygen/mars2grib_architecture.md0000664000175000017500000001174115232567717026645 0ustar alastairalastair@page architecture_page Encoder Architecture @section arch_scope Scope and Inputs The Encoder is designed to operate on a minimal and well-defined set of inputs. The user is expected to provide: - a **MARS dictionary** - a **miscellaneous dictionary** (hereafter referred to as *misc dictionary*) No additional dictionaries are required or assumed. All information required to determine the encoded product structure is derived from the inputs listed above and from internal compile-time rules. @section arch_mars_dictionary The MARS Dictionary The MARS dictionary is the primary semantic input of the Encoder. In addition to classical MARS metadata, the dictionary is **extended** and may contain post-processing related keywords, including (but not limited to): - grid - truncation - packing These extended keywords are treated as first-class inputs and participate directly in the encoding logic. @section arch_split Frontend and Backend Separation The Encoder is logically split into two distinct components: @subsection arch_frontend Frontend The frontend: - depends **only** on the MARS dictionary - performs no encoding - produces an intermediate object called **EncoderConfiguration** The frontend is responsible for interpreting the semantic intent expressed by the MARS dictionary and mapping it to an internal structural description. @subsection arch_backend Backend The backend: - depends on the MARS dictionary - depends on the misc dictionary - depends on the EncoderConfiguration produced by the frontend The backend performs the actual encoding by executing the encoding logic described by the configuration. @image html architecture.svg "General overview of the encoder architecture" @section arch_encoder_configuration EncoderConfiguration The EncoderConfiguration is a complete, self-contained description of the **structure** of the encoded header. Here, *structure* refers to: - memory layout - presence of keywords - association between sections and concepts The EncoderConfiguration is specified as follows. @subsection arch_sections Sections and Template Numbers For each encoding section, a template number is provided. - For sections 2, 3, 4, and 5, the template number is semantically relevant. - For all other sections, the template number is present but effectively a dummy value, as only a single valid template exists. @subsection arch_concept_variants Concept Variants per Section For each section, the EncoderConfiguration contains an ordered list of `Concept::Variant` entries. Each entry identifies: - the concept - the concrete variant of that concept to be used in that section @section arch_configuration_construction Construction of EncoderConfiguration The EncoderConfiguration is constructed by combining: - information extracted from the MARS dictionary - compile-time information provided by recipes The construction process follows a strict and deterministic workflow. @subsection arch_step1 Concept Activation From the MARS dictionary: - the set of **active concepts** is determined - for each active concept, a **version** is deduced Inactive concepts are excluded at this stage. @subsection arch_step2 Template Determination The set of active `(Concept, Version)` pairs is used to determine: - the template number for each relevant section This step must result in a unique and consistent set of template numbers. @subsection arch_step3 Recipe Completion Once template numbers are known, recipes are used to complete the EncoderConfiguration. For each `(section, template number)` pair, a recipe provides a list of `(Concept, Variant)` pairs. The following rules apply: - If a variant is **not explicitly specified** in the recipe, it is assumed to be `Default`. - If a variant is explicitly specified in the recipe, it is **mandatory**. A mismatch with the variant deduced from the MARS dictionary results in an error. - If the recipe specifies `Default`, the variant deduced from the MARS dictionary may override it. @section arch_dispatch Dispatch Table Construction Once the EncoderConfiguration is finalized, a three-dimensional table of function pointers is constructed. The table is indexed as: - **Stage** - **Section** - **Concept** The table is inferred **exclusively** from the EncoderConfiguration. No additional runtime decisions are required. During construction, **all null entries are explicitly eliminated**. Only valid `(Stage, Section, Concept)` combinations are inserted. As a result, the dispatch table is **dense** and contains only callable function pointers. @section arch_execution Encoding Execution Encoding is performed by iterating over the dispatch table. - for each stage: - for each section: - all function pointers associated with that stage and section are invoked in deterministic order Because the dispatch table contains no null entries: - execution is purely linear over the precomputed table All semantic decisions are completed prior to execution, during the construction of the EncoderConfiguration and the dispatch table. metkit-1.19.2/src/metkit/mars2grib/docs/doxygen/mars2grib_overview.md0000664000175000017500000000317315232567717026031 0ustar alastairalastair@mainpage Encoder Architecture Overview @tableofcontents @section intro_overview Overview This documentation describes the architecture of the Encoder: a modular, concept-driven system designed to construct encoded products (e.g. GRIB) from high-level semantic information. The Encoder is not a monolithic procedure. Instead, it is built as a composition of independent, orthogonal units—called *concepts*—that collectively describe *what* must be encoded, while the Encoder framework determines *how* this information is mapped to a concrete binary representation. The primary design goals are: - Separation of concerns between *semantics* and *encoding mechanics* - Compile-time validation of encoder configurations - Extensibility without modification of existing code paths - Deterministic and reproducible encoding behaviour @section intro_audience Intended Audience This documentation targets: - Developers extending or maintaining the Encoder - Reviewers validating correctness and design choices - Advanced users integrating the Encoder into production workflows Familiarity with modern C++ (templates, constexpr, type traits) and encoded data formats (e.g. GRIB) is assumed. @section intro_structure Documentation Structure The documentation is organised as follows: - @ref architecture_page : High-level architecture and data flow - @ref concepts_page : Definition and role of concepts - @ref stages_page : Encoding stages and execution model - @ref recipes_page : Template selection and validation Each section builds progressively from abstract principles to concrete implementation details. metkit-1.19.2/src/metkit/mars2grib/docs/doxygen/mars2grib.config.in0000664000175000017500000044426315232567717025366 0ustar alastairalastair# Doxyfile 1.13.2 # This file describes the settings to be used by the documentation system # Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). # # Note: # # Use Doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] # Use Doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "mars2grib" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = 0.0.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewers a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # With the PROJECT_ICON tag one can specify an icon that is included in the tabs # when the HTML document is shown. Doxygen will copy the logo to the output # directory. PROJECT_ICON = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = . # If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format # and will distribute the generated files over these directories. Enabling this # option can be useful when feeding Doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise cause # performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to # control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = YES # Controls the number of sub-directories that will be created when # CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every # level increment doubles the number of directories, resulting in 4096 # directories at level 8 which is the default and also the maximum value. The # sub-directories are organized in 2 levels, the first level always has a fixed # number of 16 directories. # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. CREATE_SUBDIRS_LEVEL = 8 # If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by Doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English # (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, # Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with # English messages), Korean, Korean-en (Korean with English messages), Latvian, # Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, # Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, # Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which Doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where Doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = @MARS2GRIB_SOURCE_DIRECTORY@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but # less readable) file names. This can be useful if your file system doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the # first line (until the first dot, question mark or exclamation mark) of a # Javadoc-style comment as the brief description. If set to NO, the Javadoc- # style will behave just like regular Qt-style comments (thus requiring an # explicit @brief command for a brief description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by Doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first # line (until the first dot, question mark or exclamation mark) of a Qt-style # comment as the brief description. If set to NO, the Qt-style will behave just # like regular Qt-style comments (thus requiring an explicit \brief command for # a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and Doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # Doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as Doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". Note that you cannot put \n's in the value part of an alias # to insert newlines (in the resulting output). You can put ^^ in the value part # of an alias to insert a newline as if a physical newline was in the original # file. When you need a literal { or } or , in the value part of an alias you # have to escape them by means of a backslash (\), this can lead to conflicts # with the commands \{ and \} for these it is advised to use the version @{ and # @} or use a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make Doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by Doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by Doxygen, so you can # mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 6. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 6 # The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to # generate identifiers for the Markdown headings. Note: Every identifier is # unique. # Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a # sequence number starting at 0 and GITHUB use the lower case version of title # with any whitespace replaced by '-' and punctuation characters removed. # The default value is: DOXYGEN. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. MARKDOWN_ID_STYLE = DOXYGEN # When enabled Doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. Words listed in the # AUTOLINK_IGNORE_WORDS tag are excluded from automatic linking. # The default value is: YES. AUTOLINK_SUPPORT = YES # This tag specifies a list of words that, when matching the start of a word in # the documentation, will suppress auto links generation, if it is enabled via # AUTOLINK_SUPPORT. This list does not affect affect links explicitly created # using \# or the \link or commands. # This tag requires that the tag AUTOLINK_SUPPORT is set to YES. AUTOLINK_IGNORE_WORDS = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let Doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also makes the inheritance and # collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software) sources only. Doxygen will parse # them like normal C++ but will assume all classes use public instead of private # inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # Doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then Doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small # Doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run Doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use # during processing. When set to 0 Doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 # If the TIMESTAMP tag is set different from NO then each generated page will # contain the date or date and time when the page was generated. Setting this to # NO can help when comparing the output of multiple runs. # Possible values are: YES, NO, DATETIME and DATE. # The default value is: NO. TIMESTAMP = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = YES # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # will also hide undocumented C++ concepts if enabled. This option has no effect # if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_UNDOC_NAMESPACES tag is set to YES, Doxygen will hide all # undocumented namespaces that are normally visible in the namespace hierarchy. # If set to NO, these namespaces will be included in the various overviews. This # option has no effect if EXTRACT_ALL is enabled. # The default value is: YES. HIDE_UNDOC_NAMESPACES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES Doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and macOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # Possible values are: SYSTEM, NO and YES. # The default value is: SYSTEM. CASE_SENSE_NAMES = SYSTEM # If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_HEADERFILE tag is set to YES then the documentation for a class # will show which file needs to be included to use the class. # The default value is: YES. SHOW_HEADERFILE = YES # If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # Doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by Doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by Doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents Doxygen's defaults, run Doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. See also section "Changing the # layout of pages" for information. # # Note that if you run Doxygen from a directory containing a file called # DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = # The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH # environment variable) so that external tools such as latex and gs can be # found. # Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the # path already specified by the PATH variable, and are added in the order # specified. # Note: This option is particularly useful for macOS version 14 (Sonoma) and # higher, when running Doxygen from Doxywizard, because in this case any user- # defined changes to the PATH are ignored. A typical example on macOS is to set # EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin # together with the standard path, the full search path used by doxygen when # launching external tools will then become # PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin EXTERNAL_TOOL_PATH = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by Doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in # a documented function twice, or documenting parameters that don't exist or # using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete # function parameter documentation. If set to NO, Doxygen will accept that some # parameters have no documentation without warning. # The default value is: YES. WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, Doxygen will only warn about wrong parameter # documentation, but not about the absence of documentation. If EXTRACT_ALL is # set to YES then this flag will automatically be disabled. See also # WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO # If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about # undocumented enumeration values. If set to NO, Doxygen will accept # undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: NO. WARN_IF_UNDOC_ENUM_VAL = NO # If WARN_LAYOUT_FILE option is set to YES, Doxygen will warn about issues found # while parsing the user defined layout file, such as missing or wrong elements. # See also LAYOUT_FILE for details. If set to NO, problems with the layout file # will be suppressed. # The default value is: YES. WARN_LAYOUT_FILE = YES # If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the Doxygen process Doxygen will return with a non-zero status. # If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves # like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not # write the warning messages in between other messages but write them at the end # of a run, in case a WARN_LOGFILE is defined the warning messages will be # besides being in the defined file also be shown at the end of a run, unless # the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case # the behavior will remain as with the setting FAIL_ON_WARNINGS. # Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # In the $text part of the WARN_FORMAT command it is possible that a reference # to a more specific place is given. To make it easier to jump to this place # (outside of Doxygen) the user can define a custom "cut" / "paste" string. # Example: # WARN_LINE_FORMAT = "'vi $file +$line'" # See also: WARN_FORMAT # The default value is: at line $line of file $file. WARN_LINE_FORMAT = "at line $line of file $file" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). In case the file specified cannot be opened for writing the # warning and error messages are written to standard error. When as file - is # specified the warning and error messages are written to standard output # (stdout). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = \@MARS2GRIB_SOURCE_DIRECTORY@/utils/configConverter.h \ @MARS2GRIB_SOURCE_DIRECTORY@/utils/dictionary_traits/dictaccess_codes_handle.h \ @MARS2GRIB_SOURCE_DIRECTORY@/utils/dictionary_traits/dictionary_access_traits.h \ @MARS2GRIB_SOURCE_DIRECTORY@/utils/dictionary_traits/dictaccess_eckit_configuration.h \ @MARS2GRIB_SOURCE_DIRECTORY@/utils/type_traits_name.h \ @MARS2GRIB_SOURCE_DIRECTORY@/utils/mars2gribExceptions.h \ @MARS2GRIB_SOURCE_DIRECTORY@/utils/logUtils.h \ @MARS2GRIB_SOURCE_DIRECTORY@/utils/enableOptions.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/origin/originMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/origin/originConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/origin/originEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/origin/originEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/reference-time/referenceTimeEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/reference-time/referenceTimeConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/reference-time/referenceTimeMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/reference-time/referenceTimeEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/data-type/dataTypeEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/data-type/dataTypeMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/data-type/dataTypeConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/data-type/dataTypeEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/shape-of-the-earth/shapeOfTheEarthMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/shape-of-the-earth/shapeOfTheEarthConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/shape-of-the-earth/shapeOfTheEarthEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/shape-of-the-earth/shapeOfTheEarthEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/satellite/satelliteMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/satellite/satelliteEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/satellite/satelliteEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/satellite/satelliteConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/GeneralRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/EncodingCallbacksRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/longrange/longrangeEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/longrange/longrangeEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/longrange/longrangeConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/longrange/longrangeMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/derived/derivedEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/derived/derivedMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/derived/derivedConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/derived/derivedEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/nil/nilEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/nil/nilEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/nil/nilMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/nil/nilConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/packing/packingConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/packing/packingMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/packing/packingEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/packing/packingEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/mars/marsMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/mars/marsEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/mars/marsConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/mars/marsEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/composition/compositionMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/composition/compositionEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/composition/compositionEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/composition/compositionConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/level/levelConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/level/levelEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/level/levelEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/level/levelMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/tables/tablesConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/tables/tablesMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/tables/tablesEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/tables/tablesEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/generating-process/generatingProcessEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/generating-process/generatingProcessConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/generating-process/generatingProcessMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/generating-process/generatingProcessEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/MatchingCallbacksRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/destine/destineEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/destine/destineMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/destine/destineEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/destine/destineConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/statistics/statisticsEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/statistics/statisticsEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/statistics/statisticsMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/statistics/statisticsConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/wave/waveConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/wave/waveEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/wave/waveMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/wave/waveEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/AllConcepts.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/analysis/analysisMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/analysis/analysisEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/analysis/analysisConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/analysis/analysisEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/point-in-time/pointInTimeEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/point-in-time/pointInTimeMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/point-in-time/pointInTimeEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/point-in-time/pointInTimeConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/ensemble/ensembleMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/ensemble/ensembleEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/ensemble/ensembleEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/ensemble/ensembleConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/representation/representationEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/representation/representationConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/representation/representationMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/representation/representationEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/param/paramMatcher.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/param/paramEncoding.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/param/paramEnum.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/concepts/param/paramConceptDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/compile-time-registry-engine/makePhaseCallbacksRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/compile-time-registry-engine/RegisterEntryDescriptor.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/compile-time-registry-engine/utils.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/compile-time-registry-engine/EntryVariantRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/compile-time-registry-engine/common.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/compile-time-registry-engine/makeVariantCallbacksRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/compile-time-registry-engine/makeEntryCallbacksRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/productionStatusOfProcessedData.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/generation.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/typeOfProcessedData.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/backgroundProcess.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/waveFrequencyGrid.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/scaleFactorOfCentralWaveNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/paramId.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/methodNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/typeOfGeneratingProcess.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/allowedReferenceValue.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/activity.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/periodItMin.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/realization.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/significanceOfReferenceTime.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/perturbationNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/localTablesVersion.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/model.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/waveFrequencyNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/shapeOfTheEarth.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/typeOfEnsembleForecast.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/pvArray.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/detail/pv_137_be.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/detail/ProductTime.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/detail/StatType.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/detail/StatisticalWindow.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/dataset.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/lengthOfTimeWindow.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/scaledValueOfCentralWaveNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/stream.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/expver.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/subCentre.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/tablesVersion.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/bitsPerValue.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/productTime.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/typeOfStatisticalProcessing.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/systemNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/resolution.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/experiment.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/class.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/subSetTrunc.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/timeIncrementInSeconds.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/laplacianOperator.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/channel.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/chemId.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/firstWavelength.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/level.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/instrumentType.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/derivedForecast.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/satelliteSeries.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/waveDirectionNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/centre.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/waveDirectionGrid.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/periodItMax.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/generatingProcessIdentifier.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/offsetToEndOf4DvarWindow.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/numberOfForecastsInEnsemble.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/numberOfFrequencies.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/type.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/deductions/satelliteNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/matchDataRepresentationTemplateNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/matchProductDefinitionTemplateNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/checkLocalUseSection.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/matchGridDefinitionTemplateNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/matchDataset.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/checkDestinELocalSection.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/checkEnsembleProductDefinitionSection.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/matchLocalDefinitionNumber.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/checkDerivedProductDefinitionSection.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/checks/checkStatisticsProductDefinitionSection.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/SectionLayoutData.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/Recipes.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/TemplateSignatureKey.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/dsl.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/Select.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/Recipe.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/ActiveConceptsData.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/ResolvedTemplateData.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/CompressionMask.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/resolver/SectionTemplateSelector.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/initializers/sectionInitializer2.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/initializers/sectionInitializer5.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/initializers/sectionInitializerCore.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/initializers/sectionInitializer4.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/initializers/sectionInitializer0.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/initializers/sectionInitializer1.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/initializers/sectionInitializer3.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/sections/initializers/sectionRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/productionStatusOfProcessedData.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/typeOfProcessedData.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/backgroundProcess.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/typeOfStatisticalProcessing.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/typeOfGeneratingProcess.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/significanceOfReferenceTime.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/timeUnits.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/typeOfEnsembleForecast.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/typeOfTimeIntervals.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/derivedForecast.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/shapeOfTheReferenceSystem.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/tables/typeOfInterval.h \ @MARS2GRIB_SOURCE_DIRECTORY@/backend/encodeValues.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/GribHeaderLayoutData.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/make_HeaderLayout.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/resolveActiveConcepts.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/resolveSectionsLayout.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/section-recipes/impl/section1Recipes.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/section-recipes/impl/section3Recipes.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/section-recipes/impl/section5Recipes.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/section-recipes/impl/section2Recipes.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/section-recipes/impl/section4Recipes.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/section-recipes/impl/section0Recipes.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/resolution/section-recipes/SectionTemplateSelectors.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/header/EncodingPlan.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/header/SpecializedEncoder.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/normalization.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/normalizeMarsDict.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/normalizeMiscDict.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/SanitizationRegistry.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/generation.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/param.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/ident.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/activity.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/hdate.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/stattype.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/anoffset.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/levtype.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/realization.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/model.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/timespan.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/packing.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/dataset.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/htime.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/stream.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/expver.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/time.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/resolution.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/All.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/experiment.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/class.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/channel.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/system.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/method.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/level.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/origin.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/direction.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/wavelength.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/number.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/date.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/frequency.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/domain.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/truncation.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/instrument.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/grid.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/levelist.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/type.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/chem.h \ @MARS2GRIB_SOURCE_DIRECTORY@/frontend/normalization/per_key/mars/step.h \ @MARS2GRIB_SOURCE_DIRECTORY@/api/Options.h \ @MARS2GRIB_SOURCE_DIRECTORY@/api/Mars2Grib.h \ @MARS2GRIB_SOURCE_DIRECTORY@/CoreOperations.h \ @MARS2GRIB_SOURCE_DIRECTORY@/api/Mars2Grib.cc \ @MARS2GRIB_SOURCE_DIRECTORY@/api/pymars2grib/mars2grib_core.cc # This tag can be used to specify the character encoding of the source files # that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # This tag can be used to specify the character encoding of the source files # that Doxygen parses. The INPUT_FILE_ENCODING tag can be used to specify # character encoding on a per file pattern basis. Doxygen will compare the file # name with each pattern and apply the encoding instead of the default # INPUT_ENCODING if there is a match. The character encodings are a list of the # form: pattern=encoding (like *.php=ISO-8859-1). # See also: INPUT_ENCODING for further information on supported encodings. INPUT_FILE_ENCODING = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by Doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, # *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, # *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, # *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to # be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h *.cc # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which Doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = @MARS2GRIB_SOURCE_DIRECTORY@/mars2grib/docs/diagrams # The INPUT_FILTER tag can be used to specify a program that Doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that Doxygen will use the data processed and written to standard output # for further processing, therefore nothing else, like debug statements or used # commands (so in case of a Windows batch file always use @echo OFF), should be # written to standard output. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by Doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by Doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the Doxygen output. USE_MDFILE_AS_MAINPAGE = # If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- # directories of the project's root, is used as the documentation for that sub- # directory, except when the README.md starts with a \dir, \page or \mainpage # command. If set to NO, the README.md file needs to start with an explicit \dir # command in order to be used as directory documentation. # The default value is: YES. IMPLICIT_DIR_DOCS = YES # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common # extension is to allow longer lines before the automatic comment starts. The # setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can # be processed before the automatic comment starts. # Minimum value: 7, maximum value: 10000, default value: 72. FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # multi-line macros, enums or list initialized variables directly into the # documentation. # The default value is: NO. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of Doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by Doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) # that should be ignored while generating the index headers. The IGNORE_PREFIX # tag works for classes, function and member names. The entity will be placed in # the alphabetical list under the first letter of the entity name that remains # after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank Doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that Doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that Doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of Doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank Doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank Doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that Doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by Doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). # Note: Since the styling of scrollbars can currently not be overruled in # Webkit/Chromium, the styling will be left out of the default doxygen.css if # one or more extra stylesheets have been specified. So if scrollbar # customization is desired it has to be added explicitly. For an example see the # documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. # Possible values are: LIGHT always generates light mode output, DARK always # generates dark mode output, AUTO_LIGHT automatically sets the mode according # to the user preference, uses light mode if no preference is set (the default), # AUTO_DARK automatically sets the mode according to the user preference, uses # dark mode if no preference is set and TOGGLE allows a user to switch between # light and dark mode via a button. # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE = AUTO_LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be # dynamically folded and expanded in the generated HTML source code. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_CODE_FOLDING = YES # If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in # the top right corner of code and text fragments that allows the user to copy # its content to the clipboard. Note this only works if supported by the browser # and the web page is served via a secure context (see: # https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: # protocol. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COPY_CLIPBOARD = YES # Doxygen stores a couple of settings persistently in the browser (via e.g. # cookies). By default these settings apply to all HTML pages generated by # Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store # the settings under a project specific key, such that the user preferences will # be stored separately. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_PROJECT_COOKIE = # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 9999 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, Doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag determines the URL of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDURL = # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # on Windows. In the beginning of 2021 Microsoft took the original page, with # a.o. the download links, offline (the HTML help workshop was already many # years in maintenance mode). You can download the HTML help workshop from the # web archives at Installation executable (see: # http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo # ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # Doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # The SITEMAP_URL tag is used to specify the full URL of the place where the # generated documentation will be placed on the server by the user during the # deployment of the documentation. The generated sitemap is called sitemap.xml # and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL # is specified no sitemap is generated. For information about the sitemap # protocol see https://www.sitemaps.org # This tag requires that the tag GENERATE_HTML is set to YES. SITEMAP_URL = # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine tune the look of the index (see "Fine-tuning the output"). As an # example, the default style sheet generated by Doxygen has an example that # shows how to put an image at the root of the tree instead of the PROJECT_NAME. # Since the tree basically has the same information as the tab index, you could # consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES # When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the # FULL_SIDEBAR option determines if the side bar is limited to only the treeview # area (value NO) or if it should extend to the full height of the window (value # YES). Setting this to YES gives a layout similar to # https://docs.readthedocs.io with more room for contents, but less room for the # project logo, title, and description. If either GENERATE_TREEVIEW or # DISABLE_INDEX is set to NO, this option has no effect. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. FULL_SIDEBAR = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # Doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # When the SHOW_ENUM_VALUES tag is set doxygen will show the specified # enumeration values besides the enumeration mnemonics. # The default value is: NO. SHOW_ENUM_VALUES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email # addresses. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. OBFUSCATE_EMAILS = YES # If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # Doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = YES # With MATHJAX_VERSION it is possible to specify the MathJax version to be used. # Note that the different versions of MathJax have different requirements with # regards to the different settings, so it is possible that also other MathJax # settings have to be changed when switching between the different MathJax # versions. # Possible values are: MathJax_2 and MathJax_3. # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for # the MathJax output. For more details about the output format see MathJax # version 2 (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 # (see: # http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best # compatibility. This is the name for Mathjax version 2, for MathJax version 3 # this will be translated into chtml), NativeMML (i.e. MathML. Only supported # for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This # is the name for Mathjax version 3, for MathJax version 2 this will be # translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be /ec/res4/hpcperm/mavm/ba/metkit-bundle/source/metkit/src/metkit/mars2grib/mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. The default value is: # - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 # - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # for MathJax version 2 (see # https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # For example for MathJax version 3 (see # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): # MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled Doxygen will generate a search box for # the HTML output. The underlying search engine uses JavaScript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the JavaScript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /Node, # Edge and Graph Attributes specification You need to make sure dot is able # to find the font, which can be done by putting it in a standard location or by # setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. Default graphviz fontsize is 14. # The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" # DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can # add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about # arrows shapes. # The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" # DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes # around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification # The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" # You can set the path where dot can find font specified with fontname in # DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = # If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will # generate a graph for each documented class showing the direct and indirect # inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and # HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case # the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the # CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. # If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance # relations will be shown as texts / links. Explicit enabling an inheritance # graph or choosing a different representation for an inheritance graph of a # specific class, can be accomplished by means of the command \inheritancegraph. # Disabling an inheritance graph can be accomplished by means of the command # \hideinheritancegraph. # Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the # class with other documented classes. Explicit enabling a collaboration graph, # when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the # command \collaborationgraph. Disabling a collaboration graph can be # accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for # groups, showing the direct groups dependencies. Explicit enabling a group # dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means # of the command \groupgraph. Disabling a directory graph can be accomplished by # means of the command \hidegroupgraph. See also the chapter Grouping in the # manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. UML_LOOK = YES # If the UML_LOOK tag is enabled, the fields and methods are shown inside the # class node. If there are many fields or methods and many nodes the graph may # become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the # number of items for each type to make the size more manageable. Set this to 0 # for no limit. Note that the threshold may be exceeded by 50% before the limit # is enforced. So when you set the threshold to 10, up to 15 fields may appear, # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. # This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 # If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and # methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS # tag is set to YES, Doxygen will add type and arguments for attributes and # methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen # will not generate fields with class member information in the UML graphs. The # class diagrams will look similar to the default class diagrams but using UML # notation for the relationships. # Possible values are: NO, YES and NONE. # The default value is: NO. # This tag requires that the tag UML_LOOK is set to YES. DOT_UML_DETAILS = YES # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # to display on a single line. If the actual line length exceeds this threshold # significantly it will be wrapped across multiple lines. Some heuristics are # applied to avoid ugly line breaks. # Minimum value: 0, maximum value: 1000, default value: 17. # This tag requires that the tag HAVE_DOT is set to YES. DOT_WRAP_THRESHOLD = 17 # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. TEMPLATE_RELATIONS = YES # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then Doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented # files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, # can be accomplished by means of the command \includegraph. Disabling an # include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then Doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented # files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set # to NO, can be accomplished by means of the command \includedbygraph. Disabling # an included by graph can be accomplished by means of the command # \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH tag is set to YES then Doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. Disabling a call graph can be # accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. CALL_GRAPH = YES # If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. Disabling a caller graph can be # accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the # files in the directories. Explicit enabling a directory graph, when # DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command # \directorygraph. Disabling a directory graph can be accomplished by means of # the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES # The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels # of child directories generated in directory dependency graphs by dot. # Minimum value: 1, maximum value: 25, default value: 1. # This tag requires that the tag DIRECTORY_GRAPH is set to YES. DIR_GRAPH_MAX_DEPTH = 25 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: # https://www.graphviz.org/)). # # Note the formats svg:cairo and svg:cairo:cairo cannot be used in combination # with INTERACTIVE_SVG (the INTERACTIVE_SVG will be set to NO). # Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus, # png:gdiplus:gdiplus, svg:cairo, svg:cairo:cairo, svg:svg, svg:svg:core, # gif:cairo, gif:cairo:gd, gif:cairo:gdiplus, gif:gdiplus, gif:gdiplus:gdiplus, # gif:gd, gif:gd:gd, jpg:cairo, jpg:cairo:gd, jpg:cairo:gdiplus, jpg:gd, # jpg:gd:gd, jpg:gdiplus and jpg:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg or svg:svg or svg:svg:core, then this option # can be set to YES to enable generation of interactive SVG images that allow # zooming and panning. # # Note that this requires a modern browser other than Internet Explorer. Tested # and working are Firefox, Chrome, Safari, and Opera. # # Note This option will be automatically disabled when DOT_IMAGE_FORMAT is set # to svg:cairo or svg:cairo:cairo. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. INTERACTIVE_SVG = YES # The DOT_PATH tag can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. # This tag requires that the tag HAVE_DOT is set to YES. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the \dotfile # command). # This tag requires that the tag HAVE_DOT is set to YES. DOTFILE_DIRS = # You can include diagrams made with dia in Doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. # If left empty dia is assumed to be found in the default search path. DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile # command). DIAFILE_DIRS = # When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file or to the filename of jar file # to be used. If left blank, it is assumed PlantUML is not used or called during # a preprocessing step. Doxygen will generate a warning when it encounters a # \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = # When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a # configuration file for PlantUML. PLANTUML_CFG_FILE = # When using PlantUML, the specified paths are searched for files specified by # the !include statement in a PlantUML block. PLANTUML_INCLUDE_PATH = # The PLANTUMLFILE_DIRS tag can be used to specify one or more directories that # contain PlantUml files that are included in the documentation (see the # \plantumlfile command). PLANTUMLFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, Doxygen will truncate the graph, which is visualized # by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. # Minimum value: 0, maximum value: 10000, default value: 50. # This tag requires that the tag HAVE_DOT is set to YES. DOT_GRAPH_MAX_NODES = 1000 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs # generated by dot. A depth value of 3 means that only nodes reachable from the # root by following a path via at most 3 edges will be shown. Nodes that lay # further from the root node will be omitted. Note that setting this option to 1 # or 2 may greatly reduce the computation time needed for large code bases. Also # note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. # Minimum value: 0, maximum value: 1000, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. # Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal # graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate # files that are used to generate the various graphs. # # Note: This setting is not only used for dot files but also for msc temporary # files. # The default value is: YES. DOT_CLEANUP = YES # You can define message sequence charts within Doxygen comments using the \msc # command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will # use a built-in version of mscgen tool to produce the charts. Alternatively, # the MSCGEN_TOOL tag can also specify the name an external tool. For instance, # specifying prog as the value, Doxygen will call the tool as prog -T # -o . The external tool should support # output file formats "png", "eps", "svg", and "ismap". MSCGEN_TOOL = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the \mscfile # command). MSCFILE_DIRS = metkit-1.19.2/src/metkit/mars2grib/docs/doxygen/mars2grib_concepts.md0000664000175000017500000001255215232567717026002 0ustar alastairalastair@page concepts_page Concepts @section concepts_definition Definition of a Concept A **Concept** is the fundamental semantic unit of the Encoder. A concept represents an orthogonal piece of information required to describe an encoded product. Concepts are the only entities that **set values in the GRIB header**. @image html concept.svg "General idea of a concept" Concepts do not decide *what* values to set. They only decide *where* and *how* values are written. All value resolution is delegated to deductions. @section concepts_compile_time Compile-Time Registration Model Concepts are registered entirely at compile time. Each registered entity is a **pair** composed of: 1. a **key**, identifying the semantic capability 2. a **value**, representing the executable contributions @subsection concepts_registration_key Registration Key The registration key is: Concept::Variant Each `(Concept, Variant)` pair represents a distinct semantic realization and is treated as an independent entity by the Encoder. @subsection concepts_new_concept_or_variant New Concept or New Variant Before changing the concept system, decide whether the behavior belongs to a new concept or to a new variant of an existing concept. - A new concept is appropriate for an independent semantic axis that must be composable with other concepts. - A new variant is appropriate for an alternative realization inside an existing semantic axis when independent composability is not required. This is a domain decision and is not always apparent from the code structure. @section concepts_level_guardrail Level Concept Guardrail The `level` concept deliberately hides the raw fixed-surface representation used by GRIB. GRIB vertical levels are ultimately represented by: - `typeOfFirstFixedSurface` - `scaleFactorOfFirstFixedSurface` - `scaledValueOfFirstFixedSurface` - `typeOfSecondFixedSurface` - `scaleFactorOfSecondFixedSurface` - `scaledValueOfSecondFixedSurface` These keys must not be set directly by mars2grib concept changes. Although many combinations are technically possible, most are not meaningful ECMWF levels. Level encoding must go through the official abstraction: `typeOfLevel` plus, when needed, `level`, `topLevel`, `bottomLevel`, and PV-array data. Each supported `typeOfLevel` corresponds to a `LevelType` variant or to a small number of virtual type-of-level values maintained in mars2grib for backward-compatibility reasons. If new level behavior is required, update the `LevelType` variant, matcher mapping, or deduction instead of writing fixed-surface keys directly. @section concepts_registration_value Registration Value The value associated with each `Concept::Variant` key is a **dense, fixed-size table** indexed by: - **Stage** - **Section** Conceptually: Table[Stage][Section] -> function pointer | nullptr Each entry corresponds to a specialization of the concept for a given `(Stage, Section)` pair. - A non-null entry indicates that the concept provides logic for that `(Stage, Section)`. - A null entry indicates that no specialization exists for that pair. This table is constructed at compile time and is immutable. @section concepts_specialization Specializations A concept provides logic by defining one or more specializations of the form: Concept Only explicitly defined specializations are registered. No implicit instantiation or fallback mechanism exists. Missing `(Stage, Section)` combinations are represented by null pointers in the registration table. @section concepts_dispatch_role Role in Encoder Dispatch The compile-time registration tables define the **maximum set of capabilities** available to the Encoder. At runtime: - the EncoderConfiguration selects a set of `(Concept, Variant)` keys - for each selected key, only the non-null `(Stage, Section)` entries are extracted - all null entries are explicitly discarded From this process, a **dense runtime dispatch table** is built, containing only valid function pointers. Concepts are unaware of: - which other concepts are active - execution order - template numbers - runtime configuration @section concepts_deductions Deductions as the Only Resolution Mechanism Concepts do not access input dictionaries directly. All resolution of GRIB keywords and values is performed exclusively through **deductions**. A deduction: - reads information from the MARS and misc dictionaries - applies validation, normalization, and consistency rules - exposes resolved values through a stable interface Concepts consume only deduction results and never inspect raw inputs. This strict separation ensures that: - concepts remain dictionary-agnostic - resolution logic is centralized - concepts are reusable and independently testable @section concepts_orthogonality Semantic Orthogonality Concepts are **semantically orthogonal**. Each concept: - has a single, well-defined semantic responsibility - does not depend on other concepts - does not encode cross-concept assumptions Any interaction between concepts is expressed through: - deduction logic - recipe constraints Never through direct concept-to-concept coupling. @section concepts_non_goals Explicit Non-Goals Concepts intentionally do not: - select template numbers - perform value deduction - allocate memory - manage execution order - inspect runtime configuration Concepts are purely declarative, statically registered contributors to the encoding process. metkit-1.19.2/src/metkit/mars2grib/api/0000775000175000017500000000000015232567717020031 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/api/pymars2grib/0000775000175000017500000000000015232567717022272 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2grib/api/pymars2grib/__init__.py0000664000175000017500000000004015232567717024375 0ustar alastairalastairfrom .mars2grib import Mars2Gribmetkit-1.19.2/src/metkit/mars2grib/api/pymars2grib/mars2grib.py0000664000175000017500000000151215232567717024533 0ustar alastairalastairfrom .mars2grib_core import init_bindings, Mars2GribCore class Mars2Grib(): """ Mars2Grib encoder object """ def __init__(self, options: dict = {}): init_bindings() self._mars2grib = Mars2GribCore(options) def encode(self, values: list[float], mars: dict, misc: dict = {}) -> bytes: """ Encode values, described by mars and (optionally) misc dictionaries, as a GRIB2 message :param values: Values to encode :type values: list[float] :param mars: Mars keys describing the values :type mars: dict :param misc: Miscellaneous (non-mars) keys describing the values, may be empty :type misc: dict :return: Encoded GRIB2 message as bytes object :rtype: bytes """ return self._mars2grib.encode(values, mars, misc) metkit-1.19.2/src/metkit/mars2grib/api/pymars2grib/mars2grib_core.cc0000664000175000017500000000775015232567717025512 0ustar alastairalastair/* * (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. */ #include #include #include #include #include #include "eckit/config/LocalConfiguration.h" #include "eckit/exception/Exceptions.h" #include "eckit/runtime/Main.h" #include "metkit/mars2grib/api/Mars2Grib.h" namespace py = pybind11; using metkit::mars2grib::Mars2Grib; static eckit::LocalConfiguration dictToLocalConfig(const py::dict& dict) { eckit::LocalConfiguration config; for (auto item : dict) { const auto key = item.first.cast(); const auto value = item.second; if (py::isinstance(value)) { config.set(key, value.cast()); } else if (py::isinstance(value)) { config.set(key, value.cast()); } else if (py::isinstance(value)) { config.set(key, value.cast()); } else if (py::isinstance(value)) { config.set(key, value.cast()); } else if (py::isinstance(value)) { config.set(key, dictToLocalConfig(value.cast())); } else if (py::isinstance(value)) { const py::list list = value.cast(); if (list.empty()) { config.set(key, std::vector{}); } else { bool hasFloat = false; for (const auto& item : list) { if (py::isinstance(item)) { hasFloat = true; } else if (!py::isinstance(item)) { throw eckit::Exception{"Unsupported type in list for key '" + key + "'", Here()}; } // else it's an int, just fall through... } if (hasFloat) { std::vector vector; vector.reserve(list.size()); for (const auto& item : list) { vector.push_back(item.cast()); } config.set(key, vector); } else { std::vector vector; vector.reserve(list.size()); for (const auto& item : list) { vector.push_back(item.cast()); } config.set(key, vector); } } } else { throw eckit::Exception{"Unsupported type for key '" + key + "'", Here()}; } } return config; } py::bytes encode(Mars2Grib& encoder, const std::vector& values, const py::dict& mars, const py::dict& misc) { const auto message = encoder.encode(values, dictToLocalConfig(mars), dictToLocalConfig(misc)); const auto size = message->messageSize(); std::vector buffer(message->messageSize()); message->copyInto(buffer.data(), buffer.size()); return py::bytes(reinterpret_cast(buffer.data()), buffer.size()); } PYBIND11_MODULE(mars2grib_core, m) { m.def("init_bindings", []() { const char* args[] = {"mars2grib", ""}; eckit::Main::initialise(1, const_cast(args)); }); auto mars2grib = py::class_(m, "Mars2GribCore") .def(py::init<>()) .def(py::init([](py::dict dict) { return std::make_unique(dictToLocalConfig(dict)); })) .def("encode", &encode, py::arg("values"), py::arg("mars"), py::arg("misc")); } metkit-1.19.2/src/metkit/mars2grib/api/pymars2grib/CMakeLists.txt0000664000175000017500000000203515232567717025032 0ustar alastairalastairpybind11_add_module(mars2grib_core MODULE mars2grib_core.cc) target_link_libraries(mars2grib_core PRIVATE pybind11::module pybind11::lto metkit eckit ) if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) # Strip unnecessary sections of the binary on Linux/macOS pybind11_strip(mars2grib_core) endif() set_target_properties(mars2grib_core PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/pymars2grib ) install( TARGETS mars2grib_core COMPONENT python DESTINATION "${CMAKE_INSTALL_LIBDIR}/pymars2grib" ) # Copy mars2grib.py into the install directory for now # TODO: Create a python wheel which can be installed with pip install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/mars2grib.py ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py COMPONENT python DESTINATION "${CMAKE_BINARY_DIR}/lib/pymars2grib" ) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/mars2grib.py ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py COMPONENT python DESTINATION "${CMAKE_INSTALL_LIBDIR}/pymars2grib" ) metkit-1.19.2/src/metkit/mars2grib/api/Mars2Grib.cc0000664000175000017500000004771115232567717022142 0ustar alastairalastair/* * (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 Mars2Grib.cc /// @brief Implementation of the Mars2Grib public encoding API. /// /// This file contains the implementation of the `Mars2Grib` class declared /// in `Mars2Grib.h`. /// /// It provides the concrete orchestration logic that: /// - builds the internal encoder configuration from the MARS dictionary /// - invokes the specialized backend encoder /// - injects field values into the resulting GRIB handle /// - exposes a temporary staged-encoding cache interface for benchmarking /// and transitional comparison purposes /// /// This file intentionally contains **no GRIB semantics** and **no deduction /// logic**. All domain-specific decisions are delegated to lower layers. /// /// --- /// /// ## Error propagation /// /// The current implementation propagates all exceptions across the API /// boundary after printing extended diagnostic information. /// /// This behavior is intentional for early integration phases and may be /// revised in the future to provide API-stable error wrapping. /// /// --- /// /// ## Transitional staged API /// /// This implementation also exposes a temporary staged-encoding interface /// based on `prepare()` and `finaliseEncoding()`. /// /// This interface is not intended for general use. It exists only to allow /// short-term benchmarking and comparison against a legacy cache-based /// implementation before the cache lifecycle is fully internalized in /// lower layers. /// /// --- /// /// ## Scope /// /// - This file is part of the **Mars2Grib public API implementation** /// - It is not intended for direct use by end users /// - Its behavior defines the observable semantics of `Mars2Grib::encode` /// and the temporary staged-encoding API /// /// @ingroup mars2grib_api /// #include "Mars2Grib.h" // other libraries #include "eckit/exception/Exceptions.h" // dictionary access traits #include "metkit/mars2grib/utils/dictionary_traits/dictaccess_codes_handle.h" #include "metkit/mars2grib/utils/dictionary_traits/dictaccess_eckit_configuration.h" #include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" // encode header/values implementation #include "metkit/mars2grib/CoreOperations.h" namespace metkit::mars2grib { namespace { /// /// @brief Read Mars2Grib options from a configuration object. /// /// This helper function maps a subset of keys from an /// `eckit::LocalConfiguration` into a strongly typed `Options` object. /// /// Only explicitly present keys are applied; all others retain their /// default values. /// /// @param[in] conf /// Configuration object containing encoder options. /// /// @return /// A fully initialized `Options` structure. /// Options readOptions(const eckit::LocalConfiguration& conf) { using metkit::mars2grib::utils::dict_traits::get_or_throw; using metkit::mars2grib::utils::dict_traits::has; Options opts; if (has(conf, "applyChecks")) { opts.applyChecks = get_or_throw(conf, "applyChecks"); } if (has(conf, "enableOverride")) { opts.enableOverride = get_or_throw(conf, "enableOverride"); } if (has(conf, "enableBitsPerValueCompression")) { opts.enableBitsPerValueCompression = get_or_throw(conf, "enableBitsPerValueCompression"); } if (has(conf, "normalizeMars")) { opts.normalizeMars = get_or_throw(conf, "normalizeMars"); } if (has(conf, "normalizeMisc")) { opts.normalizeMisc = get_or_throw(conf, "normalizeMisc"); } if (has(conf, "fixMarsGrid")) { opts.fixMarsGrid = get_or_throw(conf, "fixMarsGrid"); } if (has(conf, "skipSection3")) { opts.skipSection3 = get_or_throw(conf, "skipSection3"); } return opts; } } // namespace /// /// @brief Concrete specialization of the generic core cache entry. /// /// This alias binds the generic `CoreOperations::CacheEntry` template to the /// concrete public API types: /// - `eckit::LocalConfiguration` for MARS metadata /// - `eckit::LocalConfiguration` for auxiliary metadata /// - `Options` for encoding options /// - `metkit::codes::CodesHandle` for the GRIB output object /// /// It is used internally as the implementation payload of the opaque /// `Mars2Grib::CacheEntry`. /// /// @note /// This staged cache path is temporary and not intended for public use. /// using CoreCacheEntry = CoreOperations::CacheEntry; /// /// @brief Opaque API wrapper around the concrete core cache entry. /// /// This type hides the concrete staged-encoding cache representation from /// API users while preserving unique ownership and immutability. /// /// Internally, it stores a fully prepared `CoreCacheEntry` specialized for /// the public `Mars2Grib` API types. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// purposes and may be changed or removed without notice. /// struct Mars2Grib::CacheEntry { /// /// @brief Construct the opaque wrapper from a concrete core cache. /// /// Ownership of the prepared core cache is transferred into the wrapper. /// /// @param[in] impl /// Unique pointer owning the prepared concrete core cache entry. /// explicit CacheEntry(std::unique_ptr&& impl) : impl_{std::move(impl)} {} CacheEntry(const CacheEntry&) = delete; CacheEntry& operator=(const CacheEntry&) = delete; CacheEntry(CacheEntry&&) = delete; CacheEntry& operator=(CacheEntry&&) = delete; ~CacheEntry() = default; /// /// @brief Owned immutable implementation cache. /// /// This pointer stores the concrete staged-encoding cache used by the /// generic core layer. /// const std::unique_ptr impl_; }; /// /// @brief Destroy an opaque API cache entry. /// /// This custom deleter is required because `Mars2Grib::CacheEntry` is /// incomplete in the public header and only fully defined in this /// implementation file. /// /// @param[in] p /// Pointer to the opaque cache entry to destroy. /// /// @note /// This staged cache API is temporary and not intended for public use. /// void Mars2Grib::CacheEntryDeleter::operator()(const CacheEntry* p) const { delete p; } /// /// @brief Prepare an opaque staged-encoding cache. /// /// This function delegates to the generic core layer to: /// - normalize input metadata if enabled, /// - resolve the structural header layout, /// - build the specialized encoder state, /// - prepare an immutable reusable sample for later finalization. /// /// The returned cache is opaque at the public API level and can be /// passed to `finaliseEncoding()` multiple times. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer owning an opaque immutable cache entry. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// purposes and may be changed or removed without notice. /// Mars2Grib::CacheEntryPtr Mars2Grib::prepare(const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { auto coreCache = CoreOperations::prepare(mars, misc, opts_, language_); return CacheEntryPtr(new CacheEntry(std::move(coreCache))); } /// /// @brief Finalize a GRIB encoding from a previously prepared cache. /// /// This function delegates to the generic core layer to complete the /// encoding pipeline using a cache previously produced by `prepare()`. /// /// The cache provides the pre-specialized, reusable encoding state, /// while this function injects the supplied field values and produces /// a final `CodesHandle`. /// /// The cache is not consumed and may be reused in subsequent calls. /// /// @param[in] cacheEntry /// Opaque staged-encoding cache previously produced by `prepare()`. /// /// @param[in] values /// Field values to encode as double. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// purposes and may be changed or removed without notice. /// std::unique_ptr Mars2Grib::finaliseEncoding(const CacheEntryPtr& cacheEntry, const std::vector& values, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { return CoreOperations::finaliseEncoding( *(cacheEntry->impl_), Span{values}, mars, misc, opts_, language_); } /// /// @brief Finalize a GRIB encoding from a previously prepared cache. /// /// This overload accepts field values as `float`. /// /// The cache supplies the pre-specialized, reusable encoding state, /// while this function injects the provided field values and applies /// any remaining dynamic metadata handling required to produce a final /// GRIB message. /// /// The cache is not consumed by this operation and may be reused /// for subsequent calls. /// /// @param[in] cacheEntry /// Opaque staged-encoding cache previously produced by `prepare()`. /// /// @param[in] values /// Pointer to the field values as double. /// /// @param[in] length /// Number of values in the buffer. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// std::unique_ptr Mars2Grib::finaliseEncoding(const CacheEntryPtr& cacheEntry, const double* values, size_t length, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { return CoreOperations::finaliseEncoding( *(cacheEntry->impl_), Span{values, length}, mars, misc, opts_, language_); } /// /// @brief Finalize a GRIB encoding from a previously prepared cache. /// /// This overload accepts field values as `float`. /// /// The cache provides the pre-specialized, reusable encoding state, /// while this function injects the supplied field values and produces /// a final `CodesHandle`. /// /// The cache is not consumed and may be reused in subsequent calls. /// /// @param[in] cacheEntry /// Opaque staged-encoding cache previously produced by `prepare()`. /// /// @param[in] values /// Field values to encode as float. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// purposes and may be changed or removed without notice. /// std::unique_ptr Mars2Grib::finaliseEncoding(const CacheEntryPtr& cacheEntry, const std::vector& values, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { return CoreOperations::finaliseEncoding(*(cacheEntry->impl_), Span{values}, mars, misc, opts_, language_); } /// /// @brief Finalize a GRIB encoding from a previously prepared cache. /// /// This overload accepts field values as `float`. /// /// The cache supplies the pre-specialized, reusable encoding state, /// while this function injects the provided field values and applies /// any remaining dynamic metadata handling required to produce a final /// GRIB message. /// /// The cache is not consumed by this operation and may be reused /// for subsequent calls. /// /// @param[in] cacheEntry /// Opaque staged-encoding cache previously produced by `prepare()`. /// /// @param[in] values /// Pointer to the field values as float. /// /// @param[in] length /// Number of values in the buffer. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// std::unique_ptr Mars2Grib::finaliseEncoding(const CacheEntryPtr& cacheEntry, const float* values, size_t length, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { return CoreOperations::finaliseEncoding( *(cacheEntry->impl_), Span{values, length}, mars, misc, opts_, language_); } // ----------------------------------------------------------------------------- // Mars2Grib construction // ----------------------------------------------------------------------------- Mars2Grib::Mars2Grib() : opts_{} {} Mars2Grib::Mars2Grib(const Options& opts) : opts_{opts} {} Mars2Grib::Mars2Grib(const eckit::LocalConfiguration& opts) : opts_{readOptions(opts)} {} // ----------------------------------------------------------------------------- // Encoding interfaces // ----------------------------------------------------------------------------- std::unique_ptr Mars2Grib::encode(const std::vector& values, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { return CoreOperations::encode(Span{values}, mars, misc, opts_, language_); } std::unique_ptr Mars2Grib::encode(const std::vector& values, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { return CoreOperations::encode(Span{values}, mars, misc, opts_, language_); } std::unique_ptr Mars2Grib::encode(const std::vector& values, const eckit::LocalConfiguration& mars) { const eckit::LocalConfiguration misc{}; return CoreOperations::encode(Span{values}, mars, misc, opts_, language_); } std::unique_ptr Mars2Grib::encode(const std::vector& values, const eckit::LocalConfiguration& mars) { const eckit::LocalConfiguration misc{}; return CoreOperations::encode(Span{values}, mars, misc, opts_, language_); } std::unique_ptr Mars2Grib::encode(const double* values, size_t length, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { return CoreOperations::encode(Span{values, length}, mars, misc, opts_, language_); } std::unique_ptr Mars2Grib::encode(const float* values, size_t length, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc) { return CoreOperations::encode(Span{values, length}, mars, misc, opts_, language_); } std::unique_ptr Mars2Grib::encode(const double* values, size_t length, const eckit::LocalConfiguration& mars) { const eckit::LocalConfiguration misc{}; return CoreOperations::encode(Span{values, length}, mars, misc, opts_, language_); } std::unique_ptr Mars2Grib::encode(const float* values, size_t length, const eckit::LocalConfiguration& mars) { const eckit::LocalConfiguration misc{}; return CoreOperations::encode(Span{values, length}, mars, misc, opts_, language_); } } // namespace metkit::mars2grib metkit-1.19.2/src/metkit/mars2grib/api/Options.h0000664000175000017500000001051115232567717021633 0ustar alastairalastair/* * (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 Options.h /// @brief Configuration options for the Mars2Grib encoding API. /// /// This header defines the public configuration structure used to /// control the behavior of the **Mars2Grib encoder**. /// /// The `Options` structure is part of the **user-facing API** and is /// intentionally simple, explicit, and stable. Each option enables or /// disables a well-defined aspect of the encoding process. /// /// Options: /// - do NOT change the semantic meaning of the input data /// - do NOT introduce implicit defaults in metadata /// - only affect validation, override behavior, or encoding strategies /// /// Options can be: /// - constructed programmatically /// - passed directly to the `Mars2Grib` constructor /// - populated from an `eckit::LocalConfiguration` /// /// @ingroup mars2grib_api /// #pragma once namespace metkit::mars2grib { /// /// @brief Encoding options for the Mars2Grib API. /// /// This structure controls optional behaviors of the GRIB encoding /// process. All options are **opt-in** and have conservative defaults /// to preserve backward compatibility and predictable behavior. /// /// The default-constructed `Options` object corresponds to the /// standard mars2grib encoding behavior. /// struct Options { /// /// @brief Enable or disable input validation checks. /// /// When enabled, the encoder performs consistency and validity /// checks at selected critical points during the encoding phase. /// /// Disabling this option may improve performance but can result /// in failures that are harder to diagnose in the presence of /// malformed or inconsistent input. /// /// @default true /// bool applyChecks = true; /// /// @brief Enable metadata override semantics. /// /// When enabled, values provided through parameter dictionary are /// allowed to override values resolved from the MARS dictionary. /// /// When disabled, conflicting overrides result in an error. /// /// @default false /// bool enableOverride = false; /// /// @brief Enable bits-per-value compression. /// /// When enabled, the encoder is allowed to select a bits-per-value /// packing strategy to reduce message size. /// /// This option affects only the **encoding strategy** and does not /// alter the numerical values of the field. /// /// @default false /// bool enableBitsPerValueCompression = false; /// /// @brief Enable semantic normalization of the MARS dictionary. /// /// When active, the MARS request is sanitized against the library /// language definition to ensure key-value consistency and /// case-insensitivity before resolution. /// /// @default false /// bool normalizeMars = false; /// /// @brief Enable semantic normalization of the auxiliary metadata. /// /// When active, the auxiliary (Misc) dictionary is sanitized against /// the library language definition. This is recommended when /// parameters are provided as raw strings. /// /// @default false /// bool normalizeMisc = false; /// /// @brief Automatically normalize MARS 'grid' syntax. /// /// If enabled, the encoder detects and converts legacy MARS grid /// specifications (e.g., 'L640x320') into standard GRIB-compliant /// increment strings ('deltaLon/deltaLat'). /// /// This is a **procedural normalization** that ensures resolution /// compatibility for Gaussian or reduced grids. /// /// @default true /// bool fixMarsGrid = true; /// /// @brief Skip GRIB Section 3 (Grid Definition Section) encoding. /// /// When enabled, the encoder bypasses the encoding of Section 3, /// allowing the gridSpec/ecCodes to take care of all the geometry and grid definition details. /// /// @default false /// bool skipSection3 = false; }; } // namespace metkit::mars2grib metkit-1.19.2/src/metkit/mars2grib/api/Mars2Grib.h0000664000175000017500000005052015232567717021774 0ustar alastairalastair/* * (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 Mars2Grib.h /// @brief High-level API for encoding MARS fields into GRIB messages. /// /// This header defines the **public Mars2Grib encoding API**, providing a /// user-facing interface to convert MARS-style metadata and field values /// into GRIB messages. /// /// The `Mars2Grib` class acts as a **stateless encoder facade**: /// - it validates and interprets user-provided metadata /// - it constructs a GRIB header according to MARS conventions /// - it encodes the provided field values /// - it returns a fully-formed GRIB handle /// /// This API is designed for: /// - application developers /// - workflow orchestration layers /// - bindings (Fortran, Python, etc.) /// /// It intentionally hides all internal concepts such as planners, /// deductions, sections, or encoding strategies. /// /// --- /// /// ## Conceptual overview /// /// Encoding is driven by three inputs: /// /// - **MARS dictionary** (`mars`) /// Describes the field semantics (e.g. parameter, level, step, date). /// /// - **Misc dictionary** (`misc`, optional) /// Provides auxiliary metadata not strictly part of the MARS request /// (e.g. grid geometry, packing hints, implementation options). /// /// - **Values** /// The numerical field values to be encoded. /// /// The result of an encoding operation is a /// `metkit::codes::CodesHandle`, which can be: /// - written to file /// - passed to ecCodes /// - transferred to downstream systems /// /// --- /// /// ## Error handling /// /// - All encoding failures are reported via C++ exceptions. /// - Errors are fail-fast and no partial GRIB messages are produced. /// - On failure, no `CodesHandle` is returned. /// /// --- /// /// ## Thread safety /// /// - A `Mars2Grib` instance is safe to use from a single thread. /// - Concurrent use from multiple threads requires separate instances. /// /// @ingroup mars2grib_api /// #pragma once // System includes #include #include // eckit #include "eckit/config/LocalConfiguration.h" #include "eckit/value/Value.h" // ecCodes API wrapper #include "metkit/codes/api/CodesAPI.h" // Codes wrapper types #include "metkit/codes/api/CodesTypes.h" // mars2grib public options #include "metkit/mars2grib/api/Options.h" namespace metkit::mars2grib { /// --- /// /// ## Transitional staged API /// /// A temporary staged-encoding interface is also exposed through /// `prepare()` and `finaliseEncoding()`. /// /// This interface exists only for short-term benchmarking and migration /// purposes and is not intended for external use. It may be changed or /// removed without notice. /// /// /// @brief High-level encoder for converting MARS fields to GRIB. /// /// The `Mars2Grib` class provides a **single-entry-point API** /// for encoding numerical field data together with MARS metadata /// into a GRIB message. /// /// A `Mars2Grib` object encapsulates a fixed set of encoding options /// and can be reused to encode multiple fields with the same /// configuration. /// /// ### Lifetime and ownership /// /// - `Mars2Grib` does not own any external resources. /// - Each call to `encode()` returns a new `CodesHandle` owned /// by the caller. /// /// ### Copy semantics /// /// Copy and move operations are explicitly disabled to avoid /// accidental sharing of internal state. /// class Mars2Grib { public: template using Span = metkit::codes::Span; /// /// @brief Construct a Mars2Grib encoder with default options. /// /// Default options correspond to standard mars2grib behavior. /// Mars2Grib(); /// /// @brief Construct a Mars2Grib encoder with explicit options. /// /// @param[in] opts /// Encoding options controlling behavior such as validation, /// logging, or feature toggles. /// explicit Mars2Grib(const Options& opts); /// /// @brief Construct a Mars2Grib encoder from a configuration object. /// /// This constructor allows options to be provided via an /// `eckit::LocalConfiguration`, typically originating from YAML /// or JSON configuration files. /// /// @param[in] opts /// Configuration object describing encoder options. /// explicit Mars2Grib(const eckit::LocalConfiguration& opts); Mars2Grib(const Mars2Grib&) = delete; Mars2Grib(Mars2Grib&&) = delete; Mars2Grib operator=(const Mars2Grib&) = delete; Mars2Grib operator=(Mars2Grib&&) = delete; ~Mars2Grib() = default; // ------------------------------------------------------------------ // Encoding interface — std::vector based // ------------------------------------------------------------------ /// /// @brief Encode a field into a GRIB message. /// /// @param[in] values /// Field values to encode as double. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// std::unique_ptr encode(const std::vector& values, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); /// /// @brief Encode a field into a GRIB message. /// /// @param[in] values /// Field values to encode as float. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// std::unique_ptr encode(const std::vector& values, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); /// /// @brief Encode a field using only a MARS dictionary. /// /// This overload omits the `misc` dictionary. /// /// @param[in] values /// Field values to encode as double. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// std::unique_ptr encode(const std::vector& values, const eckit::LocalConfiguration& mars); /// /// @brief Encode a field using only a MARS dictionary. /// /// This overload omits the `misc` dictionary. /// /// @param[in] values /// Field values to encode as float. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// std::unique_ptr encode(const std::vector& values, const eckit::LocalConfiguration& mars); // ------------------------------------------------------------------ // Encoding interface — raw pointer based // ------------------------------------------------------------------ /// /// @brief Encode a field from a raw value buffer. /// /// @param[in] values /// Pointer to the field values as double. /// /// @param[in] length /// Number of values in the buffer. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// std::unique_ptr encode(const double* values, size_t length, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); /// /// @brief Encode a field from a raw value buffer. /// /// @param[in] values /// Pointer to the field values as float. /// /// @param[in] length /// Number of values in the buffer. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// std::unique_ptr encode(const float* values, size_t length, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); /// /// @brief Encode a field from a raw value buffer. /// /// @param[in] values /// Pointer to the field values as double. /// /// @param[in] length /// Number of values in the buffer. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// std::unique_ptr encode(const double* values, size_t length, const eckit::LocalConfiguration& mars); /// /// @brief Encode a field from a raw value buffer. /// /// @param[in] values /// Pointer to the field values as float. /// /// @param[in] length /// Number of values in the buffer. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// std::unique_ptr encode(const float* values, size_t length, const eckit::LocalConfiguration& mars); /// /// @brief Opaque cache object for staged GRIB encoding. /// /// This type represents a precomputed encoding cache produced by /// `prepare()` and later consumed by `finaliseEncoding()`. /// /// The cache internalizes the expensive metadata-specialization phase /// and stores immutable state that can be reused across multiple /// finalization calls. /// /// The concrete representation of this type is intentionally hidden /// from API users. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// struct CacheEntry; /// /// @brief Custom deleter for opaque cache entries. /// /// This deleter is paired with `CacheEntryPtr` to allow ownership of /// an incomplete, opaque `CacheEntry` type in the public interface. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// struct CacheEntryDeleter { /// /// @brief Destroy an opaque cache entry. /// /// @param[in] cache /// Pointer to the cache entry to destroy. /// void operator()(const CacheEntry*) const; }; /// /// @brief Owning smart pointer to an opaque staged-encoding cache. /// /// This alias represents unique ownership of a `CacheEntry` /// produced by `prepare()`. /// /// The pointed object is immutable and may be reused across /// multiple `finaliseEncoding()` calls. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// using CacheEntryPtr = std::unique_ptr; /// /// @brief Prepare a reusable staged-encoding cache. /// /// This function performs the metadata-specialization phase of the /// encoding pipeline and returns an opaque cache object that can be /// reused in subsequent calls to `finaliseEncoding()`. /// /// The cache is derived from: /// - the MARS dictionary, /// - the auxiliary metadata dictionary, /// - the current `Mars2Grib` options, /// - the active language definition. /// /// This operation is intended for workflows where the structural /// encoding setup is reused multiple times with different field values /// and/or repeated finalization requests. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer owning an opaque immutable cache entry. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// CacheEntryPtr prepare(const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); /// /// @brief Finalize a GRIB encoding from a previously prepared cache. /// /// This function completes the encoding pipeline using a cache /// previously produced by `prepare()`. /// /// The cache supplies the pre-specialized, reusable encoding state, /// while this function injects the provided field values and applies /// any remaining dynamic metadata handling required to produce a final /// GRIB message. /// /// The cache is not consumed by this operation and may be reused /// for subsequent calls. /// /// @param[in] cacheEntry /// Opaque staged-encoding cache previously produced by `prepare()`. /// /// @param[in] values /// Field values to encode as double. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// std::unique_ptr finaliseEncoding(const CacheEntryPtr& cacheEntry, const std::vector& values, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); /// /// @brief Finalize a GRIB encoding from a previously prepared cache. /// /// This function completes the encoding pipeline using a cache /// previously produced by `prepare()`. /// /// The cache supplies the pre-specialized, reusable encoding state, /// while this function injects the provided field values and applies /// any remaining dynamic metadata handling required to produce a final /// GRIB message. /// /// The cache is not consumed by this operation and may be reused /// for subsequent calls. /// /// @param[in] cacheEntry /// Opaque staged-encoding cache previously produced by `prepare()`. /// /// @param[in] values /// Pointer to the field values as double. /// /// @param[in] length /// Number of values in the buffer. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// std::unique_ptr finaliseEncoding(const CacheEntryPtr& cacheEntry, const double* values, size_t length, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); /// /// @brief Finalize a GRIB encoding from a previously prepared cache. /// /// This overload accepts field values as `float`. /// /// The cache supplies the pre-specialized, reusable encoding state, /// while this function injects the provided field values and applies /// any remaining dynamic metadata handling required to produce a final /// GRIB message. /// /// The cache is not consumed by this operation and may be reused /// for subsequent calls. /// /// @param[in] cacheEntry /// Opaque staged-encoding cache previously produced by `prepare()`. /// /// @param[in] values /// Field values to encode as float. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// std::unique_ptr finaliseEncoding(const CacheEntryPtr& cacheEntry, const std::vector& values, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); /// /// @brief Finalize a GRIB encoding from a previously prepared cache. /// /// This overload accepts field values as `float`. /// /// The cache supplies the pre-specialized, reusable encoding state, /// while this function injects the provided field values and applies /// any remaining dynamic metadata handling required to produce a final /// GRIB message. /// /// The cache is not consumed by this operation and may be reused /// for subsequent calls. /// /// @param[in] cacheEntry /// Opaque staged-encoding cache previously produced by `prepare()`. /// /// @param[in] values /// Pointer to the field values as float. /// /// @param[in] length /// Number of values in the buffer. /// /// @param[in] mars /// MARS dictionary describing the field metadata. /// /// @param[in] misc /// Auxiliary metadata dictionary. /// /// @return /// A unique pointer to a GRIB handle containing the encoded message. /// /// @note /// This staged cache API is temporary and not intended for public use. /// It is exposed only for transitional benchmarking and comparison /// against a legacy implementation. It may be changed or removed /// without notice. /// std::unique_ptr finaliseEncoding(const CacheEntryPtr& cacheEntry, const float* values, size_t length, const eckit::LocalConfiguration& mars, const eckit::LocalConfiguration& misc); private: const eckit::Value language_; const Options opts_; }; } // namespace metkit::mars2grib metkit-1.19.2/src/metkit/metkit_version.h.in0000664000175000017500000000111015232567717021201 0ustar alastairalastair#ifndef metkit_version_h #define metkit_version_h #define metkit_VERSION_STR "@metkit_VERSION_STR@" #define metkit_VERSION "@metkit_VERSION@" #define metkit_VERSION_MAJOR @metkit_VERSION_MAJOR@ #define metkit_VERSION_MINOR @metkit_VERSION_MINOR@ #define metkit_VERSION_PATCH @metkit_VERSION_PATCH@ #define metkit_GIT_SHA1 "@metkit_GIT_SHA1@" #ifdef __cplusplus extern "C" { #endif const char * metkit_version(); unsigned int metkit_version_int(); const char * metkit_version_str(); const char * metkit_git_sha1(); #ifdef __cplusplus } #endif #endif // metkit_version_h metkit-1.19.2/src/metkit/pointdb/0000775000175000017500000000000015232567717017027 5ustar alastairalastairmetkit-1.19.2/src/metkit/pointdb/GribFieldInfo.cc0000664000175000017500000001201415232567717021777 0ustar alastairalastair/* * (C) Copyright 1996-2013 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 "metkit/pointdb/GribFieldInfo.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/pointdb/GribDataSource.h" #include using namespace eckit; extern "C" { unsigned long grib_decode_unsigned_long(const unsigned char* p, long* offset, int bits); double grib_power(long s, long n); } namespace metkit { namespace pointdb { static Mutex mutex; #define MISSING 9999 static int bits[65536] = { #include "metkit/pointdb/bits.h" }; static uint64_t masks[64] = { #include "metkit/pointdb/masks.h" }; static inline int count_bits(unsigned long long n) { return bits[n & 0xffffu] + bits[(n >> 16) & 0xffffu] + bits[(n >> 32) & 0xffffu] + bits[(n >> 48) & 0xffffu]; } GribFieldInfo::GribFieldInfo() : referenceValue_(0), binaryScaleFactor_(0), decimalScaleFactor_(0), bitsPerValue_(0), offsetBeforeData_(0), offsetBeforeBitmap_(0), numberOfValues_(0), numberOfDataPoints_(0), sphericalHarmonics_(0) {} void GribFieldInfo::update(const codes::CodesHandle& h) { binaryScaleFactor_ = h.getLong("binaryScaleFactor"); decimalScaleFactor_ = h.getLong("decimalScaleFactor"); bitsPerValue_ = h.getLong("bitsPerValue"); referenceValue_ = h.getDouble("referenceValue"); offsetBeforeData_ = h.getLong("offsetBeforeData"); numberOfDataPoints_ = h.getLong("numberOfDataPoints"); numberOfValues_ = h.getLong("numberOfValues"); sphericalHarmonics_ = h.getLong("sphericalHarmonics"); if (h.getLong("bitmapPresent")) offsetBeforeBitmap_ = h.getLong("offsetBeforeBitmap"); else offsetBeforeBitmap_ = 0; if (!sphericalHarmonics_) geographyHash_ = h.getString("md5GridSection"); } void GribFieldInfo::print(std::ostream& s) const { s << "GribFieldInfo["; s << "binaryScaleFactor=" << binaryScaleFactor_; s << ",decimalScaleFactor=" << decimalScaleFactor_; s << ",bitsPerValue=" << bitsPerValue_; s << ",referenceValue=" << referenceValue_; s << ",offsetBeforeData=" << offsetBeforeData_; s << ",numberOfDataPoints=" << numberOfDataPoints_; s << ",numberOfValues=" << numberOfValues_; s << ",offsetBeforeBitmap=" << offsetBeforeBitmap_; s << ",sphericalHarmonics=" << sphericalHarmonics_; s << ",geographyHash=" << geographyHash_; s << "]"; } double GribFieldInfo::interpolate(GribDataSource& f, double& lat, double& lon) const { NOTIMP; } double GribFieldInfo::value(const GribDataSource& f, size_t index) const { unsigned char buf[8]; if (bitsPerValue_ == 0) return referenceValue_; ASSERT(!sphericalHarmonics_); if (offsetBeforeBitmap_) { ASSERT(index < numberOfDataPoints_); Log::info() << "offsetBeforeBitmap_ " << offsetBeforeBitmap_ << ",index " << index << std::endl; Offset offset(offsetBeforeBitmap_); ASSERT(f.seek(offset) == offset); size_t count = 0; uint64_t n; ASSERT(sizeof(n) == 8); size_t skip = index / (sizeof(n) * 8); Log::info() << "Read " << skip << " words" << std::endl; for (size_t i = 0; i < skip; ++i) { ASSERT(f.read(&n, sizeof(n)) == sizeof(n)); count += count_bits(n); } Log::info() << "Count " << count << std::endl; size_t pos = index % (sizeof(n) * 8); Log::info() << "Read last bits, " << pos << std::endl; ASSERT(f.read(&n, sizeof(n)) == sizeof(n)); Log::info() << "Read last bits, " << pos << ", before mask " << std::bitset<64>(n) << std::endl; n &= masks[pos]; Log::info() << "Read last bits, " << pos << ", after mask " << std::bitset<64>(n) << ", bits=" << count_bits(n) << std::endl; count += count_bits(n); Log::info() << "Count " << count << std::endl; n = (n >> (64 - pos)) & 1; if (!n) { return MISSING; } ASSERT(count); index = count - 1; } Log::info() << "index " << index << ", numberOfValues " << numberOfValues_ << std::endl; ASSERT(index < numberOfValues_); { Offset offset = off_t(offsetBeforeData_) + off_t(index * bitsPerValue_ / 8); ASSERT(f.seek(offset) == offset); long len = (bitsPerValue_ + 7) / 8; ASSERT(f.read(buf, len) == len); } long bitp = (index * bitsPerValue_) % 8; unsigned long p = grib_decode_unsigned_long(buf, &bitp, bitsPerValue_); // TODO: store the precomputed values double s = grib_power(binaryScaleFactor_, 2); double d = grib_power(-decimalScaleFactor_, 10); double v = (double)(((p * s) + referenceValue_) * d); return v; } } // namespace pointdb } // namespace metkit metkit-1.19.2/src/metkit/pointdb/GribHandleDataSource.h0000664000175000017500000000311315232567717023150 0ustar alastairalastair/* * (C) Copyright 1996-2013 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. */ #ifndef metkit_GribHandleDataSource_H #define metkit_GribHandleDataSource_H #include "metkit/pointdb/GribDataSource.h" #include "metkit/pointdb/GribFieldInfo.h" namespace eckit { class DataHandle; } namespace metkit { namespace pointdb { class GribHandleDataSource : public GribDataSource { public: GribHandleDataSource(const eckit::PathName&, const eckit::Offset& = 0); GribHandleDataSource(eckit::DataHandle&, const eckit::Offset& = 0); GribHandleDataSource(eckit::DataHandle*, const eckit::Offset& = 0); ~GribHandleDataSource(); private: mutable eckit::DataHandle* handle_; bool ownsHandle_; mutable bool opened_; mutable GribFieldInfo info_; eckit::Offset offset_; virtual eckit::Offset seek(const eckit::Offset&) const override; virtual long read(void*, long) const override; virtual const GribFieldInfo& info() const override; virtual void print(std::ostream& s) const override; virtual const std::map& request() const override; virtual std::string groupKey() const override; virtual std::string sortKey() const override; void open() const; }; } // namespace pointdb } // namespace metkit #endif metkit-1.19.2/src/metkit/pointdb/DataSource.h0000664000175000017500000000366315232567717021242 0ustar alastairalastair/* * (C) Copyright 1996-2013 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. */ #ifndef metkit_DataSource_H #define metkit_DataSource_H #include #include #include "eckit/memory/NonCopyable.h" namespace eckit { class JSON; class Value; } // namespace eckit namespace metkit { class MarsRequest; namespace pointdb { class DataSource; struct PointResult { double value_; double lat_; double lon_; const DataSource* source_; // Warning, the source must have a longer life time as the result void print(std::ostream& s) const; friend std::ostream& operator<<(std::ostream& s, const PointResult& f) { f.print(s); return s; } }; class DataSourceHandler { public: virtual void handle(DataSource*) = 0; }; class DataSource : public eckit::NonCopyable { public: virtual ~DataSource(); virtual PointResult extract(double lat, double lon) const = 0; // Encode a MARS-like request representing the field virtual const std::map& request() const = 0; // A key to group source togther, e.g. sources poiting to the same file virtual std::string groupKey() const = 0; // A key to sort sources of the same group, e.g. offset in the file virtual std::string sortKey() const = 0; // Used to throw away requests in case of restarted transactions virtual size_t batch() const; private: virtual void print(std::ostream& s) const = 0; friend std::ostream& operator<<(std::ostream& s, const DataSource& f) { f.print(s); return s; } }; } // namespace pointdb } // namespace metkit #endif metkit-1.19.2/src/metkit/pointdb/masks.h0000664000175000017500000000325515232567717020323 0ustar alastairalastair/* * (C) Copyright 1996-2013 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. */ 0x0, 0x8000000000000000, 0xc000000000000000, 0xe000000000000000, 0xf000000000000000, 0xf800000000000000, 0xfc00000000000000, 0xfe00000000000000, 0xff00000000000000, 0xff80000000000000, 0xffc0000000000000, 0xffe0000000000000, 0xfff0000000000000, 0xfff8000000000000, 0xfffc000000000000, 0xfffe000000000000, 0xffff000000000000, 0xffff800000000000, 0xffffc00000000000, 0xffffe00000000000, 0xfffff00000000000, 0xfffff80000000000, 0xfffffc0000000000, 0xfffffe0000000000, 0xffffff0000000000, 0xffffff8000000000, 0xffffffc000000000, 0xffffffe000000000, 0xfffffff000000000, 0xfffffff800000000, 0xfffffffc00000000, 0xfffffffe00000000, 0xffffffff00000000, 0xffffffff80000000, 0xffffffffc0000000, 0xffffffffe0000000, 0xfffffffff0000000, 0xfffffffff8000000, 0xfffffffffc000000, 0xfffffffffe000000, 0xffffffffff000000, 0xffffffffff800000, 0xffffffffffc00000, 0xffffffffffe00000, 0xfffffffffff00000, 0xfffffffffff80000, 0xfffffffffffc0000, 0xfffffffffffe0000, 0xffffffffffff0000, 0xffffffffffff8000, 0xffffffffffffc000, 0xffffffffffffe000, 0xfffffffffffff000, 0xfffffffffffff800, 0xfffffffffffffc00, 0xfffffffffffffe00, 0xffffffffffffff00, 0xffffffffffffff80, 0xffffffffffffffc0, 0xffffffffffffffe0, 0xfffffffffffffff0, 0xfffffffffffffff8, 0xfffffffffffffffc, 0xfffffffffffffffe, metkit-1.19.2/src/metkit/pointdb/FieldIndexer.cc0000664000175000017500000000125015232567717021676 0ustar alastairalastair/* * (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 "metkit/pointdb/FieldIndexer.h" namespace metkit { namespace pointdb { FieldIndexer::~FieldIndexer() {} //---------------------------------------------------------------------------------------------------------------------- } // namespace pointdb } // namespace metkit metkit-1.19.2/src/metkit/pointdb/bits.h0000664000175000017500000065407115232567717020156 0ustar alastairalastair/* * (C) Copyright 1996-2013 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. */ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 8, 9, 8, 9, 9, 10, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 7, 8, 8, 9, 8, 9, 9, 10, 8, 9, 9, 10, 9, 10, 10, 11, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 8, 9, 9, 10, 9, 10, 10, 11, 9, 10, 10, 11, 10, 11, 11, 12, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 9, 10, 10, 11, 10, 11, 11, 12, 10, 11, 11, 12, 11, 12, 12, 13, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 10, 11, 11, 12, 11, 12, 12, 13, 11, 12, 12, 13, 12, 13, 13, 14, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 11, 12, 12, 13, 12, 13, 13, 14, 12, 13, 13, 14, 13, 14, 14, 15, 12, 13, 13, 14, 13, 14, 14, 15, 13, 14, 14, 15, 14, 15, 15, 16, metkit-1.19.2/src/metkit/pointdb/GribFieldInfo.h0000664000175000017500000000320015232567717021636 0ustar alastairalastair/* * (C) Copyright 1996-2013 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. */ #ifndef FieldInfoData_H #define FieldInfoData_H #include "metkit/codes/api/CodesAPI.h" #include "eckit/io/Length.h" #include "eckit/io/Offset.h" #include "eckit/types/FixedString.h" namespace eckit { class PathName; } namespace metkit::pointdb { class GribDataSource; class GribFieldInfo { public: GribFieldInfo(); std::string geographyHash() const { return geographyHash_; } bool ready() const { return numberOfValues_ > 0; } void update(const codes::CodesHandle& h); double value(const GribDataSource&, size_t index) const; bool useInterpolation() const { return sphericalHarmonics_ != 0; } double interpolate(GribDataSource&, double& lat, double& lon) const; private: double referenceValue_; long binaryScaleFactor_; long decimalScaleFactor_; unsigned long bitsPerValue_; unsigned long offsetBeforeData_; unsigned long offsetBeforeBitmap_; unsigned long numberOfValues_; unsigned long numberOfDataPoints_; long sphericalHarmonics_; eckit::FixedString<32> geographyHash_; void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const GribFieldInfo& f) { f.print(s); return s; } }; } // namespace metkit::pointdb #endif metkit-1.19.2/src/metkit/pointdb/GribDataSource.h0000664000175000017500000000216715232567717022044 0ustar alastairalastair/* * (C) Copyright 1996-2013 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. */ #ifndef metkit_GribDataSource_H #define metkit_GribDataSource_H #include "metkit/pointdb/DataSource.h" #include "metkit/pointdb/PointIndex.h" namespace eckit { class Offset; } namespace metkit { namespace pointdb { class GribFieldInfo; class GribDataSource : public DataSource { public: virtual PointResult extract(double lat, double lon) const; private: virtual double value(size_t index) const; virtual std::string geographyHash() const; virtual eckit::Offset seek(const eckit::Offset&) const = 0; virtual long read(void*, long) const = 0; virtual const GribFieldInfo& info() const = 0; friend class GribFieldInfo; }; } // namespace pointdb } // namespace metkit #endif metkit-1.19.2/src/metkit/pointdb/FieldIndexer.h0000664000175000017500000000426615232567717021552 0ustar alastairalastair/* * (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. */ // File FieldIndex.h // Baudouin Raoult - ECMWF Oct 19 #ifndef metkit_FieldIndex_H #define metkit_FieldIndex_H #include #include namespace eckit { class Value; class JSON; } // namespace eckit namespace metkit { class MarsRequest; namespace pointdb { class DataSource; class DataSourceHandler; //---------------------------------------------------------------------------------------------------------------------- struct FieldIndexerStatus { size_t count_; size_t batch_; FieldIndexerStatus(size_t count, size_t batch) : count_(count), batch_(batch) {} }; class FieldIndexer { public: // -- Exceptions // None // -- Contructors // -- Destructor virtual ~FieldIndexer(); // -- Convertors // None // -- Operators // None // -- Methods virtual FieldIndexerStatus lookup(const eckit::Value&, DataSourceHandler&) const = 0; // -- Overridden methods // None // -- Class members // None // -- Class methods // None // Uncomment for persistent, remove otherwise protected: // -- Members // None // -- Methods virtual void print(std::ostream& s) const = 0; // -- Overridden methods // None // -- Class members // None // -- Class methods // None private: // No copy allowed // -- Members // -- Methods // None // -- Overridden methods // None // -- Class members // None // -- Class methods // None // -- Friends friend std::ostream& operator<<(std::ostream& s, const FieldIndexer& p) { p.print(s); return s; } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace pointdb } // namespace metkit #endif metkit-1.19.2/src/metkit/pointdb/GribHandleDataSource.cc0000664000175000017500000000716215232567717023316 0ustar alastairalastair/* * (C) Copyright 1996-2013 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 #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "eckit/io/Buffer.h" #include "eckit/io/DataHandle.h" #include "eckit/io/PooledHandle.h" #include "eckit/io/StdFile.h" #include "eckit/utils/MD5.h" #include "metkit/pointdb/GribFieldInfo.h" #include "metkit/pointdb/GribHandleDataSource.h" #include "metkit/pointdb/PointIndex.h" namespace metkit { namespace pointdb { GribHandleDataSource::GribHandleDataSource(const eckit::PathName& path, const eckit::Offset& offset) : handle_(path.fileHandle()), ownsHandle_(true), opened_(false), offset_(offset) {} GribHandleDataSource::GribHandleDataSource(eckit::DataHandle& handle, const eckit::Offset& offset) : handle_(&handle), ownsHandle_(false), opened_(false), offset_(offset) {} GribHandleDataSource::GribHandleDataSource(eckit::DataHandle* handle, const eckit::Offset& offset) : handle_(handle), ownsHandle_(true), opened_(false), offset_(offset) { ASSERT(handle_); } GribHandleDataSource::~GribHandleDataSource() { if (opened_) { handle_->close(); } if (ownsHandle_) { delete handle_; } } void GribHandleDataSource::open() const { if (!opened_) { handle_->openForRead(); opened_ = true; } } eckit::Offset GribHandleDataSource::seek(const eckit::Offset& offset) const { open(); eckit::Offset pos = handle_->seek(offset_ + offset); return (long long)pos - (long long)offset_; } long GribHandleDataSource::read(void* buffer, long len) const { open(); return handle_->read(buffer, len); } const GribFieldInfo& GribHandleDataSource::info() const { if (!info_.ready()) { eckit::MD5 md5; md5 << *handle_; md5 << static_cast(offset_); eckit::PathName cache = PointIndex::cachePath("grib-info", md5); if (cache.exists()) { eckit::StdFile f(cache); ASSERT(::fread(&info_, sizeof(info_), 1, f) == 1); f.close(); } else { open(); handle_->seek(offset_); auto codesHandle = codes::codesHandleFromStream( [&](uint8_t* buffer, int64_t len) -> int64_t { return handle_->read(buffer, len); }); ASSERT(codesHandle); info_.update(*codesHandle.get()); cache.dirName().mkdir(); eckit::StdFile f(cache, "w"); ASSERT(::fwrite(&info_, sizeof(info_), 1, f) == 1); f.close(); PointIndex::cache(*codesHandle.get()); } } return info_; } void GribHandleDataSource::print(std::ostream& s) const { s << "GribHandleDataSource[" << *handle_ << "]" << std::endl; } const std::map& GribHandleDataSource::request() const { NOTIMP; // Implement a grib2request like function } std::string GribHandleDataSource::groupKey() const { eckit::MD5 md5; md5 << *handle_; return md5; } std::string GribHandleDataSource::sortKey() const { std::ostringstream oss; oss << std::setfill('0') << std::setw(20) << offset_; return oss.str(); } //---------------------------------------------------------------------------------------------------------------------- } // namespace pointdb } // namespace metkit metkit-1.19.2/src/metkit/pointdb/GribDataSource.cc0000664000175000017500000000245015232567717022175 0ustar alastairalastair/* * (C) Copyright 1996-2013 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 "metkit/pointdb/GribDataSource.h" #include "metkit/pointdb/GribFieldInfo.h" namespace metkit { namespace pointdb { PointResult GribDataSource::extract(double lat, double lon) const { PointResult result; // ASSERT(!source.needInterpolation()); PointIndex& pi = PointIndex::lookUp(geographyHash()); PointIndex::NodeInfo n = pi.nearestNeighbour(lat, lon); result.lat_ = n.point().lat(); result.lon_ = n.point().lon(); result.value_ = value(n.point().payload_); result.source_ = this; return result; } double GribDataSource::value(size_t index) const { return info().value(*this, index); } std::string GribDataSource::geographyHash() const { return info().geographyHash(); } //---------------------------------------------------------------------------------------------------------------------- } // namespace pointdb } // namespace metkit metkit-1.19.2/src/metkit/pointdb/PointIndex.h0000664000175000017500000000555215232567717021270 0ustar alastairalastair/* * (C) Copyright 1996-2013 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. */ #ifndef PointIndex_H #define PointIndex_H #include #include "eckit/container/KDTree.h" #include "eckit/geometry/Point3.h" #include "metkit/codes/api/CodesAPI.h" namespace metkit { namespace pointdb { struct LLPoint2 : public eckit::geometry::Point3 { double lat_; double lon_; size_t payload_; double lat() const { return lat_; } double lon() const { return lon_; } size_t payload() const { return payload_; } const LLPoint2& point() const { return *this; } LLPoint2() : eckit::geometry::Point3() {} LLPoint2(double lat, double lon, size_t index) : eckit::geometry::Point3(), lat_(lat), lon_(lon) { // See http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEF payload_ = index; double& X = x_[0]; double& Y = x_[1]; double& Z = x_[2]; double h = 0; // Altitude const double earthRadius = 6378137.0; double a = earthRadius; // 6378137.0 ; // WGS84 semi-major axis double e2 = 0; // 6.69437999014E-3; // WGS84 first numerical eccentricity sqared double phi = lat / 180.0 * M_PI; double lambda = lon / 180.0 * M_PI; double cos_phi = cos(phi); double sin_phi = sin(phi); double cos_lambda = cos(lambda); double sin_lambda = sin(lambda); double N_phi = a / sqrt(1 - e2 * sin_phi * sin_phi); X = (N_phi + h) * cos_phi * cos_lambda; Y = (N_phi + h) * cos_phi * sin_lambda; Z = (N_phi * (1 - e2) + h) * sin_phi; } friend std::ostream& operator<<(std::ostream& s, const LLPoint2& p) { s << '(' << p.lat_ << "," << p.lon_ << ' ' << p.payload_ << ')'; return s; } }; struct PointIndexTraits { using Point = LLPoint2; using Payload = size_t; }; class PointIndex { public: typedef eckit::KDTreeMapped Tree; typedef Tree::Point Point; typedef Tree::NodeInfo NodeInfo; NodeInfo nearestNeighbour(double lat, double lon); static PointIndex& lookUp(const std::string& md5); static std::string cache(const metkit::codes::CodesHandle& h); static eckit::PathName cachePath(const std::string& dir, const std::string& name); private: PointIndex(const eckit::PathName&, Tree* tree = 0); ~PointIndex(); eckit::PathName path_; std::unique_ptr tree_; std::map last_; eckit::Mutex mutex_; }; } // namespace pointdb } // namespace metkit #endif metkit-1.19.2/src/metkit/pointdb/DataSource.cc0000664000175000017500000000161515232567717021373 0ustar alastairalastair/* * (C) Copyright 1996-2013 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 "metkit/pointdb/DataSource.h" #include "metkit/pointdb/PointIndex.h" namespace metkit { namespace pointdb { DataSource::~DataSource() {} size_t DataSource::batch() const { return 0; } void PointResult::print(std::ostream& s) const { s << "PointResult[lat=" << lat_ << ",lon=" << lon_ << ",value=" << value_ << "]"; } //---------------------------------------------------------------------------------------------------------------------- } // namespace pointdb } // namespace metkit metkit-1.19.2/src/metkit/pointdb/PointIndex.cc0000664000175000017500000001054215232567717021421 0ustar alastairalastair/* * (C) Copyright 1996-2013 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 "eckit/exception/Exceptions.h" // #include "eckit/thread/Mutex.h" // #include "eckit/os/Stat.h" // #include "eckit/log/Timer.h" #include "metkit/pointdb/PointIndex.h" #include "eckit/io/FileHandle.h" #include "eckit/thread/AutoLock.h" // #include "eckit/io/StdFile.h" #include "eckit/config/Resource.h" #include "metkit/codes/api/CodesAPI.h" // #include "eccodes.h" using namespace eckit; namespace metkit { namespace pointdb { static Mutex mutex; static std::set done_; static std::map cache_; eckit::PathName PointIndex::cachePath(const std::string& dir, const std::string& name) { static eckit::PathName pointdbCachePath = eckit::Resource("pointdbCachePath", "~/pointdb"); return pointdbCachePath / dir / name; } std::string PointIndex::cache(const metkit::codes::CodesHandle& h) { std::string md5 = h.getString("md5GridSection"); AutoLock lock(mutex); if (done_.find(md5) != done_.end()) { return md5; } eckit::PathName path = cachePath("grids", md5 + ".kdtree"); if (path.exists()) { done_.insert(md5); return md5; } path.dirName().mkdir(); size_t v = h.size("values"); std::vector p; p.reserve(v); size_t j = 0; for (auto data : h.values()) { while (data.longitude < 0) data.longitude += 360; while (data.longitude >= 360) data.longitude -= 360; p.push_back(Point(data.latitude, data.longitude, j)); j++; } PathName tmp = cachePath("grids", md5 + ".tmp"); tmp.unlink(); Tree* tree = new Tree(tmp, p.size(), 0); tree->build(p.begin(), p.end()); // PathName dump(std::string("/tmp/cache/pointdb/") + md5 + ".dump"); // StdFile f(dump, "w"); // codes_dump_content(h, f, "debug", 0, 0); // f.close(); // Write handle to file PathName grib = cachePath("grids", md5 + ".grib"); eckit::FileHandle fh(grib.localPath()); auto data = h.messageData(); fh.openForWrite(data.size()); fh.write(data.data(), data.size()); fh.close(); PathName::rename(tmp, path); cache_[md5] = new PointIndex(path, tree); done_.insert(md5); return md5; } PointIndex& PointIndex::lookUp(const std::string& md5) { AutoLock lock(mutex); std::map::iterator k = cache_.find(md5); if (k == cache_.end()) { eckit::PathName path = cachePath("grids", md5 + ".kdtree"); if (!path.exists()) { Log::warning() << path << " does not exists" << std::endl; PathName grib = cachePath("grids", md5 + ".grib"); if (grib.exists()) { Log::warning() << "Rebuilding index from " << grib << std::endl; auto codesHandle = codes::codesHandleFromFile(grib.localPath(), codes::Product::GRIB); ASSERT(cache(*codesHandle.get()) == md5); } } Log::warning() << "Loading " << path << std::endl; PointIndex* p = new PointIndex(path); cache_[md5] = p; return *p; } return *(*k).second; } PointIndex::PointIndex(const PathName& path, PointIndex::Tree* tree) : path_(path), tree_(tree) { if (!tree) { Log::info() << "Load tree " << path << std::endl; ASSERT(path.exists()); tree_.reset(new Tree(path, 0, 0)); } } PointIndex::~PointIndex() { // TODO } PointIndex::NodeInfo PointIndex::nearestNeighbour(double lat, double lon) { Point p(lat, lon, 0); std::map::iterator k; { AutoLock lock(mutex_); k = last_.find(p); if (k != last_.end()) return (*k).second; } NodeInfo n; { Timer timer("Find nearest"); n = tree_->nearestNeighbour(p); } { AutoLock lock(mutex_); if (last_.size() >= 4096) last_.clear(); last_[p] = n; } return n; } } // namespace pointdb } // namespace metkit metkit-1.19.2/src/metkit/metkit_config.h.in0000664000175000017500000000137315232567717020774 0ustar alastairalastair/* * (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. */ #ifndef metkit_config_h #define metkit_config_h #include "metkit_ecbuild_config.h" /* generated by ecbuild */ /* variables */ #cmakedefine metkit_HAVE_GRIB #cmakedefine metkit_HAVE_NETCDF #cmakedefine metkit_HAVE_BUFR #cmakedefine metkit_HAVE_ODB #cmakedefine metkit_HAVE_FAIL_ON_CCSDS #cmakedefine metkit_HAVE_MARS2MARS /* packages */ #endif /* metkit_config_h */ metkit-1.19.2/src/metkit/grib2mars/0000775000175000017500000000000015232567717017260 5ustar alastairalastairmetkit-1.19.2/src/metkit/grib2mars/utils/0000775000175000017500000000000015232567717020420 5ustar alastairalastairmetkit-1.19.2/src/metkit/grib2mars/utils/paramMatcher.h0000664000175000017500000000257615232567717023207 0ustar alastairalastair/* * (C) Copyright 2026- 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 paramMatcher.h /// @brief Helpers for compact parameter matching in rule tables. #pragma once namespace metkit::grib2mars::util::param_matcher { /// @brief Inclusive integer range used by parameter predicates. struct Range { int first; int last; /// @brief Check whether a value lies inside the range. bool contains(int x) const { return x >= first && x <= last; } }; /// @brief Construct an inclusive integer range. inline Range range(int first, int last) { return {first, last}; } /// @brief Match a value against a range argument. inline bool matchSingle(int x, const Range& arg) { return arg.contains(x); } /// @brief Match a value against a scalar argument. inline bool matchSingle(int x, int y) { return x == y; } /// @brief Match a value against any supplied argument. template bool matchAny(int value, T... arg) { return (matchSingle(value, arg) || ...); } } // namespace metkit::grib2mars::util::param_matcher metkit-1.19.2/src/metkit/grib2mars/utils/logUtils.h0000664000175000017500000000534015232567717022375 0ustar alastairalastair/// @file logUtils.h /// @brief Logging macros used by grib2mars. #pragma once #include "eckit/log/Log.h" #include "metkit/config/LibMetkit.h" #include "metkit/grib2mars/utils/generalUtils.h" /// @brief Log a validation or guard check. #define GRIB2MARS_LOG_CHECK(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " GRIB2MARS-CHECK: " << msg << std::endl; \ } while (0) /// @brief Log a successful match. #define GRIB2MARS_LOG_MATCH(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " GRIB2MARS-MATCH: " << msg << std::endl; \ } while (0) /// @brief Log a resolved value. #define GRIB2MARS_LOG_RESOLVE(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " GRIB2MARS-RESOLVE: " << msg << std::endl; \ } while (0) /// @brief Log an overridden value. #define GRIB2MARS_LOG_OVERRIDE(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " GRIB2MARS-OVERRIDE: " << msg << std::endl; \ } while (0) /// @brief Log a defaulted value. #define GRIB2MARS_LOG_DEFAULT(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " GRIB2MARS-DEFAULT: " << msg << std::endl; \ } while (0) /// @brief Log a concept invocation. #define GRIB2MARS_LOG_CONCEPT(CONCEPTNAME) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " GRIB2MARS-CONCEPT:" \ << "[Concept " << std::string(CONCEPTNAME##Name) << "] " \ << "Op called: " \ << "Stage=" << Stage << ", " \ << "Section=" << Section << ", " \ << "Variant=" << std::string(CONCEPTNAME##TypeName()) << std::endl; \ } while (0) metkit-1.19.2/src/metkit/grib2mars/utils/generalUtils.h0000664000175000017500000000027715232567717023235 0ustar alastairalastair/// @file generalUtils.h /// @brief Small utility macros shared by grib2mars. #pragma once /// @brief Mark a code path as unreachable. #define grib2marsUnreachable() __builtin_unreachable() metkit-1.19.2/src/metkit/grib2mars/utils/grib2marsExceptions.h0000664000175000017500000001303515232567717024525 0ustar alastairalastair/* * (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 grib2marsExceptions.h /// @brief Unified exception hierarchy for the grib2mars framework. /// /// This header defines the complete exception model used across /// grib2mars, covering: /// /// - Generic infrastructure errors /// - Layer-specific failures (matcher, rules, validation, tables, deduction) /// - Concept execution failures (with contextual metadata) /// - Encoder failures (with serialized dictionary state) /// /// The hierarchy is designed with the following goals: /// /// - Strong contextual diagnostics /// - Support for nested exception propagation /// - Structured debug frame printing /// - Clear separation between backend and frontend layers /// /// All exceptions ultimately derive from `eckit::Exception`, /// ensuring compatibility with the broader ECMWF ecosystem. /// /// Nested exception support allows propagation chains to be /// printed in a structured stack-like format. /// /// @ingroup grib2mars_utils #pragma once // System includes #include #include #include #include // Project includes #include "eckit/exception/Exceptions.h" #include "metkit/config/LibMetkit.h" #include "metkit/grib2mars/utils/generalUtils.h" namespace metkit::grib2mars::utils::exceptions { /// @brief Base exception for grib2mars. /// /// This is the root exception type for most grib2mars failures. /// It: /// /// - Inherits from `eckit::Exception` /// - Supports nested exceptions via `std::nested_exception` /// - Provides structured frame printing /// /// Derived exceptions typically extend this class with /// additional contextual metadata. /// /// The `printFrame()` method is designed to be used by /// extended stack printers. class Grib2MarsGenericException : public eckit::Exception, public std::nested_exception { public: Grib2MarsGenericException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : eckit::Exception(reason, loc) {} virtual ~Grib2MarsGenericException() = default; virtual void printFrame(const std::string& pad, std::ostream& os) const { const auto& loc = location(); os << pad << "+ file: " << loc.file() << "\n" << pad << "+ function: " << loc.func() << "\n" << pad << "+ line: " << loc.line() << "\n" << pad << "+ link: " << loc.file() << ":" << loc.line() << "\n" << pad << "+ message: " << what() << "\n"; } }; /// @brief Exception raised in the rules layer. /// /// Used when evaluating rule-based logic fails. /// /// This class derives directly from `eckit::Exception` and /// supports nested exceptions. class Grib2MarsRulesException : public eckit::Exception, public std::nested_exception { public: Grib2MarsRulesException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : eckit::Exception(reason, loc) {} }; /// @brief Exception raised in the dictionary access layer. /// /// Used when dictionary validation or access fails. /// Inherits structured printing from the generic exception. class Grib2MarsDictException : public Grib2MarsGenericException { public: Grib2MarsDictException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Grib2MarsGenericException(reason, loc) {} }; inline constexpr int tabSize = 4; inline constexpr int lineSize = 120; inline std::string indent(std::size_t level) { return std::string(level * tabSize, ' '); } /// @brief Print structured exception stack with detailed frames. /// /// For each nested exception frame: /// /// - Prints file, function, line, and message (if available) /// - Prints additional metadata for specialized exceptions /// /// This function detects `Grib2MarsGenericException` /// via `dynamic_cast` and calls `printFrame()` /// to extract structured information. /// /// Nested exceptions are recursively printed. /// /// @param e Root exception /// @param level Indentation level /// @param frame Frame counter inline void printExtendedStack(const std::exception& e, std::ostream& os, std::size_t level = 0, std::size_t frame = 1) { const std::string pad = indent(level); os << pad << "+ " << std::string(lineSize, '=') << std::endl << pad << "+ frame " << frame << std::endl << pad << "+ " << std::string(lineSize, '-') << std::endl; if (const auto* me = dynamic_cast(&e)) { me->printFrame(pad, os); } else { os << pad << "+ message: " << e.what() << std::endl; } os << pad << "+ " << std::string(lineSize, '+') << std::endl; const auto* nested = dynamic_cast(&e); if (nested == nullptr) { return; } const std::exception_ptr nestedPtr = nested->nested_ptr(); if (!nestedPtr) { return; } try { std::rethrow_exception(nestedPtr); } catch (const std::exception& nestedException) { printExtendedStack(nestedException, os, level + 1, frame + 1); } catch (...) { os << std::string(level + 1, ' ') << "[nested non-std exception]" << std::endl; } } } // namespace metkit::grib2mars::utils::exceptions metkit-1.19.2/src/metkit/grib2mars/utils/type_traits_name.h0000664000175000017500000001057515232567717024150 0ustar alastairalastair/* * (C) Copyright 2026- 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 type_traits_name.h /// @brief Human-readable type names used in diagnostics. #pragma once #include #include #include #include #include #include #include "metkit/codes/api/CodesTypes.h" #include "metkit/grib2mars/utils/generalUtils.h" namespace metkit::grib2mars::utils { /// @brief Return a readable name for `T`. // ============================================================ // Primary template (deferred to fallback demangling) // ============================================================ template constexpr std::string_view type_name(); // ============================================================ // Base type specializations // ============================================================ template <> constexpr std::string_view type_name() { return "string"; } template <> constexpr std::string_view type_name() { return "bool"; } template <> constexpr std::string_view type_name() { return "char"; } template <> constexpr std::string_view type_name() { return "signed char"; } template <> constexpr std::string_view type_name() { return "unsigned char"; } template <> constexpr std::string_view type_name() { return "int"; } template <> constexpr std::string_view type_name() { return "long"; } template <> constexpr std::string_view type_name() { return "long long"; } template <> constexpr std::string_view type_name() { return "unsigned int"; } template <> constexpr std::string_view type_name() { return "unsigned long"; } template <> constexpr std::string_view type_name() { return "unsigned long long"; } template <> constexpr std::string_view type_name() { return "float"; } template <> constexpr std::string_view type_name() { return "double"; } template <> constexpr std::string_view type_name() { return "long double"; } // ============================================================ // std::vector specializations // ============================================================ template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector_view"; } template <> constexpr std::string_view type_name>() { return "vector_view"; } } // namespace metkit::grib2mars::utils metkit-1.19.2/src/metkit/grib2mars/utils/dictionary_traits/0000775000175000017500000000000015232567717024153 5ustar alastairalastairmetkit-1.19.2/src/metkit/grib2mars/utils/dictionary_traits/dictaccess_eckit_configuration.h0000664000175000017500000005074415232567717032551 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include #include #include #include #include "eckit/config/LocalConfiguration.h" #include "metkit/grib2mars/utils/generalUtils.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" #include "metkit/grib2mars/utils/type_traits_name.h" // ============================================================================ // Helper macros to reduce boilerplate (similar to LocalConfiguration version) // ============================================================================ // ============================================================== // GET_OR_THROW // ============================================================== #define G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(CTYPE, ISFUNC, GETFUNC) \ template <> \ struct DictGetOrThrow { \ \ static CTYPE get_or_throw(const eckit::LocalConfiguration& cfg, std::string_view key) noexcept(false) { \ const std::string k{key}; \ \ try { \ /* Check key exists */ \ if (!cfg.has(k)) { \ throw exceptions::Grib2MarsDictException("Missing key `"s + k + "` in dictionary type `"s + \ std::string(type_name()) + \ "`", \ Here()); \ } \ \ /* Check type */ \ if (hacks::ISFUNC(cfg, k)) { \ return cfg.GETFUNC(k); \ } \ else { \ throw exceptions::Grib2MarsDictException( \ "Key `"s + k + "` is not of expected type `"s + std::string(type_name()) + \ "` for dictionary type `"s + std::string(type_name()) + "`", \ Here()); \ } \ } \ catch (const exceptions::Grib2MarsGenericException&) { \ throw; \ } \ catch (...) { \ std::throw_with_nested(exceptions::Grib2MarsDictException( \ "Internal error while reading key `"s + k + "` as `" + std::string(type_name()) + \ "` from dictionary type `"s + std::string(type_name()) + "`", \ Here())); \ } \ grib2marsUnreachable(); \ } \ }; // ============================================================== // GET_OPT // ============================================================== #define G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(CTYPE, ISFUNC, GETFUNC) \ template <> \ struct DictGetOpt { \ static std::optional get_opt(const eckit::LocalConfiguration& cfg, \ std::string_view key) noexcept(false) { \ const std::string k{key}; \ \ try { \ if (!cfg.has(k)) { \ return std::nullopt; \ } \ \ if (hacks::ISFUNC(cfg, k)) { \ return cfg.GETFUNC(k); \ } \ else { \ return std::nullopt; \ } \ } \ catch (...) { \ return std::nullopt; \ } \ grib2marsUnreachable(); \ } \ }; // ============================================================== // SET_OR_THROW // ============================================================== #define G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(CTYPE, SETFUNC) \ template <> \ struct DictSetOrIgnore { \ static void set_or_ignore(eckit::LocalConfiguration& cfg, std::string_view key, \ const CTYPE& value) noexcept(false) { \ const std::string k{key}; \ \ try { \ cfg.SETFUNC(k, value); \ return; \ } \ catch (...) { \ } \ grib2marsUnreachable(); \ } \ }; // ============================================================== // SET_OR_IGNORE // ============================================================== #define G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(CTYPE, SETFUNC) \ template <> \ struct DictSetOrThrow { \ \ static void set_or_throw(eckit::LocalConfiguration& cfg, std::string_view key, \ const CTYPE& value) noexcept(false) { \ const std::string k{key}; \ \ try { \ cfg.SETFUNC(k, value); \ return; \ } \ catch (const exceptions::Grib2MarsGenericException&) { \ throw; \ } \ catch (...) { \ std::throw_with_nested(exceptions::Grib2MarsDictException( \ "Unable to set key `"s + k + "` with type `"s + std::string(type_name()) + \ "` in dictionary type `"s + std::string(type_name()) + "`", \ Here())); \ } \ grib2marsUnreachable(); \ } \ }; namespace metkit::grib2mars::utils { // ----------------------------------------------------------------------------- // type_name specialisation // ----------------------------------------------------------------------------- template <> constexpr std::string_view type_name() { return "eckit::LocalConfiguration"; } template <> constexpr std::string_view type_name>() { return "vector"; } } // namespace metkit::grib2mars::utils namespace metkit::grib2mars::utils::dict_traits { using std::operator""s; namespace hacks { inline bool isIntegral(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isIntegral(std::string{key}); } inline bool isFloatingPoint(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isFloatingPoint(std::string{key}) || conf.isIntegral(std::string{key}); } inline bool isBoolean(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isBoolean(std::string{key}); } inline bool isString(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isString(std::string{key}); } inline bool isSubConfiguration(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isSubConfiguration(std::string{key}); } inline bool isIntegralList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isIntegralList(std::string{key}); } inline bool isFloatingPointList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isFloatingPointList(std::string{key}) || conf.isIntegralList(std::string{key}); } inline bool isStringList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isStringList(std::string{key}); } inline bool isSubConfigurationList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isSubConfigurationList(std::string{key}); } } // namespace hacks // ----------------------------------------------------------------------------- // to_json // ----------------------------------------------------------------------------- template <> struct DictToJsonTraits { static std::string to_json(const eckit::LocalConfiguration& cfg) noexcept(true) { try { std::ostringstream os; os << cfg; return os.str(); } catch (...) { return "[to_json failed for eckit::LocalConfiguration]"; } } }; // ----------------------------------------------------------------------------- // DictCreateFromSample / Clone / NeedsChecks // ----------------------------------------------------------------------------- template <> struct DictTraits { static constexpr bool support_checks = false; static std::unique_ptr make_from_sample_or_throw(std::string_view name) { auto cfg = std::make_unique(); cfg->set("SampleName", std::string(name)); return cfg; } static std::unique_ptr clone_or_throw(const eckit::LocalConfiguration& cfg) { return std::make_unique(cfg); } }; // ----------------------------------------------------------------------------- // DictHas // ----------------------------------------------------------------------------- template <> struct DictHas { static bool has(const eckit::LocalConfiguration& cfg, std::string_view key) noexcept(false) { const std::string k{key}; try { return cfg.has(k); } catch (const exceptions::Grib2MarsGenericException&) { throw; } catch (...) { std::throw_with_nested(exceptions::Grib2MarsDictException( "Internal error while checking presence of key `"s + k + "` in dictionary type `"s + std::string(type_name()) + "`", Here())); } grib2marsUnreachable(); } }; // ============================================================ // eckit::LocalConfiguration specializations via macros // ============================================================ //------------------------------------------------------------------------------ // Scalar types //------------------------------------------------------------------------------ // bool G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(bool, isBoolean, getBool) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(bool, isBoolean, getBool) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(bool, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(bool, set) // int G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(int, isIntegral, getInt) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(int, isIntegral, getInt) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(int, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(int, set) // long G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(long, isIntegral, getLong) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(long, isIntegral, getLong) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(long, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(long, set) // float G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(float, isFloatingPoint, getFloat) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(float, isFloatingPoint, getFloat) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(float, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(float, set) // double G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(double, isFloatingPoint, getDouble) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(double, isFloatingPoint, getDouble) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(double, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(double, set) // std::string G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::string, isString, getString) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::string, isString, getString) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::string, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::string, set) // eckit::LocalConfiguration (sub-configuration) G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(eckit::LocalConfiguration, isSubConfiguration, getSubConfiguration) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(eckit::LocalConfiguration, isSubConfiguration, getSubConfiguration) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(eckit::LocalConfiguration, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(eckit::LocalConfiguration, set) //------------------------------------------------------------------------------ // Vector types //------------------------------------------------------------------------------ // std::vector G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isIntegralList, getIntVector) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isIntegralList, getIntVector) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isIntegralList, getLongVector) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isIntegralList, getLongVector) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isFloatingPointList, getFloatVector) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isFloatingPointList, getFloatVector) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isFloatingPointList, getDoubleVector) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isFloatingPointList, getDoubleVector) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isStringList, getStringVector) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isStringList, getStringVector) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector G2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isSubConfigurationList, getSubConfigurations) G2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isSubConfigurationList, getSubConfigurations) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) G2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) } // namespace metkit::grib2mars::utils::dict_traits metkit-1.19.2/src/metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h0000664000175000017500000002231615232567717031404 0ustar alastairalastair/* * (C) Copyright 2026- 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 dictionary_access_traits.h /// @brief Generic dictionary access traits for grib2mars. #pragma once #include #include #include #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "metkit/grib2mars/utils/generalUtils.h" // Exceptions #include "metkit/config/LibMetkit.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" #include "metkit/grib2mars/utils/type_traits_name.h" namespace metkit::grib2mars::utils::dict_traits { using std::operator""s; template struct dependent_false : std::false_type {}; /// @brief Trait hook used to render a dictionary as JSON. template struct DictToJsonTraits { static std::string to_json(const Dict&) { return std::string{"[to_json not supported for this dictionary type]"}; } static void dump_or_ignore(const Dict&, const std::string&) { LOG_DEBUG_LIB(LibMetkit) << to_json(std::declval()); } }; /// @brief Trait hook for cloning and sample creation. template struct DictTraits { static constexpr bool support_checks = false; static std::unique_ptr make_from_sample_or_throw(std::string_view) { static_assert(dependent_false::value, "DictTraits::make_from_sample_or_throw not specialized"); } static std::unique_ptr clone_or_throw(const Dict&) { static_assert(dependent_false::value, "DictTraits::clone_or_throw not specialized"); } }; /// @brief Trait hook for key presence checks. template struct DictHas { static bool has(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictHas not specialized for this Dict"); grib2marsUnreachable(); } }; /// @brief Trait hook for missing-value checks. template struct DictMissing { static bool isMissing(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictMissing not specialized for this Dict"); grib2marsUnreachable(); } static void setMissing(Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictMissing not specialized for this Dict"); grib2marsUnreachable(); } }; /// @brief Trait hook for optional value retrieval. template struct DictGetOpt { static std::optional get_opt(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictGetOpt not specialized for this Dict and type"); grib2marsUnreachable(); } }; /// @brief Trait hook for mandatory value retrieval. template struct DictGetOrThrow { static T get_or_throw(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictGetOrThrow not specialized for this Dict and type"); grib2marsUnreachable(); } }; /// @brief Trait hook for assignment that may be ignored. template struct DictSetOrIgnore { static void set_or_ignore(Dict&, std::string_view, const T&) noexcept(false) { static_assert(dependent_false::value, "DictSetOrIgnore not specialized for this Dict and type"); grib2marsUnreachable(); } }; /// @brief Trait hook for assignment that must succeed. template struct DictSetOrThrow { static void set_or_throw(Dict&, std::string_view, const T&) noexcept(false) { static_assert(dependent_false::value, "DictSetOrThrow not specialized for this Dict and type"); grib2marsUnreachable(); } }; // ============================================================ // dict_to_json // ============================================================ /// @brief Convert a dictionary to JSON using the active traits. template std::string dict_to_json(const Dict& d) { return DictToJsonTraits::to_json(d); } // ============================================================ // clone / make_from_sample / needs_checks // ============================================================ /// @brief Report whether a dictionary supports consistency checks. template inline constexpr bool dict_supports_checks_v = DictTraits::support_checks; /// @brief Create a dictionary from a sample name. template std::unique_ptr make_from_sample_or_throw(std::string_view name) { return DictTraits::make_from_sample_or_throw(name); } /// @brief Clone a supported dictionary. template std::unique_ptr clone_or_throw(const Dict& d) { return DictTraits::clone_or_throw(d); } /// @brief Render a dictionary or ignore unsupported types. template void dump_or_ignore(const Dict& d, const std::string& f) { DictToJsonTraits::dump_or_ignore(d, f); } // ============================================================ // has / isMissing / setMissing // ============================================================ // has(dict,key) /// @brief Check whether a key exists in a dictionary. template inline bool has(const Dict& dict, std::string_view key) { return DictHas::has(dict, key); } // has(dict,key) /// @brief Check whether a typed value exists in a dictionary. template inline bool has(const Dict& dict, std::string_view key) { return DictGetOpt::get_opt(dict, key).has_value(); } // isMissing(dict,key) /// @brief Check whether a key is semantically missing. template inline bool isMissing(const Dict& dict, std::string_view key) { return DictMissing::isMissing(dict, key); } // setMissing(dict,key) /// @brief Mark a key as missing. template inline void setMissing_or_throw(Dict& dict, std::string_view key) { DictMissing::setMissing(dict, key); return; } // check(dict,key,cond) -> bool /// @brief Evaluate a predicate on a typed optional value. template inline bool check(const Dict& dict, std::string_view key, Cond&& condition) { if (auto v = DictGetOpt::get_opt(dict, key); v.has_value()) { return std::forward(condition)(*v); } return false; } // ============================================================ // GET UTILITIES // ============================================================ // get_or_throw(dict,key) -> T /// @brief Retrieve a mandatory typed value from a dictionary. template inline T get_or_throw(const Dict& dict, std::string_view key) { try { return DictGetOrThrow::get_or_throw(dict, key); } catch (...) { std::throw_with_nested( exceptions::Grib2MarsDictException("Forwarding errors while getting key `"s + std::string(key) + "` as `" + std::string(type_name()) + "` from dictionary`"s, Here())); grib2marsUnreachable(); } grib2marsUnreachable(); } // get(dict,key) -> std::optional /// @brief Retrieve an optional typed value from a dictionary. template inline std::optional get_opt(const Dict& dict, std::string_view key) { try { return DictGetOpt::get_opt(dict, key); } catch (...) { return std::nullopt; } grib2marsUnreachable(); } // ============================================================ // SET UTILITIES // ============================================================ // set(dict,key,value) /// @brief Set a typed value in a dictionary and throw on failure. template inline void set_or_throw(Dict& dict, std::string_view key, const T& value) { try { DictSetOrThrow::set_or_throw(dict, key, value); return; } catch (...) { std::throw_with_nested(exceptions::Grib2MarsDictException("Forwarding errors while setting key `"s + std::string(key) + "` as `" + std::string(type_name()) + "` to dictionary`"s, Here())); grib2marsUnreachable(); } grib2marsUnreachable(); } /// @brief Set a typed value in a dictionary and ignore failures. template inline void set_or_ignore(Dict& dict, std::string_view key, const T& value) { try { DictSetOrIgnore::set_or_ignore(dict, key, value); return; } catch (...) { // ignore exceptions grib2marsUnreachable(); } grib2marsUnreachable(); } } // namespace metkit::grib2mars::utils::dict_traits metkit-1.19.2/src/metkit/grib2mars/utils/dictionary_traits/dictaccess_mars_request.h0000664000175000017500000005654315232567717031240 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "eckit/log/JSON.h" #include "metkit/mars/MarsRequest.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/generalUtils.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" #include "metkit/grib2mars/utils/type_traits_name.h" namespace metkit::grib2mars::utils { template <> constexpr std::string_view type_name() { return "metkit::mars::MarsRequest"; } } // namespace metkit::grib2mars::utils namespace metkit::grib2mars::utils::dict_traits { using std::operator""s; namespace detail { inline const std::string& scalarStringOrThrow(const metkit::mars::MarsRequest& mars, std::string_view key) { const std::string k{key}; if (!mars.has(k)) { throw exceptions::Grib2MarsDictException("Missing key `"s + k + "` in metkit::mars::MarsRequest", Here()); } const auto& values = mars.values(k); if (values.size() != 1) { throw exceptions::Grib2MarsDictException( "Invalid non-scalar key `"s + k + "` in metkit::mars::MarsRequest: found `"s + std::to_string(values.size()) + "` values. grib2mars expects a single MARS point, not a hypercube.", Here()); } return values.front(); } inline std::optional scalarStringOpt(const metkit::mars::MarsRequest& mars, std::string_view key) { const std::string k{key}; if (!mars.has(k)) { return std::nullopt; } const auto& values = mars.values(k); if (values.empty()) { return std::nullopt; } if (values.size() != 1) { throw exceptions::Grib2MarsDictException( "Invalid non-scalar key `"s + k + "` in metkit::mars::MarsRequest: found `"s + std::to_string(values.size()) + "` values. grib2mars expects a single MARS point, not a hypercube.", Here()); } return values.front(); } inline long parsePlainLong(std::string_view value, std::string_view key) { const std::string s{value}; std::size_t pos = 0; long result = 0; try { result = std::stol(s, &pos); } catch (...) { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to long", Here()); } if (pos != s.size()) { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to long without loss", Here()); } return result; } inline double parsePlainDouble(std::string_view value, std::string_view key) { const std::string s{value}; std::size_t pos = 0; double result = 0.0; try { result = std::stod(s, &pos); } catch (...) { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to double", Here()); } if (pos != s.size()) { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to double without loss", Here()); } return result; } inline long parseHoursAsLong(std::string_view value, std::string_view key) { const std::string s{value}; if (s.empty()) { throw exceptions::Grib2MarsDictException("Cannot convert empty key `"s + std::string{key} + "` value to hours", Here()); } if (s.back() == 'h') { return parsePlainLong(std::string_view{s.data(), s.size() - 1}, key); } if (s.back() == 'm' || s.back() == 's') { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to hours: sub-hourly extents are not supported by this grib2mars path", Here()); } // Existing workflows treat bare numeric step/timespan as hours. return parsePlainLong(s, key); } inline std::string longToHours(long value) { return std::to_string(value) + "h"; } inline std::string doubleToString(double value) { std::ostringstream os; os.precision(std::numeric_limits::max_digits10); os << value; return os.str(); } // ----------------------------------------------------------------------------- // long converters // ----------------------------------------------------------------------------- using ToLong = long (*)(std::string_view value, std::string_view key); using FromLong = std::string (*)(long value); inline long paramToLong(std::string_view value, std::string_view key) { // Conservative first implementation: // param=130 -> OK // param=228.128 -> throw // // If later you need "228.128" -> some canonical long encoding, // add that rule here, not in generic parsing. // std::cout << "Parsing param value `" << value << "` as long for key `" << key << "`" << std::endl; return parsePlainLong(value, key); } inline long plainLong(std::string_view value, std::string_view key) { return parsePlainLong(value, key); } inline long hoursLong(std::string_view value, std::string_view key) { return parseHoursAsLong(value, key); } inline std::string plainLongString(long value) { return std::to_string(value); } inline bool allDigits(std::string_view s) { return std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); }); } inline bool isLeapYear(long year) { return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); } inline long daysInMonth(long year, long month) { switch (month) { case 1: return 31; case 2: return isLeapYear(year) ? 29 : 28; case 3: return 31; case 4: return 30; case 5: return 31; case 6: return 30; case 7: return 31; case 8: return 31; case 9: return 30; case 10: return 31; case 11: return 30; case 12: return 31; default: return 0; } } inline void checkYYYYMMDD(long yyyymmdd, std::string_view key) { const long year = yyyymmdd / 10000; const long month = (yyyymmdd / 100) % 100; const long day = yyyymmdd % 100; if (year <= 0 || month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) { throw exceptions::Grib2MarsDictException("Invalid MARS date `"s + std::to_string(yyyymmdd) + "` for key `"s + std::string{key} + "`. Expected a valid yyyymmdd date.", Here()); } } inline long marsDateToLong(std::string_view value, std::string_view key) { const std::string s{value}; std::string compact; if (s.size() == 8 && allDigits(s)) { compact = s; } else if (s.size() == 10 && s[4] == '-' && s[7] == '-' && allDigits(std::string_view{s.data(), 4}) && allDigits(std::string_view{s.data() + 5, 2}) && allDigits(std::string_view{s.data() + 8, 2})) { compact.reserve(8); compact.append(s, 0, 4); compact.append(s, 5, 2); compact.append(s, 8, 2); } else { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to MARS date integer yyyymmdd. Supported forms are yyyymmdd and yyyy-mm-dd.", Here()); } const long result = parsePlainLong(compact, key); checkYYYYMMDD(result, key); return result; } inline std::string longToMarsDate(long value) { const std::string s = std::to_string(value); if (s.size() != 8 || !allDigits(s)) { throw exceptions::Grib2MarsDictException("Cannot convert integer `"s + s + "` to MARS date. Expected yyyymmdd.", Here()); } checkYYYYMMDD(value, "date"); return s; } inline long parseTimePart(std::string_view part, std::string_view full, std::string_view key) { if (part.empty() || !allDigits(part)) { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + std::string{full} + "` to MARS time.", Here()); } return parsePlainLong(part, key); } inline long makeHHMMSS(long hh, long mm, long ss, std::string_view value, std::string_view key) { if (hh < 0 || hh > 23 || mm < 0 || mm > 59 || ss < 0 || ss > 59) { throw exceptions::Grib2MarsDictException("Invalid MARS time `"s + std::string{value} + "` for key `"s + std::string{key} + "`. Expected hh=[0,23], mm=[0,59], ss=[0,59].", Here()); } if (ss != 0) { throw exceptions::Grib2MarsDictException("Invalid MARS time `"s + std::string{value} + "` for key `"s + std::string{key} + "`. Seconds are not supported by metkit MARS time normalisation.", Here()); } return hh * 10000 + mm * 100 + ss; } inline long parseSeparatedMarsTime(std::string_view value, std::string_view key, char sep) { const auto p0 = value.find(sep); if (p0 == std::string_view::npos) { throw exceptions::Grib2MarsDictException( "Internal error while parsing separated MARS time `"s + std::string{value} + "`", Here()); } const auto p1 = value.find(sep, p0 + 1); const auto hhPart = value.substr(0, p0); if (p1 == std::string_view::npos) { const auto mmPart = value.substr(p0 + 1); const long hh = parseTimePart(hhPart, value, key); const long mm = parseTimePart(mmPart, value, key); return makeHHMMSS(hh, mm, 0, value, key); } if (value.find(sep, p1 + 1) != std::string_view::npos) { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + std::string{value} + "` to MARS time.", Here()); } const auto mmPart = value.substr(p0 + 1, p1 - p0 - 1); const auto ssPart = value.substr(p1 + 1); const long hh = parseTimePart(hhPart, value, key); const long mm = parseTimePart(mmPart, value, key); const long ss = parseTimePart(ssPart, value, key); return makeHHMMSS(hh, mm, ss, value, key); } inline long marsTimeToLong(std::string_view value, std::string_view key) { const std::string s{value}; if (s.empty()) { throw exceptions::Grib2MarsDictException( "Cannot convert empty key `"s + std::string{key} + "` value to MARS time.", Here()); } if (s.find(':') != std::string::npos) { return parseSeparatedMarsTime(s, key, ':'); } if (s.find('-') != std::string::npos) { return parseSeparatedMarsTime(s, key, '-'); } if (!allDigits(s)) { throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to MARS time integer hhmmss.", Here()); } if (s.size() <= 2) { // H or HH -> HH:00:00 const long hh = parsePlainLong(s, key); return makeHHMMSS(hh, 0, 0, value, key); } if (s.size() <= 4) { // HMM or HHMM -> HH:MM:00 std::string hhmm = s; hhmm.insert(hhmm.begin(), 4 - hhmm.size(), '0'); const long hh = parsePlainLong(std::string_view{hhmm.data(), 2}, key); const long mm = parsePlainLong(std::string_view{hhmm.data() + 2, 2}, key); return makeHHMMSS(hh, mm, 0, value, key); } if (s.size() <= 6) { // HMMSS or HHMMSS -> HH:MM:SS std::string hhmmss = s; hhmmss.insert(hhmmss.begin(), 6 - hhmmss.size(), '0'); const long hh = parsePlainLong(std::string_view{hhmmss.data(), 2}, key); const long mm = parsePlainLong(std::string_view{hhmmss.data() + 2, 2}, key); const long ss = parsePlainLong(std::string_view{hhmmss.data() + 4, 2}, key); return makeHHMMSS(hh, mm, ss, value, key); } throw exceptions::Grib2MarsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to MARS time integer hhmmss.", Here()); } inline std::string longToMarsTime(long value) { if (value < 0 || value > 235959) { throw exceptions::Grib2MarsDictException( "Cannot convert integer `"s + std::to_string(value) + "` to MARS time. Expected hhmmss.", Here()); } std::ostringstream os; os << std::setw(6) << std::setfill('0') << value; const std::string hhmmss = os.str(); const long hh = parsePlainLong(std::string_view{hhmmss.data(), 2}, "time"); const long mm = parsePlainLong(std::string_view{hhmmss.data() + 2, 2}, "time"); const long ss = parsePlainLong(std::string_view{hhmmss.data() + 4, 2}, "time"); makeHHMMSS(hh, mm, ss, hhmmss, "time"); // metkit canonical MARS time is HHMM, not HHMMSS. return hhmmss.substr(0, 4); } inline const std::unordered_map& toLongConverters() { static const std::unordered_map converters = { {"param", paramToLong}, {"levelist", plainLong}, {"chem", plainLong}, {"step", hoursLong}, {"timespan", hoursLong}, {"date", marsDateToLong}, {"hdate", marsDateToLong}, {"time", marsTimeToLong}, {"htime", marsTimeToLong}, }; return converters; } inline const std::unordered_map& fromLongConverters() { static const std::unordered_map converters = { {"param", plainLongString}, {"levelist", plainLongString}, {"chem", plainLongString}, {"step", longToHours}, {"timespan", longToHours}, {"date", longToMarsDate}, {"hdate", longToMarsDate}, {"time", longToMarsTime}, {"htime", longToMarsTime}, }; return converters; } inline long convertToLongOrThrow(std::string_view key, std::string_view value) { const std::string k{key}; const auto& converters = toLongConverters(); const auto it = converters.find(k); if (it == converters.end()) { throw exceptions::Grib2MarsDictException( "Key `"s + k + "` does not support conversion to long for metkit::mars::MarsRequest", Here()); } return it->second(value, key); } inline std::string convertFromLongOrThrow(std::string_view key, long value) { const std::string k{key}; const auto& converters = fromLongConverters(); const auto it = converters.find(k); if (it == converters.end()) { throw exceptions::Grib2MarsDictException( "Key `"s + k + "` does not support assignment from long for metkit::mars::MarsRequest", Here()); } return it->second(value); } // ----------------------------------------------------------------------------- // double converters // ----------------------------------------------------------------------------- using ToDouble = double (*)(std::string_view value, std::string_view key); using FromDouble = std::string (*)(double value); inline const std::unordered_map& toDoubleConverters() { static const std::unordered_map converters = { {"wavelength", parsePlainDouble}, }; return converters; } inline const std::unordered_map& fromDoubleConverters() { static const std::unordered_map converters = { {"wavelength", doubleToString}, }; return converters; } inline double convertToDoubleOrThrow(std::string_view key, std::string_view value) { const std::string k{key}; const auto& converters = toDoubleConverters(); const auto it = converters.find(k); if (it == converters.end()) { throw exceptions::Grib2MarsDictException( "Key `"s + k + "` does not support conversion to double for metkit::mars::MarsRequest", Here()); } return it->second(value, key); } inline std::string convertFromDoubleOrThrow(std::string_view key, double value) { const std::string k{key}; const auto& converters = fromDoubleConverters(); const auto it = converters.find(k); if (it == converters.end()) { throw exceptions::Grib2MarsDictException( "Key `"s + k + "` does not support assignment from double for metkit::mars::MarsRequest", Here()); } return it->second(value); } } // namespace detail // ----------------------------------------------------------------------------- // DictToJsonTraits // ----------------------------------------------------------------------------- template <> struct DictToJsonTraits { static std::string to_json(const metkit::mars::MarsRequest& mars) noexcept(true) { try { std::ostringstream os; eckit::JSON json(os); mars.json(json); return os.str(); } catch (...) { return "[to_json failed for metkit::mars::MarsRequest]"; } } }; // ----------------------------------------------------------------------------- // DictTraits // ----------------------------------------------------------------------------- template <> struct DictTraits { static constexpr bool support_checks = false; // static std::unique_ptr // make_from_sample_or_throw(std::string_view name) { // return std::make_unique(std::string{name}); // } static std::unique_ptr clone_or_throw(const metkit::mars::MarsRequest& mars) { return std::make_unique(mars); } }; // ----------------------------------------------------------------------------- // DictHas // ----------------------------------------------------------------------------- template <> struct DictHas { static bool has(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { return mars.has(std::string{key}); } }; // ----------------------------------------------------------------------------- // DictMissing // ----------------------------------------------------------------------------- template <> struct DictMissing { static bool isMissing(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const std::string k{key}; if (!mars.has(k)) { return true; } const auto& values = mars.values(k, true); return values.empty(); } static void setMissing(metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { mars.unsetValues(std::string{key}); } }; // ----------------------------------------------------------------------------- // std::string access: all keys, raw scalar string // ----------------------------------------------------------------------------- template <> struct DictGetOrThrow { static std::string get_or_throw(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { return detail::scalarStringOrThrow(mars, key); } }; template <> struct DictGetOpt { static std::optional get_opt(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { return detail::scalarStringOpt(mars, key); } }; template <> struct DictSetOrThrow { static void set_or_throw(metkit::mars::MarsRequest& mars, std::string_view key, const std::string& value) noexcept(false) { mars.setValue(std::string{key}, value); } }; template <> struct DictSetOrIgnore { static void set_or_ignore(metkit::mars::MarsRequest& mars, std::string_view key, const std::string& value) noexcept(false) { try { mars.setValue(std::string{key}, value); } catch (...) { } } }; // ----------------------------------------------------------------------------- // long access: explicit supported keys only // ----------------------------------------------------------------------------- template <> struct DictGetOrThrow { static long get_or_throw(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const auto& raw = detail::scalarStringOrThrow(mars, key); return detail::convertToLongOrThrow(key, raw); } }; template <> struct DictGetOpt { static std::optional get_opt(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const auto raw = detail::scalarStringOpt(mars, key); if (!raw.has_value()) { return std::nullopt; } return detail::convertToLongOrThrow(key, *raw); } }; template <> struct DictSetOrThrow { static void set_or_throw(metkit::mars::MarsRequest& mars, std::string_view key, const long& value) noexcept(false) { mars.setValue(std::string{key}, detail::convertFromLongOrThrow(key, value)); } }; template <> struct DictSetOrIgnore { static void set_or_ignore(metkit::mars::MarsRequest& mars, std::string_view key, const long& value) noexcept(false) { try { mars.setValue(std::string{key}, detail::convertFromLongOrThrow(key, value)); } catch (...) { } } }; // ----------------------------------------------------------------------------- // double access: wavelength only // ----------------------------------------------------------------------------- template <> struct DictGetOrThrow { static double get_or_throw(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const auto& raw = detail::scalarStringOrThrow(mars, key); return detail::convertToDoubleOrThrow(key, raw); } }; template <> struct DictGetOpt { static std::optional get_opt(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const auto raw = detail::scalarStringOpt(mars, key); if (!raw.has_value()) { return std::nullopt; } return detail::convertToDoubleOrThrow(key, *raw); } }; template <> struct DictSetOrThrow { static void set_or_throw(metkit::mars::MarsRequest& mars, std::string_view key, const double& value) noexcept(false) { mars.setValue(std::string{key}, detail::convertFromDoubleOrThrow(key, value)); } }; template <> struct DictSetOrIgnore { static void set_or_ignore(metkit::mars::MarsRequest& mars, std::string_view key, const double& value) noexcept(false) { try { mars.setValue(std::string{key}, detail::convertFromDoubleOrThrow(key, value)); } catch (...) { } } }; } // namespace metkit::grib2mars::utils::dict_traitsmetkit-1.19.2/src/metkit/grib2mars/mappings/0000775000175000017500000000000015232567717021076 5ustar alastairalastairmetkit-1.19.2/src/metkit/grib2mars/mappings/MarsTopology.h0000664000175000017500000002154315232567717023713 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/codes/api/KeyIterator.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { class MarsTopology { public: using container_type = std::vector; using const_iterator = container_type::const_iterator; MarsTopology() = delete; explicit MarsTopology(const metkit::codes::CodesHandle& grib) : MarsTopology(make_MarsKeywords(grib)) {} MarsTopology(const MarsTopology&) = default; MarsTopology& operator=(const MarsTopology&) = default; MarsTopology(MarsTopology&&) = default; MarsTopology& operator=(MarsTopology&&) = default; ~MarsTopology() = default; const_iterator begin() const { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { return keywords_.begin(); } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to access begin iterator of grib2mars MarsTopology", Here())); } } const_iterator end() const { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { return keywords_.end(); } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to access end iterator of grib2mars MarsTopology", Here())); } } bool contains(const std::string& keyword) const { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { const std::unordered_set::const_iterator it = index_.find(keyword); return it != index_.end(); } catch (...) { std::throw_with_nested(Grib2MarsGenericException( "Failed to check MARS keyword `" + keyword + "` in grib2mars MarsTopology", Here())); } } std::size_t size() const { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { return keywords_.size(); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to get size of grib2mars MarsTopology", Here())); } } bool empty() const { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { return keywords_.empty(); } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to check whether grib2mars MarsTopology is empty", Here())); } } private: explicit MarsTopology(std::vector keywords) : keywords_(std::move(keywords)), index_(make_index(keywords_)) {} static bool contains_keyword(const std::vector& keywords, const std::string& keyword) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { const std::vector::const_iterator it = std::find(keywords.begin(), keywords.end(), keyword); return it != keywords.end(); } catch (...) { std::throw_with_nested(Grib2MarsGenericException( "Failed to check MARS keyword `" + keyword + "` while building grib2mars MarsTopology", Here())); } } static void append_unique(std::vector& keywords, const std::string& keyword) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { const bool alreadyPresent = contains_keyword(keywords, keyword); if (!alreadyPresent) { keywords.push_back(keyword); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException( "Failed to append MARS keyword `" + keyword + "` while building grib2mars MarsTopology", Here())); } } static void remove_keyword(std::vector& keywords, const std::string& keyword) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { keywords.erase(std::remove(keywords.begin(), keywords.end(), keyword), keywords.end()); } catch (...) { std::throw_with_nested(Grib2MarsGenericException( "Failed to remove MARS keyword `" + keyword + "` while building grib2mars MarsTopology", Here())); } } static void append_ecCodes_MarsNamespace_keywords(const metkit::codes::CodesHandle& grib, std::vector& keywords) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { for (const metkit::codes::KeyIterator& key : grib.keys(metkit::codes::namespaces::mars)) { const std::string keyword = key.name(); append_unique(keywords, keyword); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException( "Failed to extract ecCodes MARS namespace while building grib2mars MarsTopology", Here())); } } static void append_packing_keyword(const metkit::codes::CodesHandle& grib, std::vector& keywords) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { const bool hasPackingType = grib.has("packingType"); if (hasPackingType) { append_unique(keywords, "packing"); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException( "Failed to add MARS keyword `packing` while building grib2mars MarsTopology", Here())); } } static void append_geometry_keyword(const metkit::codes::CodesHandle& grib, std::vector& keywords) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { const bool hasGridType = grib.has("gridType"); if (!hasGridType) { return; } const std::string gridType = grib.getString("gridType"); if (gridType == "sh") { remove_keyword(keywords, "grid"); append_unique(keywords, "truncation"); return; } remove_keyword(keywords, "truncation"); append_unique(keywords, "grid"); } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to select MARS geometry keyword `grid`/`truncation` " "while building grib2mars MarsTopology", Here())); } } static void append_required_keywords(std::vector& keywords) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { append_unique(keywords, "class"); append_unique(keywords, "stream"); append_unique(keywords, "type"); append_unique(keywords, "origin"); } catch (...) { std::throw_with_nested(Grib2MarsGenericException( "Failed to append required MARS keywords while building grib2mars MarsTopology", Here())); } } static std::vector make_MarsKeywords(const metkit::codes::CodesHandle& grib) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { std::vector keywords; append_ecCodes_MarsNamespace_keywords(grib, keywords); // MARS keywords required by mars2grib but not always injected by ecCodes. append_packing_keyword(grib, keywords); append_geometry_keyword(grib, keywords); append_required_keywords(keywords); return keywords; } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to build MARS keyword list for grib2mars MarsTopology", Here())); } } static std::unordered_set make_index(const std::vector& keywords) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { std::unordered_set index; for (const std::string& keyword : keywords) { index.insert(keyword); } return index; } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to build keyword index for grib2mars MarsTopology", Here())); } } private: const std::vector keywords_; const std::unordered_set index_; }; } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/Grib2MarsReturnValue.h0000664000175000017500000000174415232567717025242 0ustar alastairalastair/* * (C) Copyright 2026- 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 Grib2MarsReturnValue.h /// @brief Return type used by grib2mars conversion APIs. #pragma once #include "eckit/config/LocalConfiguration.h" namespace metkit::grib2mars { /// @brief Structured result returned by grib2mars conversions. /// /// @tparam MarsDict /// Dictionary type used for the converted request. template struct Grib2MarsResult { /// @brief Converted MARS request. MarsDict mars; /// @brief Auxiliary metadata produced during conversion. eckit::LocalConfiguration misc; }; } // namespace metkit::grib2mars metkit-1.19.2/src/metkit/grib2mars/mappings/mappings.h0000664000175000017500000000315715232567717023073 0ustar alastairalastair#pragma once #include #include #include #include #include "eckit/config/LocalConfiguration.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/mappings/Grib2MarsReturnValue.h" #include "metkit/grib2mars/mappings/MarsTopology.h" #include "metkit/grib2mars/mappings/rules/extract.h" #include "metkit/grib2mars/mappings/rules/misc.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules { template Grib2MarsResult convertAll(const metkit::codes::CodesHandle& grib) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { std::unique_ptr mars = std::make_unique(); std::unique_ptr misc = std::make_unique(); // // Topology is the subset of MARS keywords that are relevant to describe the input grib message. const metkit::grib2mars::rules::impl::MarsTopology topology(grib); // // This is used to inject the values for (const std::string& keyword : topology) { impl::extract(keyword, grib, *mars, *misc); } // Extract the remaining miscelanious values which are not attached to a MARS mapping impl::extractMisc(grib, *mars, *misc); return Grib2MarsResult{std::move(*mars), std::move(*misc)}; } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to convertAll grib message", Here())); } } } // namespace metkit::grib2mars::rulesmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/0000775000175000017500000000000015232567717022230 5ustar alastairalastairmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/model.h0000664000175000017500000000212015232567717023474 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractModel(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/hdate.h0000664000175000017500000000210015232567717023457 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractHdate(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/stattype.h0000664000175000017500000000215015232567717024254 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractStatType(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/expver.h0000664000175000017500000000204115232567717023707 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractExpver(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException("GRIB message is missing required key `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/origin.h0000664000175000017500000000252315232567717023672 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractOrigin(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (grib.has("origin")) { const std::string value = grib.getString("origin"); set_or_throw(mars, keyword, value); return; } if (grib.has("centre")) { const std::string value = grib.getString("centre"); set_or_throw(mars, keyword, value); return; } throw Grib2MarsGenericException( "Missing GRIB keys `origin` and `centre` required to extract " "MARS keyword `" + keyword + "`", Here()); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/grid.h0000664000175000017500000000547415232567717023340 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractGrid(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has("gridType")) { throw Grib2MarsGenericException( "Missing GRIB key `gridType` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string gridType = grib.getString("gridType"); if (gridType == "sh") { throw Grib2MarsGenericException( "Cannot extract MARS keyword `grid` from spherical harmonics " "GRIB message; `truncation` is required instead", Here()); } if (gridType == "regular_ll") { if (!grib.has("iDirectionIncrementInDegrees")) { throw Grib2MarsGenericException( "Missing GRIB key `iDirectionIncrementInDegrees` required " "to extract regular_ll MARS grid", Here()); } if (!grib.has("jDirectionIncrementInDegrees")) { throw Grib2MarsGenericException( "Missing GRIB key `jDirectionIncrementInDegrees` required " "to extract regular_ll MARS grid", Here()); } const double dx = grib.getDouble("iDirectionIncrementInDegrees"); const double dy = grib.getDouble("jDirectionIncrementInDegrees"); const std::string grid = std::to_string(dx) + "/" + std::to_string(dy); set_or_throw(mars, keyword, grid); return; } if (!grib.has("gridName")) { throw Grib2MarsGenericException("Missing GRIB key `gridName` required to extract MARS keyword `" + keyword + "` for gridType `" + gridType + "`", Here()); } const std::string gridName = grib.getString("gridName"); set_or_throw(mars, keyword, gridName); if (grib.has("shapeOfTheEarth")) { const long shapeOfTheEarth = grib.getLong("shapeOfTheEarth"); misc.set("shapeOfTheEarth", shapeOfTheEarth); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/system.h0000664000175000017500000000210115232567717023717 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractSystem(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/anoffset.h0000664000175000017500000000315115232567717024206 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractAnoffset(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } if (!grib.has("lengthOf4DvarWindow")) { throw Grib2MarsGenericException( "Missing GRIB key `lengthOf4DvarWindow` required while " "extracting MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); const long lengthOf4DvarWindow = grib.getLong("lengthOf4DvarWindow"); // Historical mars2grib misc name. // Source GRIB key: lengthOf4DvarWindow. // Unit assumption: hours. misc.set("lengthOfTimeWindow", lengthOf4DvarWindow); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/activity.h0000664000175000017500000000215015232567717024233 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractActivity(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/misc.h0000664000175000017500000000172515232567717023341 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractMisc(const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)mars; if (grib.has("generatingProcessIdentifier")) { const auto generatingProcessIdentifier = grib.getLong("generatingProcessIdentifier"); misc.set("generatingProcessIdentifier", generatingProcessIdentifier); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MISC keywords", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/channel.h0000664000175000017500000000212615232567717024012 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractChannel(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/ident.h0000664000175000017500000000210015232567717023475 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractIdent(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/timespan.h0000664000175000017500000000463215232567717024226 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractTimespan(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has("stepType")) { throw Grib2MarsGenericException( "Missing GRIB key `stepType` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string stepType = grib.getString("stepType"); if (stepType == "instant") { set_or_throw(mars, keyword, "none"); return; } if (!grib.has("startStep")) { throw Grib2MarsGenericException("Missing GRIB key `startStep` required to extract MARS keyword `" + keyword + "` for statistical stepType `" + stepType + "`", Here()); } if (!grib.has("endStep")) { throw Grib2MarsGenericException("Missing GRIB key `endStep` required to extract MARS keyword `" + keyword + "` for statistical stepType `" + stepType + "`", Here()); } const long startStep = grib.getLong("startStep"); const long endStep = grib.getLong("endStep"); const long timespan = endStep - startStep; if (timespan <= 0) { throw Grib2MarsGenericException( "Invalid statistical window while extracting MARS keyword `" + keyword + "`: stepType=`" + stepType + "`, startStep=" + std::to_string(startStep) + ", endStep=" + std::to_string(endStep) + ", timespan=" + std::to_string(timespan), Here()); } set_or_throw(mars, keyword, timespan); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/wavelength.h0000664000175000017500000000216015232567717024544 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractWavelength(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to extract scalar MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/chem.h0000664000175000017500000000207715232567717023323 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractChem(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/domain.h0000664000175000017500000000212115232567717023644 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractDomain(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/packing.h0000664000175000017500000000565015232567717024023 0ustar alastairalastair#pragma once #include #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { namespace detail { inline std::string convertPackingType(const std::string& packingType) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { static const std::unordered_map packingMap = { {"grid_simple", "simple"}, {"grid_complex", "complex"}, {"spectral_complex", "complex"}, {"grid_ccsds", "ccsds"}, {"grid_ieee", "ccsds"}, {"grid_second_order", "ccsds"}, }; const std::unordered_map::const_iterator it = packingMap.find(packingType); if (it == packingMap.end()) { throw Grib2MarsGenericException("Unhandled packingType `" + packingType + "`", Here()); } const std::string value = it->second; return value; } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to convert GRIB packingType `" + packingType + "`", Here())); } } } // namespace detail template void extractPacking(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has("packingType")) { throw Grib2MarsGenericException( "Missing GRIB key `packingType` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string packingType = grib.getString("packingType"); const std::string packing = detail::convertPackingType(packingType); set_or_throw(mars, keyword, packing); if (grib.has("setPackingType")) { const std::string setPackingType = grib.getString("setPackingType"); const std::string overridePacking = detail::convertPackingType(setPackingType); set_or_throw(mars, keyword, overridePacking); } if (grib.has("bitmapPresent")) { const long bitmapPresent = grib.getLong("bitmapPresent"); misc.set("bitmapPresent", bitmapPresent); } const double missingValue = 9999.0; misc.set("missingValue", missingValue); long bitsPerValue = 24; if (grib.has("bitsPerValue")) { bitsPerValue = grib.getLong("bitsPerValue"); } misc.set("bitsPerValue", bitsPerValue); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/experiment.h0000664000175000017500000000215415232567717024563 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractExperiment(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/instrument.h0000664000175000017500000000341315232567717024612 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractInstrument(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); if (grib.has("satelliteSeries")) { const long satelliteSeries = grib.getLong("satelliteSeries"); misc.set("satelliteSeries", satelliteSeries); } if (grib.has("scaleFactorOfCentralWaveNumber")) { const long scaleFactorOfCentralWaveNumber = grib.getLong("scaleFactorOfCentralWaveNumber"); misc.set("scaleFactorOfCentralWaveNumber", scaleFactorOfCentralWaveNumber); } if (grib.has("scaledValueOfCentralWaveNumber")) { const long scaledValueOfCentralWaveNumber = grib.getLong("scaledValueOfCentralWaveNumber"); misc.set("scaledValueOfCentralWaveNumber", scaledValueOfCentralWaveNumber); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/levtype.h0000664000175000017500000000234615232567717024076 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractLevtype(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); if (grib.has("pv")) { const auto pv = grib.getDoubleArray("pv"); misc.set("pv", pv); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/method.h0000664000175000017500000000210115232567717023653 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractMethod(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/date.h0000664000175000017500000000207715232567717023324 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractDate(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/stream.h0000664000175000017500000000204115232567717023671 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractStream(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException("GRIB message is missing required key `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/truncation.h0000664000175000017500000000346215232567717024574 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractTruncation(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has("gridType")) { throw Grib2MarsGenericException( "Missing GRIB key `gridType` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string gridType = grib.getString("gridType"); if (gridType != "sh") { throw Grib2MarsGenericException( "Cannot extract MARS keyword `truncation` from non-spherical " "harmonics gridType `" + gridType + "`", Here()); } if (!grib.has("J")) { throw Grib2MarsGenericException("Missing GRIB key `J` required to extract MARS keyword `" + keyword + "`", Here()); } const long truncation = grib.getLong("J"); set_or_throw(mars, keyword, truncation); if (grib.has("laplacianOperator")) { const long laplacianOperator = grib.getLong("laplacianOperator"); misc.set("laplacianOperator", laplacianOperator); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/dataset.h0000664000175000017500000000214615232567717024031 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractDataset(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/param.h0000664000175000017500000000204415232567717023501 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractParam(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict&) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has("paramId")) { throw Grib2MarsGenericException( "GRIB message is missing required key `paramId` for MARS keyword `" + keyword + "`", Here()); } const long value = grib.getLong("paramId"); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/number.h0000664000175000017500000000547715232567717023706 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractNumber(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has("type")) { throw Grib2MarsGenericException( "Missing GRIB key `type` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string type = grib.getString("type"); if (type == "es" || type == "em") { if (!grib.has("numberOfForecastsInEnsemble")) { throw Grib2MarsGenericException( "Missing GRIB key `numberOfForecastsInEnsemble` required " "for derived ensemble forecast", Here()); } const long numberOfForecastsInEnsemble = grib.getLong("numberOfForecastsInEnsemble"); misc.set("numberOfForecastsInEnsemble", numberOfForecastsInEnsemble); return; } if (!grib.has("number") || !grib.has("numberOfForecastsInEnsemble")) { return; } const long number = grib.getLong("number"); const long numberOfForecastsInEnsemble = grib.getLong("numberOfForecastsInEnsemble"); if (number != 0 && numberOfForecastsInEnsemble == 0) { throw Grib2MarsGenericException("The value for key `numberOfForecastsInEnsemble` must not be 0", Here()); } if (numberOfForecastsInEnsemble == 0) { return; } set_or_throw(mars, keyword, number); misc.set("numberOfForecastsInEnsemble", numberOfForecastsInEnsemble); if (grib.has("class")) { const std::string klass = grib.getString("class"); if (klass == "ai") { // Handled in the encoder, matching the original tool logic. return; } } if (grib.has("typeOfEnsembleForecast")) { const long typeOfEnsembleForecast = grib.getLong("typeOfEnsembleForecast"); misc.set("typeOfEnsembleForecast", typeOfEnsembleForecast); return; } if (grib.has("eps")) { const long eps = grib.getLong("eps"); misc.set("typeOfEnsembleForecast", eps); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/frequency.h0000664000175000017500000000476515232567717024416 0ustar alastairalastair#pragma once #include #include #include #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractFrequency(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long frequency = grib.getLong(keyword); set_or_throw(mars, keyword, frequency); if (grib.has("scaledValuesOfWaveFrequencies")) { std::vector waveFrequencies = grib.getDoubleArray("scaledValuesOfWaveFrequencies"); double frequencyScalingFactor = 0.0; if (grib.has("scaleFactorOfWaveFrequencies")) { const double scaleFactorOfWaveFrequencies = grib.getDouble("scaleFactorOfWaveFrequencies"); frequencyScalingFactor = std::pow(10.0, scaleFactorOfWaveFrequencies); } else { if (!grib.has("frequencyScalingFactor")) { throw Grib2MarsGenericException( "Missing both `scaleFactorOfWaveFrequencies` and " "`frequencyScalingFactor` while extracting MARS keyword `" + keyword + "`", Here()); } frequencyScalingFactor = grib.getDouble("frequencyScalingFactor"); } std::for_each(waveFrequencies.begin(), waveFrequencies.end(), [frequencyScalingFactor](double& value) { value /= frequencyScalingFactor; }); const double scaleFactorOfWaveFrequencies = std::log10(frequencyScalingFactor); misc.set("scaleFactorOfWaveFrequencies", scaleFactorOfWaveFrequencies); misc.set("waveFrequencies", waveFrequencies); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/levelist.h0000664000175000017500000000406015232567717024230 0ustar alastairalastair#pragma once #include #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractLevelist(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has("levtype")) { throw Grib2MarsGenericException( "Missing GRIB key `levtype` required to extract MARS keyword `" + keyword + "`", Here()); } if (!grib.has("level")) { throw Grib2MarsGenericException( "Missing GRIB key `level` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string levtype = grib.getString("levtype"); std::optional levelist; if (levtype == "pl") { if (!grib.has("pressureUnits")) { throw Grib2MarsGenericException( "Missing GRIB key `pressureUnits` required to extract " "pressure-level MARS keyword `" + keyword + "`", Here()); } const std::string pressureUnits = grib.getString("pressureUnits"); if (pressureUnits == "hPa") { const long level = grib.getLong("level"); levelist = level * 100; } } if (!levelist) { const long level = grib.getLong("level"); levelist = level; } set_or_throw(mars, keyword, *levelist); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/time.h0000664000175000017500000000314515232567717023342 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractTime(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has("hour")) { throw Grib2MarsGenericException( "Missing GRIB key `hour` required to extract MARS keyword `" + keyword + "`", Here()); } if (!grib.has("minute")) { throw Grib2MarsGenericException( "Missing GRIB key `minute` required to extract MARS keyword `" + keyword + "`", Here()); } if (!grib.has("second")) { throw Grib2MarsGenericException( "Missing GRIB key `second` required to extract MARS keyword `" + keyword + "`", Here()); } const long hour = grib.getLong("hour"); const long minute = grib.getLong("minute"); const long second = grib.getLong("second"); const long value = hour * 10000 + minute * 100 + second; set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/resolution.h0000664000175000017500000000215415232567717024606 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractResolution(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/type.h0000664000175000017500000000203715232567717023364 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractType(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { throw Grib2MarsGenericException("GRIB message is missing required key `" + keyword + "`", Here()); } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/class.h0000664000175000017500000000170515232567717023511 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractClass(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { (void)misc; if (!grib.has(keyword)) { return; } const std::string value = grib.getString(keyword); set_or_throw(mars, keyword, value); } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/direction.h0000664000175000017500000000514515232567717024366 0ustar alastairalastair#pragma once #include #include #include #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractDirection(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has(keyword)) { throw Grib2MarsGenericException( "Missing GRIB key `" + keyword + "` required to extract MARS keyword `" + keyword + "`", Here()); } const long direction = grib.getLong(keyword); set_or_throw(mars, keyword, direction); if (grib.has("scaledValuesOfWaveDirections")) { constexpr double rad2deg = 57.29577951308232087679815481410517033240547246656432154916; std::vector waveDirections = grib.getDoubleArray("scaledValuesOfWaveDirections"); double directionScalingFactor = 0.0; if (grib.has("scaleFactorOfWaveDirections")) { const double scaleFactorOfWaveDirections = grib.getDouble("scaleFactorOfWaveDirections"); directionScalingFactor = std::pow(10.0, scaleFactorOfWaveDirections); } else { if (!grib.has("directionScalingFactor")) { throw Grib2MarsGenericException( "Missing both `scaleFactorOfWaveDirections` and " "`directionScalingFactor` while extracting MARS keyword `" + keyword + "`", Here()); } directionScalingFactor = grib.getDouble("directionScalingFactor"); } std::for_each( waveDirections.begin(), waveDirections.end(), [directionScalingFactor, rad2deg](double& value) { value /= directionScalingFactor * rad2deg; }); const double scaleFactorOfWaveDirections = std::log10(directionScalingFactor); misc.set("scaleFactorOfWaveDirections", scaleFactorOfWaveDirections); misc.set("waveDirections", waveDirections); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/extract.h0000664000175000017500000001366715232567717024070 0ustar alastairalastair#pragma once #include #include #include "eckit/config/LocalConfiguration.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" // One extractor header per MARS keyword #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/class.h" #include "metkit/grib2mars/mappings/rules/dataset.h" #include "metkit/grib2mars/mappings/rules/date.h" #include "metkit/grib2mars/mappings/rules/direction.h" #include "metkit/grib2mars/mappings/rules/domain.h" #include "metkit/grib2mars/mappings/rules/experiment.h" #include "metkit/grib2mars/mappings/rules/expver.h" #include "metkit/grib2mars/mappings/rules/frequency.h" #include "metkit/grib2mars/mappings/rules/grid.h" #include "metkit/grib2mars/mappings/rules/hdate.h" #include "metkit/grib2mars/mappings/rules/ident.h" #include "metkit/grib2mars/mappings/rules/instrument.h" #include "metkit/grib2mars/mappings/rules/levelist.h" #include "metkit/grib2mars/mappings/rules/levtype.h" #include "metkit/grib2mars/mappings/rules/method.h" #include "metkit/grib2mars/mappings/rules/model.h" #include "metkit/grib2mars/mappings/rules/number.h" #include "metkit/grib2mars/mappings/rules/origin.h" #include "metkit/grib2mars/mappings/rules/packing.h" #include "metkit/grib2mars/mappings/rules/param.h" #include "metkit/grib2mars/mappings/rules/resolution.h" #include "metkit/grib2mars/mappings/rules/stattype.h" #include "metkit/grib2mars/mappings/rules/step.h" #include "metkit/grib2mars/mappings/rules/stream.h" #include "metkit/grib2mars/mappings/rules/system.h" #include "metkit/grib2mars/mappings/rules/time.h" #include "metkit/grib2mars/mappings/rules/timespan.h" #include "metkit/grib2mars/mappings/rules/truncation.h" #include "metkit/grib2mars/mappings/rules/type.h" #include "metkit/grib2mars/mappings/rules/wavelength.h" namespace metkit::grib2mars::rules::impl { namespace detail { template using MarsExtractor = void (*)(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc); template const std::unordered_map>& extractorRegistry() { static const std::unordered_map> registry = { {"class", extractClass}, {"stream", extractStream}, {"type", extractType}, {"expver", extractExpver}, {"param", extractParam}, {"levtype", extractLevtype}, {"levelist", extractLevelist}, {"frequency", extractFrequency}, {"direction", extractDirection}, {"ident", extractIdent}, {"channel", extractChannel}, {"instrument", extractInstrument}, {"anoffset", extractAnoffset}, {"number", extractNumber}, {"grid", extractGrid}, {"truncation", extractTruncation}, {"packing", extractPacking}, {"date", extractDate}, {"hdate", extractHdate}, {"time", extractTime}, {"step", extractStep}, {"system", extractSystem}, {"method", extractMethod}, {"origin", extractOrigin}, {"timespan", extractTimespan}, {"stattype", extractStatType}, {"domain", extractDomain}, {"activity", extractActivity}, {"dataset", extractDataset}, {"experiment", extractExperiment}, {"resolution", extractResolution}, {"model", extractModel}, {"wavelength", extractWavelength}, }; return registry; } template MarsExtractor resolveExtractor(const std::string& keyword) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { const std::unordered_map>& registry = extractorRegistry(); const typename std::unordered_map>::const_iterator it = registry.find(keyword); if (it == registry.end()) { throw Grib2MarsGenericException("No grib2mars extractor registered for MARS keyword `" + keyword + "`", Here()); } const MarsExtractor extractor = it->second; return extractor; } catch (...) { std::throw_with_nested(Grib2MarsGenericException( "Failed to resolve grib2mars extractor for MARS keyword `" + keyword + "`", Here())); } } } // namespace detail template void extract(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { const detail::MarsExtractor extractor = detail::resolveExtractor(keyword); extractor(keyword, grib, mars, misc); } catch (...) { std::throw_with_nested( Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "` from GRIB message", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/mappings/rules/step.h0000664000175000017500000000601515232567717023356 0ustar alastairalastair#pragma once #include #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::rules::impl { template void extractStep(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc) { using metkit::grib2mars::utils::dict_traits::set_or_throw; using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException; try { if (!grib.has("stepType")) { throw Grib2MarsGenericException( "Missing GRIB key `stepType` required to extract MARS keyword `" + keyword + "`", Here()); } if (!grib.has("endStep")) { throw Grib2MarsGenericException( "Missing GRIB key `endStep` required to extract MARS keyword `" + keyword + "`", Here()); } const std::string stepType = grib.getString("stepType"); const long endStep = grib.getLong("endStep"); set_or_throw(mars, keyword, endStep); #if 0 if (stepType != "instant") { if (!grib.has("startStep")) { throw Grib2MarsGenericException( "Missing GRIB key `startStep` required to extract " "`timespan` for statistical GRIB message with stepType `" + stepType + "`", Here()); } const long startStep = grib.getLong("startStep"); const long timespan = endStep - startStep; if (timespan == 0) { throw Grib2MarsGenericException( "Invalid zero statistical window while extracting `timespan`: " "stepType=`" + stepType + "`, startStep=" + std::to_string(startStep) + ", endStep=" + std::to_string(endStep), Here()); } if (timespan < 0) { throw Grib2MarsGenericException( "Invalid negative statistical window while extracting `timespan`: " "stepType=`" + stepType + "`, startStep=" + std::to_string(startStep) + ", endStep=" + std::to_string(endStep), Here()); } set_or_throw(mars, "timespan", timespan); } #endif if (grib.has("initialStep")) { const long initialStep = grib.getLong("initialStep"); misc.set("initialStep", initialStep); } if (grib.has("timeIncrement")) { const long timeIncrement = grib.getLong("timeIncrement"); misc.set("timeIncrementInSeconds", timeIncrement); } } catch (...) { std::throw_with_nested(Grib2MarsGenericException("Failed to extract MARS keyword `" + keyword + "`", Here())); } } } // namespace metkit::grib2mars::rules::implmetkit-1.19.2/src/metkit/grib2mars/api/0000775000175000017500000000000015232567717020031 5ustar alastairalastairmetkit-1.19.2/src/metkit/grib2mars/api/Grib2Mars.cc0000664000175000017500000000502615232567717022133 0ustar alastairalastair/* * (C) Copyright 2026- 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 Grib2Mars.cc /// @brief Implementation of the public Grib2Mars conversion API. /// /// The implementation forwards each supported conversion through the rule /// layer and wraps the result in the common API error-handling policy. /// #include "Grib2Mars.h" #include "eckit/config/LocalConfiguration.h" #include "eckit/exception/Exceptions.h" #include "metkit/grib2mars/mappings/mappings.h" #include "metkit/mars/MarsRequest.h" // dictionary access traits #include "metkit/grib2mars/utils/dictionary_traits/dictaccess_eckit_configuration.h" #include "metkit/grib2mars/utils/dictionary_traits/dictaccess_mars_request.h" #include "metkit/grib2mars/utils/dictionary_traits/dictionary_access_traits.h" // Error handling utilities #include "Grib2MarsApiErrorHandling.h" namespace metkit::grib2mars { // ----------------------------------------------------------------------------- // Grib2Mars construction // ----------------------------------------------------------------------------- /// @brief Default construct a Grib2Mars converter. Grib2Mars::Grib2Mars() {} /// @brief Convert an `eckit::LocalConfiguration` request. template <> Grib2MarsResult Grib2Mars::convert( const metkit::codes::CodesHandle& grib) { using metkit::grib2mars::utils::exceptions::withGrib2MarsApiErrorHandling; return withGrib2MarsApiErrorHandling>( "Grib2Mars::convert", [&]() { return rules::convertAll(grib); }, Here()); } /// @brief Convert a `metkit::mars::MarsRequest` request. template <> Grib2MarsResult Grib2Mars::convert( const metkit::codes::CodesHandle& grib) { using metkit::grib2mars::utils::exceptions::withGrib2MarsApiErrorHandling; return withGrib2MarsApiErrorHandling>( "Grib2Mars::convert", [&]() { return rules::convertAll(grib); }, Here()); } } // namespace metkit::grib2mars metkit-1.19.2/src/metkit/grib2mars/api/Grib2MarsApiErrorHandling.h0000664000175000017500000001275315232567717025113 0ustar alastairalastair/* * (C) Copyright 2026- 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 Grib2MarsApiErrorHandling.h /// @brief Common error handling for grib2mars public API calls. #pragma once #include #include #include #include #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "metkit/grib2mars/utils/grib2marsExceptions.h" namespace metkit::grib2mars::utils::exceptions { namespace detail { /// @brief Environment variable controlling error-stack output. inline constexpr const char* GRIB2MARS_ERROR_STACK_DIR_ENV = "GRIB2MARS_ERROR_STACK_DIR"; /// @brief Sanitize a string for use in a generated filename. inline std::string sanitizeForFileName(const std::string& value) { std::string out; out.reserve(value.size()); for (const unsigned char c : value) { if (std::isalnum(c) || c == '_' || c == '-' || c == '.') { out.push_back(static_cast(c)); } else { out.push_back('_'); } } return out; } /// @brief Convert the current thread identifier into a filename-safe string. inline std::string currentThreadIdAsString() { std::ostringstream os; os << std::this_thread::get_id(); return sanitizeForFileName(os.str()); } /// @brief Resolve the directory where stack files are stored. inline eckit::PathName errorStackDirectory() { if (const char* value = std::getenv(GRIB2MARS_ERROR_STACK_DIR_ENV)) { if (value[0] != '\0') { return eckit::PathName(value).fullName(); } } return eckit::PathName(".").fullName(); } /// @brief Build a unique error-stack file path for an API entry point. inline eckit::PathName makeErrorStackFilePath(const std::string& apiName) { static std::atomic counter{0}; const eckit::PathName dir = errorStackDirectory(); try { dir.mkdir(); } catch (const std::exception& e) { throw eckit::UserError("Unable to create Grib2Mars error stack directory `" + dir.asString() + "`: " + e.what(), Here()); } std::ostringstream fileName; fileName << "grib2mars-error-stack-" << sanitizeForFileName(apiName) << "-pid" << static_cast(::getpid()) << "-tid" << currentThreadIdAsString() << "-" << counter.fetch_add(1, std::memory_order_relaxed) << ".log"; return dir / fileName.str(); } /// @brief Serialize a grib2mars exception stack to disk. inline void writeGrib2MarsExceptionStackToFile(const Grib2MarsGenericException& exception, const std::string& apiName, const eckit::PathName& errorStackFilePath, const eckit::CodeLocation& loc = eckit::CodeLocation()) { std::ofstream out(errorStackFilePath.asString()); if (!out) { throw eckit::UserError( "Grib2Mars conversion failed, but the error stack file could not " "be opened: `" + errorStackFilePath.asString() + "`", Here()); } out << "Grib2Mars API failure\n"; out << "API entry point: " << apiName << "\n"; out << "Error stack file: " << errorStackFilePath.asString() << "\n"; out << "\n"; out << "Nested exception stack:\n"; out << "-----------------------\n"; out << "+ " << std::string(lineSize, '=') << std::endl << "+ frame " << 0 << std::endl << "+ " << std::string(lineSize, '-') << std::endl; out << "+ file: " << loc.file() << "\n" << "+ function: " << loc.func() << "\n" << "+ line: " << loc.line() << "\n" << "+ link: " << loc.file() << ":" << loc.line() << "\n" << "+ message: " << "Error encountered at: " << apiName << "\n"; out << "+ " << std::string(lineSize, '+') << std::endl; printExtendedStack(exception, out, 1, 1); } } // namespace detail /// @brief Execute a callable under the grib2mars API error boundary. /// /// @tparam Result /// Callable return type. /// /// @tparam Callable /// Callable type. /// /// @param[in] apiName /// API entry-point name used in diagnostics. /// /// @param[in] callable /// Operation to execute. /// /// @param[in] loc /// Source location attached to user-facing failures. template Result withGrib2MarsApiErrorHandling(const std::string& apiName, Callable&& callable, const eckit::CodeLocation& loc = eckit::CodeLocation()) { try { return std::forward(callable)(); } catch (const Grib2MarsGenericException& e) { const eckit::PathName errorStackFilePath = detail::makeErrorStackFilePath(apiName); std::cout << "Grib2Mars API error" << std::endl; detail::writeGrib2MarsExceptionStackToFile(e, apiName, errorStackFilePath, loc); throw eckit::UserError( "Grib2Mars conversion failed. Full error stack written to `" + errorStackFilePath.asString() + "`", loc); } catch (...) { throw; } } } // namespace metkit::grib2mars::utils::exceptions metkit-1.19.2/src/metkit/grib2mars/api/Grib2Mars.h0000664000175000017500000000561715232567717022003 0ustar alastairalastair/* * (C) Copyright 2026- 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 Grib2Mars.h /// @brief Public conversion API for transforming MARS requests. /// /// This header exposes the primary grib2mars entry point. Callers provide a /// supported dictionary type and receive a structured result containing the /// converted MARS request plus auxiliary conversion metadata. /// /// The API is intentionally narrow and explicit: /// - only supported dictionary types may be converted /// - conversion is delegated to the rule engine /// - all failures are routed through the shared API error boundary /// /// @section References /// Implementation: /// - @ref Grib2Mars.cc /// /// Result type: /// - @ref Grib2MarsReturnValue.h /// /// @ingroup grib2mars_api /// #pragma once #include "eckit/config/LocalConfiguration.h" #include "metkit/mars/MarsRequest.h" // Codes wrapper types #include "metkit/codes/api/CodesAPI.h" #include "metkit/grib2mars/mappings/Grib2MarsReturnValue.h" namespace metkit::grib2mars { /// /// @brief Main API for converting MARS requests. /// /// The converter owns no runtime configuration state. It is a thin façade /// around the conversion rules and the shared API error handling layer. /// class Grib2Mars { public: /// /// @brief Construct a Grib2Mars converter. /// /// No configuration is stored in the object itself. /// Grib2Mars(); ~Grib2Mars() = default; /// @brief Convert a supported dictionary type. /// /// The primary template is deleted intentionally. Only explicit /// specializations are available through the public API. /// /// @tparam Dict_t /// Input dictionary type. /// /// @param[in] mars /// Input MARS dictionary. /// /// @return /// A converted result for the supported dictionary type. template Grib2MarsResult convert(const metkit::codes::CodesHandle& grib) = delete; }; // ----------------------------------------------------------------------------- // Supported API specializations // ----------------------------------------------------------------------------- /// @brief Convert an `eckit::LocalConfiguration` request. template <> Grib2MarsResult Grib2Mars::convert( const metkit::codes::CodesHandle& grib); /// @brief Convert a `metkit::mars::MarsRequest` request. template <> Grib2MarsResult Grib2Mars::convert( const metkit::codes::CodesHandle& grib); } // namespace metkit::grib2mars metkit-1.19.2/src/metkit/tool/0000775000175000017500000000000015232567717016345 5ustar alastairalastairmetkit-1.19.2/src/metkit/tool/MetkitTool.cc0000664000175000017500000000564515232567717020761 0ustar alastairalastair/* * (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 "eckit/exception/Exceptions.h" #include "eckit/log/Log.h" #include "eckit/option/CmdArgs.h" #include "eckit/option/SimpleOption.h" #include "eckit/system/Library.h" #include "metkit/config/LibMetkit.h" #include "metkit/tool/MetkitTool.h" #include "eckit/system/LibraryManager.h" using namespace eckit; using namespace eckit::option; namespace metkit { //---------------------------------------------------------------------------------------------------------------------- static MetkitTool* instance_ = nullptr; MetkitTool::MetkitTool(int argc, char** argv) : eckit::Tool(argc, argv, "METKIT_HOME") { ASSERT(instance_ == nullptr); instance_ = this; options_.push_back(new SimpleOption("version", "Prints the version and exits")); options_.push_back(new SimpleOption("porcelain", "Stable output that can be used as input to other tools")); } static void usage(const std::string& tool) { ASSERT(instance_); instance_->usage(tool); } void MetkitTool::run() { LOG_DEBUG_LIB(LibMetkit) << "MetkitTool::run()" << std::endl; CmdArgs args(&metkit::usage, options_, numberOfPositionalArguments(), minimumPositionalArguments()); if (args.has("version")) { auto& log = eckit::Log::info(); using eckit::system::LibraryManager; for (const auto& lib_name : LibraryManager::list()) { auto& lib = LibraryManager::lookup(lib_name); log << lib.name() << " " << lib.version() << " git-sha1:" << lib.gitsha1(8) << " home:" << lib.libraryHome() << std::endl; } return; //< stops processing } init(args); execute(args); finish(args); } void MetkitTool::usage(const std::string& tool) const { Log::info() << "Usage: " << tool << "[-h] [...options...]" << std::endl; } void MetkitTool::init(const eckit::option::CmdArgs& args) { LOG_DEBUG_LIB(LibMetkit) << "MetkitTool::init()" << std::endl; args.get("porcelain", porcelain_); } void MetkitTool::finish(const eckit::option::CmdArgs& args) { LOG_DEBUG_LIB(LibMetkit) << "MetkitTool::finish()" << std::endl; } //---------------------------------------------------------------------------------------------------------------------- MetkitToolException::MetkitToolException(const std::string& w) : Exception(w) {} MetkitToolException::MetkitToolException(const std::string& w, const eckit::CodeLocation& l) : Exception(w, l) {} //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit metkit-1.19.2/src/metkit/tool/MetkitTool.h0000664000175000017500000000375015232567717020616 0ustar alastairalastair/* * (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. */ /// @file MetkitTool.h /// @author Tiago Quintino /// @date Mar 2020 #ifndef metkit_MetkitTool_H #define metkit_MetkitTool_H #include #include "eckit/exception/Exceptions.h" #include "eckit/runtime/Tool.h" namespace eckit { namespace option { class Option; class CmdArgs; } // namespace option } // namespace eckit namespace metkit { //---------------------------------------------------------------------------------------------------------------------- class MetkitTool : public eckit::Tool { public: // methods MetkitTool(int argc, char** argv); virtual void usage(const std::string& tool) const; protected: // methods using options_t = std::vector; protected: // members options_t options_; bool porcelain_ = false; private: // methods virtual void init(const eckit::option::CmdArgs& args); virtual void execute(const eckit::option::CmdArgs& args) = 0; virtual void finish(const eckit::option::CmdArgs& args); virtual int numberOfPositionalArguments() const { return -1; } virtual int minimumPositionalArguments() const { return -1; } virtual void run(); }; //---------------------------------------------------------------------------------------------------------------------- class MetkitToolException : public eckit::Exception { public: MetkitToolException(const std::string&); MetkitToolException(const std::string&, const eckit::CodeLocation&); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit #endif metkit-1.19.2/src/metkit/config/0000775000175000017500000000000015232567717016635 5ustar alastairalastairmetkit-1.19.2/src/metkit/config/LibMetkit.h0000664000175000017500000000314215232567717020672 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @author Tiago Quintino /// @date August 2016 #ifndef mir_LibMetkit_H #define mir_LibMetkit_H #include "eckit/filesystem/PathName.h" #include "eckit/system/Library.h" //---------------------------------------------------------------------------------------------------------------------- namespace metkit { class LibMetkit : public eckit::system::Library { public: LibMetkit(); static eckit::PathName configFile(const std::string& filename); static eckit::PathName languageYamlFile(); static std::vector modifiersYamlFiles(); static eckit::PathName paramYamlFile(); static eckit::PathName paramStaticYamlFile(); static eckit::PathName paramIDYamlFile(); static eckit::PathName paramMatchingYamlFile(); static eckit::PathName shortnameContextYamlFile(); static eckit::PathName bufrSubtypesYamlFile(); static const LibMetkit& instance(); protected: const void* addr() const; virtual std::string version() const; virtual std::string gitsha1(unsigned int count) const; }; } // namespace metkit //---------------------------------------------------------------------------------------------------------------------- #endif metkit-1.19.2/src/metkit/config/LibMetkit.cc0000664000175000017500000000473615232567717021042 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @author Tiago Quintino /// @date August 2016 #include "metkit/config/LibMetkit.h" #include #include "eckit/config/Resource.h" #include "metkit/metkit_version.h" namespace metkit { //---------------------------------------------------------------------------------------------------------------------- REGISTER_LIBRARY(LibMetkit); LibMetkit::LibMetkit() : Library("metkit") {} const LibMetkit& LibMetkit::instance() { static LibMetkit libmetkit; return libmetkit; } const void* LibMetkit::addr() const { return this; } std::string LibMetkit::version() const { return metkit_version_str(); } std::string LibMetkit::gitsha1(unsigned int count) const { std::string sha1(metkit_git_sha1()); if (sha1.empty()) { return "not available"; } return sha1.substr(0, std::min(count, 40u)); } eckit::PathName LibMetkit::configFile(const std::string& filename) { return eckit::PathName{"~metkit/share/metkit"} / filename; } eckit::PathName LibMetkit::languageYamlFile() { return LibMetkit::configFile("language.yaml"); } std::vector LibMetkit::modifiersYamlFiles() { return std::vector{LibMetkit::configFile("modifiers.yaml"), LibMetkit::configFile("reportype.yaml")}; } eckit::PathName LibMetkit::paramYamlFile() { return LibMetkit::configFile("params.yaml"); } eckit::PathName LibMetkit::paramStaticYamlFile() { return LibMetkit::configFile("params-static.yaml"); } eckit::PathName LibMetkit::shortnameContextYamlFile() { return LibMetkit::configFile("shortname-context.yaml"); } eckit::PathName LibMetkit::paramIDYamlFile() { return LibMetkit::configFile("paramids.yaml"); } eckit::PathName LibMetkit::paramMatchingYamlFile() { return LibMetkit::configFile("param-matching.yaml"); } eckit::PathName LibMetkit::bufrSubtypesYamlFile() { return LibMetkit::configFile("bufr-subtypes.yaml"); } } // namespace metkit //---------------------------------------------------------------------------------------------------------------------- metkit-1.19.2/src/metkit/mars/0000775000175000017500000000000015232567717016332 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars/TypeAny.cc0000664000175000017500000000207215232567717020233 0ustar alastairalastair/* * (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 "metkit/mars/TypeAny.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- TypeAny::TypeAny(const std::string& name, const eckit::Value& settings) : Type(name, settings) {} void TypeAny::print(std::ostream& out) const { out << "TypeAny[name=" << name_ << "]"; } bool TypeAny::expand(std::string&, const MarsRequest&) const { return true; } static TypeBuilder type("any"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/Matcher.h0000664000175000017500000000615515232567717020075 0ustar alastairalastair/* * (C) Copyright 2025- 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. */ /// @author Chris Bradley /// @date Sep 2025 #pragma once #include #include #include #include #include #include "eckit/utils/Regex.h" #include "metkit/mars/MarsRequest.h" namespace metkit::mars { class RequestLike; /// Parse a match expression into a map of key:regex pairs /// @param expr is a series of comma separated key:regex pairs e.g. /// "expver=(0001|o[0-9a-z]{3}),dataset=^climate-dt$" std::map parseMatchString(const std::string& expr); // ------------------------------------------------------------------------------------------------------------- /// For matching requests against a set of key:regex conditions. For constructing select/exclude filters. class Matcher { public: /// Policy for handling requests with multiple values for a given key. enum class Policy { All, ///< Require all values to match Any ///< Require at least one value to match }; /// Policy for handling keys in the matcher that are absent in the request. enum class MatchMissingPolicy { MatchOnMissing, DontMatchOnMissing }; static constexpr auto MatchOnMissing = MatchMissingPolicy::MatchOnMissing; static constexpr auto DontMatchOnMissing = MatchMissingPolicy::DontMatchOnMissing; public: /// Construct a matcher from a map of key:regex pairs Matcher(std::map regexMap, Policy policy); Matcher(const std::string& expr, Policy policy); bool match(const RequestLike& request, MatchMissingPolicy matchOnMissing = MatchOnMissing) const; bool match(const MarsRequest& request, MatchMissingPolicy matchOnMissing = MatchOnMissing) const; void print(std::ostream& s) const; friend std::ostream& operator<<(std::ostream& s, const Matcher& m) { m.print(s); return s; } private: std::map regexMap_; Policy policy_; }; // ---------------------------------------------------------------------------------------------------------------------- /// Interface to access keyword / value(s) pairs across similar types. /// Designed to allow uniform access to keywords / values for MarsRequest / fdb5::Key class RequestLike { public: using values_t = std::variant, ///< single value std::reference_wrapper> ///< multiple values >; virtual ~RequestLike() = default; /// Get a reference_wrapper to the value(s) for the given keyword, or nullopt if not found virtual std::optional get(const std::string& keyword) const = 0; }; } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/StepRange.cc0000664000175000017500000001050615232567717020533 0ustar alastairalastair/* * (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 "metkit/mars/StepRange.h" #include "metkit/mars/TypeRange.h" #include "metkit/mars/TypeTime.h" #include "eckit/exception/Exceptions.h" #include "eckit/persist/DumpLoad.h" #include "eckit/utils/Tokenizer.h" using namespace eckit; //---------------------------------------------------------------------------------------------------------------------- namespace { // enum TimeUnit { // Second = 0, // Minute = 1, // Hour = 2, // Day = 3 // }; // TimeUnit maxUnit(const eckit::Time& t) { // if (t.seconds() == 0) { // if (t.minutes() == 0) { // if (t.hours() == 0) { // return TimeUnit::Day; // } // return TimeUnit::Hour; // } // return TimeUnit::Minute; // } // return TimeUnit::Second; // } std::string canonical(const eckit::Time& time, bool useDays, bool alwaysUseHours) { long h = time.hours(); long m = time.minutes(); long s = time.seconds(); long d = 0; if (useDays && h > 23) { d = h / 24; h = h % 24; } std::string out = ""; if (d != 0) { out += std::to_string(d) + "d"; } if (h != 0 || (m == 0 && s == 0 && d == 0)) { out += std::to_string(h); if (alwaysUseHours || m != 0 || s != 0 || d != 0) { out += "h"; } } if (m != 0) { out += std::to_string(m) + "m"; } if (s != 0) { out += std::to_string(s) + "s"; } return out; } // std::string canonical(const eckit::Time& time, TimeUnit unit) { // switch (unit) { // case TimeUnit::Second: // return std::to_string(time.seconds()+60*time.minutes()+3600*time.hours()) + "s"; // case TimeUnit::Minute: // return std::to_string(time.minutes()+60*time.hours()) + "m"; // case TimeUnit::Day: // case TimeUnit::Hour: // default: // return std::to_string(time.hours()); // } // } } // namespace namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- StepRange::operator std::string() const { return toString(); } std::string StepRange::toString(bool useDays, bool alwaysUseHours) const { std::ostringstream os; print(os, useDays, alwaysUseHours); return os.str(); } void StepRange::print(std::ostream& s, bool useDays, bool alwaysUseHours) const { if (from_ == to_) { s << canonical(eckit::Time(std::lround(from_ * 3600.), true), useDays, alwaysUseHours); } else { eckit::Time f{std::lround(from_ * 3600.), true}; eckit::Time t{std::lround(to_ * 3600.), true}; s << canonical(f, useDays, alwaysUseHours) << '-' << canonical(t, useDays, alwaysUseHours); } } StepRange::StepRange(const std::string& s) : from_(0.), to_(0.) { Tokenizer parse("-"); std::vector result; // negative steps are not supported if (s[0] != '-') parse(s, result); switch (result.size()) { case 1: to_ = from_ = eckit::Time(result[0], true) / 3600.; break; case 2: from_ = eckit::Time(result[0], true) / 3600.; to_ = eckit::Time(result[1], true) / 3600.; if (from_ > to_) { std::ostringstream oss; oss << "initial value " << from_ << " cannot be greater that final value " << to_; throw eckit::BadValue(oss.str(), Here()); } break; default: std::ostringstream msg; msg << "Bad StepRange [" << s << "]"; throw eckit::BadValue(msg.str(), Here()); } } void StepRange::dump(DumpLoad& a) const { a.dump(from_); a.dump(to_); } void StepRange::load(DumpLoad& a) { a.load(from_); a.load(to_); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsRequestHandle.h0000664000175000017500000000420115232567717022067 0ustar alastairalastair/* * (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. */ // File MarsRequestHandle.h // Baudouin Raoult - (c) ECMWF Feb 12 #ifndef MarsRequestHandle_H #define MarsRequestHandle_H #include #include #include "eckit/exception/Exceptions.h" #include "eckit/io/DataHandle.h" #include "eckit/types/Types.h" #include "metkit/mars/BaseProtocol.h" #include "metkit/mars/MarsRequest.h" namespace metkit::mars { class MarsRequestHandle : public eckit::DataHandle { public: class RetryTransfer : public eckit::Exception { bool retryOnClient() const override { return true; } public: RetryTransfer(const std::string& what) : eckit::Exception(what) {} }; public: MarsRequestHandle(eckit::Stream&); MarsRequestHandle(const metkit::mars::MarsRequest& request, const eckit::Configuration& database); MarsRequestHandle(const metkit::mars::MarsRequest& request, metkit::mars::BaseProtocol* protocol); ~MarsRequestHandle(); // -- Overridden methods (from Streamable) std::string className() const override { return "MarsRequestHandle"; } const eckit::ReanimatorBase& reanimator() const override; static const eckit::ClassSpec& classSpec(); const eckit::StringDict& stats() const; private: // members metkit::mars::MarsRequest request_; std::unique_ptr protocol_; bool opened_; private: // members void print(std::ostream&) const override; void encode(eckit::Stream&) const override; eckit::Length openForRead() override; void openForWrite(const eckit::Length&) override; void openForAppend(const eckit::Length&) override; long read(void*, long) override; long write(const void*, long) override; void close() override; bool canSeek() const override; }; } // namespace metkit::mars #endif metkit-1.19.2/src/metkit/mars/StepRangeNormalise.h0000664000175000017500000000561715232567717022256 0ustar alastairalastair/* * (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. */ /// @author Simon Smart /// @date May 2019 #ifndef metkit_StepRangeNormalise_H #define metkit_StepRangeNormalise_H #include #include "eckit/exception/Exceptions.h" #include "metkit/mars/StepRange.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- // It would be nice to put this in StepRange (as is done with ParamID), but this seems // to break the generation of the StepRange.b file. // // -> Split it out class StepRangeNormalise { public: template static void normalise(std::vector& v, const AXIS_T& axis); }; //---------------------------------------------------------------------------------------------------------------------- template void StepRangeNormalise::normalise(std::vector& values, const AXIS_T& axis) { std::vector outputValues; for (eckit::Ordinal i = 0; i < values.size(); ++i) { // If the supplied range is found in the axis, then use that auto j = std::find(axis.begin(), axis.end(), values[i]); if (j != axis.end()) { outputValues.push_back(values[i]); // If specified, and matched, a RANGE, then use that if (values[i].from() != values[i].to()) { eckit::Log::info() << "Matched range: " << values[i] << std::endl; continue; } } bool matched = false; double singleValue = values[i].from(); if (values[i].from() != values[i].to()) { j = std::find(axis.begin(), axis.end(), StepRange(singleValue, singleValue)); if (j != axis.end()) { outputValues.push_back(*j); matched = true; } } // If singleValue == 0, this test is the same as the previous one... if (singleValue != 0) { j = std::find(axis.begin(), axis.end(), StepRange(0, singleValue)); if (j != axis.end()) { if (matched) { eckit::Log::userWarning() << "Step " << values[i] << " matches " << values[i] << " and " << (*j) << std::endl; } outputValues.push_back(*j); } } } std::swap(values, outputValues); } //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/ParamID.cc0000664000175000017500000000636115232567717020124 0ustar alastairalastair/* * (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 "metkit/mars/ParamID.h" #include #include #include "eckit/config/Resource.h" #include "eckit/parser/YAMLParser.h" #include "eckit/system/Library.h" #include "eckit/utils/Tokenizer.h" #include "metkit/config/LibMetkit.h" using namespace eckit; namespace metkit { //---------------------------------------------------------------------------------------------------------------------- static std::vector windFamilies_; static std::vector dropTables_; static NormalisationMode normalisationMode_; static std::set mlParamsSingleLevel_; //---------------------------------------------------------------------------------------------------------------------- static pthread_once_t once = PTHREAD_ONCE_INIT; static void readTable() { eckit::PathName paramMatchingPath = eckit::Resource("paramMatchingPath;$PARAM_MATCHING_PATH", LibMetkit::paramMatchingYamlFile()); const eckit::Value paramMatching = eckit::YAMLParser::decodeFile(paramMatchingPath); const eckit::Value wind = paramMatching["wind"]; ASSERT(wind.isList()); for (size_t i = 0; i < wind.size(); ++i) { const eckit::Value s = wind[i]; ASSERT(s.isList()); ASSERT(s.size() == 4); windFamilies_.push_back(ParamID::WindFamily(s[0], s[1], s[2], s[3])); } const eckit::Value dropTables = paramMatching["drop-tables"]; ASSERT(dropTables.isList()); for (size_t i = 0; i < dropTables.size(); ++i) { dropTables_.push_back(dropTables[i]); } normalisationMode_ = NormalisationMode::Loose; if (paramMatching.contains("full-table-dropping")) { const eckit::Value fullTableDropping = paramMatching["full-table-dropping"]; ASSERT(fullTableDropping.isBool()); bool fullTableDroppingValue = fullTableDropping; if (fullTableDroppingValue) { normalisationMode_ = NormalisationMode::FullTableDropping; } } const eckit::Value mlParamsSingleLevel = paramMatching["ml-params-single-level"]; ASSERT(mlParamsSingleLevel.isList()); for (size_t i = 0; i < mlParamsSingleLevel.size(); ++i) { mlParamsSingleLevel_.insert(mlParamsSingleLevel[i]); } } const std::vector& ParamID::getWindFamilies() { pthread_once(&once, readTable); return windFamilies_; } const std::vector& ParamID::getDropTables() { pthread_once(&once, readTable); return dropTables_; } const std::set& ParamID::getMlParamsSingleLevel() { pthread_once(&once, readTable); return mlParamsSingleLevel_; } NormalisationMode ParamID::normalisationMode() { pthread_once(&once, readTable); return normalisationMode_; } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit metkit-1.19.2/src/metkit/mars/Parameter.cc0000664000175000017500000000641315232567717020565 0ustar alastairalastair/* * (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 #include #include "metkit/mars/Parameter.h" #include "metkit/mars/Type.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- class UndefinedType : public Type { void print(std::ostream& out) const override { out << ""; } bool expand(std::string&, const MarsRequest&) const override { NOTIMP; } public: UndefinedType() : Type("", eckit::Value()) { attach(); } }; static UndefinedType undefined; //---------------------------------------------------------------------------------------------------------------------- Parameter::Parameter() : type_(&undefined) { type_->attach(); } Parameter::~Parameter() { type_->detach(); } Parameter::Parameter(const std::vector& values, Type* type) : type_(type), values_(values) { if (!type) { type_ = &undefined; } type_->attach(); } Parameter::Parameter(const Parameter& other) : type_(other.type_), values_(other.values_) { type_->attach(); } Parameter& Parameter::operator=(const Parameter& other) { Type* old = type_; type_ = other.type_; type_->attach(); old->detach(); values_ = other.values_; return *this; } void Parameter::values(const std::vector& values) { values_ = values; } bool Parameter::filter(const std::vector& filter) { return type_->filter(filter, values_); } bool Parameter::filter(const std::string& keyword, const std::vector& filter) { return type_->filter(keyword, filter, values_); } bool Parameter::matches(const std::vector& match) const { return type_->matches(match, values_); } void Parameter::merge(const Parameter& p) { ASSERT(name() == p.name()); /// @note this isn't optimal O(N^2) but it respects the order std::vector diff; for (auto& o : p.values_) { bool found = false; for (auto& v : values_) { if (v == o) { found = true; break; } } if (!found) diff.push_back(o); } values_.insert(values_.end(), std::make_move_iterator(diff.begin()), std::make_move_iterator(diff.end())); } const std::string& Parameter::name() const { return type_->name(); } size_t Parameter::count() const { return type_->count(values_); } void Parameter::print(std::ostream& s) const { s << "Parameter[type=" << *type_ << ",values=" << values_ << "]"; } bool Parameter::operator<(const Parameter& other) const { if (name() != other.name()) { return name() < other.name(); } return values_ < other.values_; } //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit metkit-1.19.2/src/metkit/mars/MarsExpansion.cc0000664000175000017500000000421515232567717021432 0ustar alastairalastair/* * (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 "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsLanguage.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- MarsExpansion::MarsExpansion(bool inherit, bool strict) : inherit_(inherit), strict_(strict) {} MarsExpansion::~MarsExpansion() { for (auto& language : languages_) { delete language.second; } } void MarsExpansion::reset() { for (auto& language : languages_) { language.second->reset(); } } MarsLanguage& MarsExpansion::language(const std::string& verb) { auto v = MarsLanguage::expandVerb(verb); if (auto j = languages_.find(v); j != languages_.end()) { return *(*j).second; } auto j = languages_.emplace(v, new MarsLanguage(v)).first; return *(*j).second; } std::vector MarsExpansion::expand(const std::vector& requests) { std::vector result; result.reserve(requests.size()); // Implement inheritence for (const auto& request : requests) { auto& lang = language(request.verb()); result.emplace_back(lang.expand(request, inherit_, strict_)); } return result; } MarsRequest MarsExpansion::expand(const MarsRequest& request) { return language(request.verb()).expand(request, inherit_, strict_); } void MarsExpansion::expand(const MarsRequest& request, ExpandCallback& callback) { callback(expand(request)); } void MarsExpansion::flatten(const MarsRequest& request, FlattenCallback& callback) { language(request.verb()).flatten(request, callback); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeFloat.h0000664000175000017500000000225615232567717020417 0ustar alastairalastair/* * (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. */ /// @file TypeFloat.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @author Emanuele Danovaro /// @date April 2016 #pragma once #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeFloat : public Type { public: // methods TypeFloat(const std::string& name, const eckit::Value& settings); ~TypeFloat() noexcept override = default; private: // methods void print(std::ostream& out) const override; bool expand(std::string& value, const MarsRequest& request) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeExpver.h0000664000175000017500000000222415232567717020616 0ustar alastairalastair/* * (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. */ /// @file TypeExpver.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @date April 2016 #pragma once #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeExpver : public Type { public: // methods TypeExpver(const std::string& name, const eckit::Value& settings); ~TypeExpver() noexcept override = default; bool expand(std::string& value, const MarsRequest& request) const override; private: // methods void print(std::ostream& out) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsParser.h0000664000175000017500000000327215232567717020566 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @date Jun 2012 #pragma once #include "eckit/parser/StreamParser.h" #include "eckit/types/Types.h" #include "metkit/mars/MarsParsedRequest.h" namespace metkit::mars { class MarsRequest; //---------------------------------------------------------------------------------------------------------------------- class MarsParserCallback { public: virtual void operator()(const MarsRequest&) = 0; protected: virtual ~MarsParserCallback() = default; }; //---------------------------------------------------------------------------------------------------------------------- class MarsParser : public eckit::StreamParser { public: // methods MarsParser(std::istream& in); std::vector parse(); void parse(MarsParserCallback& cb); static void quoted(std::ostream& out, const std::string& value); private: // methods MarsParsedRequest parseRequest(); std::string parseVerb(); std::string parseKeyword(); std::vector parseValues(); std::string parseValue(); std::string parseIndent(); std::string parseIndents(); std::string parseString(char c); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsParsedRequest.h0000664000175000017500000000227715232567717022125 0ustar alastairalastair/* * (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. */ /// @author Manuel Fuentes /// @author Baudouin Raoult /// @author Tiago Quintino /// @date Sep 96 #pragma once #include "metkit/mars/MarsRequest.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class MarsParsedRequest : public MarsRequest { public: MarsParsedRequest(const std::string& verb, size_t line); virtual ~MarsParsedRequest() = default; virtual void info(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const MarsParsedRequest& r) { r.info(s); return s; } private: std::size_t line_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeEnum.cc0000664000175000017500000001101215232567717020402 0ustar alastairalastair/* * (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 "metkit/mars/TypeEnum.h" #include "eckit/utils/StringTools.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- void TypeEnum::addValue(const std::string& vv, uint16_t idx, bool allowDuplicates) const { std::string value = eckit::StringTools::lower(vv); if (!allowDuplicates && values_.find(value) != values_.end()) { std::ostringstream oss; oss << "Redefined enum value '" << value << "'"; throw eckit::SeriousBug(oss.str()); } values_[value] = idx; } uint16_t TypeEnum::parseValueNames(const eckit::Value& names, bool allowDuplicates) const { std::string firstName = names.isList() ? names[0] : names; uint16_t idx = groups_.size(); addValue(firstName, idx, allowDuplicates); if (names.isList()) { for (size_t i = 1; i < names.size(); ++i) { addValue(names[i], idx, allowDuplicates); } } if (uppercase_) { firstName = eckit::StringTools::upper(firstName); } groups_.emplace_back(firstName, std::vector{}); return idx; } std::vector TypeEnum::parseEnumValue(const eckit::Value& val, bool allowDuplicates) const { if (val.isMap()) { hasGroups_ = true; ASSERT(val.contains("name")); uint16_t idx = parseValueNames(val["name"], allowDuplicates); ASSERT(val.contains("group")); eckit::Value group = val["group"]; ASSERT(group.isList()); std::set outSet; for (size_t i = 0; i < group.size(); ++i) { std::vector groupVals = parseEnumValue(group[i], true); for (const auto& v : groupVals) { if (outSet.find(v) == outSet.end()) { outSet.insert(v); groups_.at(idx).second.push_back(v); } } } return groups_.at(idx).second; } uint16_t idx = parseValueNames(val, allowDuplicates); std::string nn = groups_.at(idx).first; groups_.at(idx).second.push_back(nn); return {nn}; } void TypeEnum::readValuesFile() const { if (!valuesFile_.empty()) { auto values = MarsLanguage::jsonFile(valuesFile_); ASSERT(values.isList()); for (size_t i = 0; i < values.size(); ++i) { parseEnumValue(values[i]); } } } TypeEnum::TypeEnum(const std::string& name, const eckit::Value& settings) : Type(name, settings) { LOG_DEBUG_LIB(LibMetkit) << "TypeEnum name=" << name << " settings=" << settings << std::endl; eckit::Value values = settings["values"]; if (settings.contains("uppercase")) { uppercase_ = settings["uppercase"]; } if (!values.isList()) { valuesFile_ = static_cast(values); } else { for (size_t i = 0; i < values.size(); ++i) { parseEnumValue(values[i]); } } } void TypeEnum::print(std::ostream& out) const { out << "TypeEnum[name=" << name_ << "]"; } bool TypeEnum::expand(std::string& value, const MarsRequest& request) const { auto it = find(value); if (it == values_.end()) { return false; } ASSERT(groups_.size() > it->second); value = groups_.at(it->second).first; return true; } std::map::const_iterator TypeEnum::find(const std::string& value) const { std::call_once(readValues_, &TypeEnum::readValuesFile, this); return values_.find(eckit::StringTools::lower(value)); } std::optional>> TypeEnum::group(const std::string& value) const { ASSERT(hasGroups_); auto it = find(value); if (it != values_.end()) { return groups_.at(it->second).second; } return std::nullopt; } void TypeEnum::reset() { Type::reset(); } static TypeBuilder type("enum"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/Quantile.cc0000664000175000017500000000607115232567717020427 0ustar alastairalastair/* * (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 #include "eckit/exception/Exceptions.h" #include "eckit/utils/Tokenizer.h" #include "metkit/mars/Quantile.h" using namespace eckit; namespace metkit { //---------------------------------------------------------------------------------------------------------------------- Quantile::Quantile(const std::string& value) { Tokenizer parse(":"); std::vector result; parse(value, result); if (result.size() != 2) { std::ostringstream oss; oss << "Quantile " << value << " must be in the form :"; throw eckit::BadValue(oss.str()); } try { num_ = std::stol(result[0]); den_ = std::stol(result[1]); } catch (const std::invalid_argument& e) { std::ostringstream oss; oss << "Quantile " << value << " must be in the form :"; throw eckit::BadValue(oss.str()); } check(); } void Quantile::check() const { if (num_ < 0) { std::ostringstream oss; oss << "Quantile numerator " << num_ << " must be non negative"; throw eckit::BadValue(oss.str()); } if (den_ < 0) { std::ostringstream oss; oss << "Quantile denominator " << den_ << " must be non negative"; throw eckit::BadValue(oss.str()); } if (den_ < num_) { std::ostringstream oss; oss << "Quantile numerator " << num_ << " must be less or equal the value of denominator " << den_; throw eckit::BadValue(oss.str()); } } Quantile::Quantile(long num, long den) : num_(num), den_(den) { check(); } Quantile::operator std::string() { std::ostringstream oss; oss << num_ << ':' << den_; return oss.str(); } void Quantile::print(std::ostream& s) const { s << num_ << ':' << den_; } Quantile& Quantile::operator+=(const long& rhs) { num_ += rhs; check(); return *this; } Quantile& Quantile::operator-=(const long& rhs) { num_ -= rhs; check(); return *this; } bool operator==(const Quantile& lhs, const Quantile& rhs) { if (lhs.den() != rhs.den()) { std::ostringstream oss; oss << "Quantile values must belong to the same quantile group"; throw eckit::BadValue(oss.str()); } return (lhs.num() == rhs.num()); } bool operator<(const Quantile& lhs, const Quantile& rhs) { if (lhs.den() != rhs.den()) { std::ostringstream oss; oss << "Quantile values must belong to the same quantile group"; throw eckit::BadValue(oss.str()); } return (lhs.num() < rhs.num()); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit metkit-1.19.2/src/metkit/mars/BaseProtocol.cc0000664000175000017500000000634715232567717021247 0ustar alastairalastair/* * (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. */ // File BaseProtocol.cc // Baudouin Raoult - (c) ECMWF Feb 12 #include "metkit/mars/BaseProtocol.h" #include #include #include "eckit/config/Configuration.h" #include "eckit/exception/Exceptions.h" #include "eckit/log/Log.h" #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "eckit/types/Types.h" namespace metkit::mars { static eckit::Mutex* local_mutex = 0; static std::map* m = 0; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { local_mutex = new eckit::Mutex(); m = new std::map(); } BaseProtocol::BaseProtocol() = default; BaseProtocol::BaseProtocol(eckit::Stream& s) : eckit::Streamable(s) {} BaseProtocol::BaseProtocol(const eckit::Configuration&) {} const eckit::StringDict& BaseProtocol::stats() const { static const eckit::StringDict empty{}; return empty; } const eckit::ClassSpec& BaseProtocol::classSpec() { static eckit::ClassSpec spec = {&Streamable::classSpec(), "BaseProtocol"}; return spec; } void BaseProtocol::encode(eckit::Stream& s) const { eckit::Streamable::encode(s); } //======================================================================== ProtocolFactory::ProtocolFactory(const std::string& name) : name_(name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); if (m->find(name) != m->end()) { throw eckit::SeriousBug("ProtocolFactory: duplication action: " + name); } ASSERT(m->find(name) == m->end()); (*m)[name] = this; } ProtocolFactory::~ProtocolFactory() { eckit::AutoLock lock(local_mutex); m->erase(name_); } BaseProtocol* ProtocolFactory::build(const eckit::Configuration& params) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); const std::string& name = params.getString("class"); std::map::const_iterator j = m->find(name); if (j == m->end()) { if (j == m->end()) { eckit::Log::error() << "No ProtocolFactory for [" << name << "]" << std::endl; eckit::Log::error() << "ProtocolFactories are:" << std::endl; for (j = m->begin(); j != m->end(); ++j) eckit::Log::error() << " " << (*j).first << std::endl; throw eckit::SeriousBug(std::string("No ProtocolFactory called ") + name); } } return (*j).second->make(params); } void ProtocolFactory::list(std::ostream& out) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); const char* sep = ""; for (std::map::const_iterator j = m->begin(); j != m->end(); ++j) { out << sep << (*j).first; sep = ", "; } } } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeAny.h0000664000175000017500000000223015232567717020071 0ustar alastairalastair/* * (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. */ /// @file TypeAny.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @date April 2016 #pragma once #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeAny : public Type { public: // methods TypeAny(const std::string& name, const eckit::Value& settings = eckit::Value()); ~TypeAny() noexcept override = default; private: // methods void print(std::ostream& out) const override; bool expand(std::string& value, const MarsRequest& request) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeToByListQuantile.h0000664000175000017500000000235215232567717022563 0ustar alastairalastair/* * (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. */ /// @file TypeToByListQuantile.h /// @author Emanuele Danovaro /// @date February 2022 #pragma once #include "metkit/mars/Quantile.h" #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeToByListQuantile : public Type { public: // methods TypeToByListQuantile(const std::string& name, const eckit::Value& settings); ~TypeToByListQuantile() noexcept override = default; private: // methods void print(std::ostream& out) const override; bool expand(std::string& value, const MarsRequest& request) const override; std::set denominators_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/Type.cc0000664000175000017500000002751215232567717017571 0ustar alastairalastair/* * (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 "metkit/mars/Type.h" #include #include #include #include #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "eckit/value/Value.h" #include "metkit/hypercube/HyperCube.h" #include "metkit/mars/ContextRule.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/TypeToByList.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- void Context::add(std::unique_ptr rule) { rules_.push_back(std::move(rule)); } bool Context::matches(MarsRequest req) const { for (const auto& r : rules_) { if (!r->matches(req)) { return false; } } return true; } std::ostream& operator<<(std::ostream& s, const Context& c) { c.print(s); return s; } void Context::print(std::ostream& out) const { std::string sep; out << "Context["; for (const auto& r : rules_) { out << sep << *r; sep = ","; } out << "]"; } //---------------------------------------------------------------------------------------------------------------------- // HELPERS std::unique_ptr parseRule(std::string key, eckit::Value r) { std::set vals; if (r.isList()) { if (r.size() == 0) { throw eckit::UserError("Empty list for context rule '" + key + "'"); } bool exclude = (r[0] == "!"); for (size_t k = exclude ? 1 : 0; k < r.size(); k++) { vals.insert(r[k]); } if (exclude) return std::make_unique(key, vals); return std::make_unique(key, vals); } else { ASSERT(r.isString()); std::string v = r; if (v == "undefined") { return std::make_unique(key); } else if (v == "defined") { return std::make_unique(key); } } return nullptr; } std::unique_ptr Context::parseContext(eckit::Value c) { std::unique_ptr context = std::make_unique(); eckit::Value keys = c.keys(); for (size_t j = 0; j < keys.size(); j++) { std::string key = keys[j]; context->add(parseRule(key, c[key])); } return context; } size_t Context::maxAxisIndex() const { size_t maxIndex = 0; for (const auto& r : rules_) { size_t idx = 0; if (!r->key().empty() && r->key()[0] != '_') { idx = metkit::hypercube::AxisOrder::instance().index(r->key()); if (idx > maxIndex) { maxIndex = idx; } } } return maxIndex; } //---------------------------------------------------------------------------------------------------------------------- Type::Type(const std::string& name, const eckit::Value& settings) : name_(name), flatten_(true), multiple_(false), duplicates_(true) { if (settings.contains("multiple")) { multiple_ = settings["multiple"]; } if (settings.contains("flatten")) { flatten_ = settings["flatten"]; } if (settings.contains("duplicates")) { duplicates_ = settings["duplicates"]; } if (settings.contains("category")) { category_ = std::string(settings["category"]); } if (settings.contains("defaults")) { eckit::Value defaults = settings["defaults"]; if (!defaults.isNil() && defaults.isList()) { for (size_t i = 0; i < defaults.size(); i++) { std::vector vals; eckit::Value d = defaults[i]; ASSERT(d.contains("vals")); eckit::Value vv = d["vals"]; if (vv.isList()) { for (size_t k = 0; k < vv.size(); k++) { vals.push_back(vv[k]); } } else { vals.push_back(vv); } if (d.contains("context")) { defaults_.emplace(Context::parseContext(d["context"]), vals); } else { defaults_.emplace(std::make_unique(), vals); } } } } } void Type::defaults(std::shared_ptr context, const std::vector& values) { defaults_.emplace(std::move(context), values); } void Type::set(std::shared_ptr context, const std::vector& values) { sets_.emplace(std::move(context), values); } void Type::unset(std::shared_ptr context) { unsets_.insert(std::move(context)); } void Type::patchRequest(MarsRequest& request, const std::vector& values) { // Special case: inheritance from another key. // If the value is of the form _key, then copy values from that key if (values.size() == 1 && values[0][0] == '_') { std::string key = values[0].substr(1); if (request.has(key)) { request.setValuesTyped(this, request.values(key)); } } else { request.setValuesTyped(this, values); } } bool Type::flatten() const { return flatten_; } bool Type::multiple() const { return multiple_; } size_t Type::count(const std::vector& values) const { return flatten_ ? values.size() : 1; } class NotInSet { std::set set_; public: NotInSet(const std::vector& f) : set_(f.begin(), f.end()) {} bool operator()(const std::string& s) const { return set_.find(s) == set_.end(); } }; bool Type::filter(const std::vector& filter, std::vector& values) const { NotInSet not_in_set(filter); values.erase(std::remove_if(values.begin(), values.end(), not_in_set), values.end()); return !values.empty(); } bool Type::filter(const std::string& keyword, const std::vector& f, std::vector& values) const { if (keyword == name_) { return filter(f, values); } auto it = filters_.find(keyword); if (it == filters_.end()) { return false; } return it->second(f, values); } class InSet { std::set set_; public: InSet(const std::vector& f) : set_(f.begin(), f.end()) {} bool operator()(const std::string& s) const { return set_.find(s) != set_.end(); } }; bool Type::matches(const std::vector& match, const std::vector& values) const { InSet in_set(match); return std::find_if(values.begin(), values.end(), in_set) != values.end(); } std::ostream& operator<<(std::ostream& s, const Type& x) { x.print(s); return s; } std::string Type::tidy(const std::string& value, const MarsRequest& request) const { std::string result = value; expand(result, request); return result; } bool Type::expand(std::string& value, const MarsRequest&) const { std::ostringstream oss; oss << *this << ": expand not implemented (" << value << ")"; throw eckit::SeriousBug(oss.str()); } void Type::expand(std::vector& values, const MarsRequest& request) const { if (toByList_ && values.size() > 1) { toByList_->expandRanges(values, request); } std::vector newvals; std::set seen; for (std::string& val : values) { std::string value = val; if (!expand(value, request)) { std::ostringstream oss; oss << *this << ": cannot expand '" << val << "'"; throw eckit::UserError(oss.str()); } if (hasGroups()) { auto gg = group(value); if (gg) { for (const auto& v : gg->get()) { if (seen.find(v) == seen.end()) { seen.insert(v); newvals.push_back(v); } } } } else { if (!duplicates_ && seen.find(value) != seen.end()) { std::ostringstream oss; oss << *this << ": duplicated value '" << value << "'"; throw eckit::UserError(oss.str()); } newvals.push_back(value); } } std::swap(newvals, values); if (!multiple_ && values.size() > 1) { throw eckit::UserError("Only one value possible for '" + name_ + "'"); } } void Type::setDefaults(MarsRequest& request) { if (inheritance_) { request.setValuesTyped(this, inheritance_.value()); } else { bool unset = false; for (const auto& unsetContext : unsets_) { if (unsetContext->matches(request)) { unset = true; break; } } if (!unset) { for (const auto& [defaultContext, values] : defaults_) { if (defaultContext->matches(request)) { patchRequest(request, values); break; } } } } } void Type::setInheritance(const std::vector& inheritance) { inheritance_ = inheritance; } const std::vector& Type::flattenValues(const MarsRequest& request) { return request.values(name_); } void Type::clearDefaults() { defaults_.clear(); } void Type::reset() { inheritance_.reset(); } const std::string& Type::name() const { return name_; } const std::string& Type::category() const { return category_; } void Type::pass2(MarsRequest& request) {} void Type::finalise(MarsRequest& request, bool strict) { const std::vector& values = request.values(name_, true); if (values.size() == 1 && values[0] == "off") { request.unsetValues(name_); } else { if (values.size() > 0) { for (const auto& context : unsets_) { if (context->matches(request)) { if (strict && request.has(name_)) { std::ostringstream oss; oss << *this << ": Key [" << name_ << "] not acceptable with context: " << *context; throw eckit::UserError(oss.str()); } request.unsetValues(name_); } } } if (request.verb() != "list") { for (const auto& [context, values] : sets_) { if (context->matches(request)) { if (strict && !request.has(name_)) { std::ostringstream oss; oss << *this << ": missing Key [" << name_ << "] - required with context: " << *context; throw eckit::UserError(oss.str()); } patchRequest(request, values); } } } } } void Type::check(const std::vector& values) const { if (flatten_) { std::set s(values.begin(), values.end()); if (values.size() != s.size()) { std::cerr << "Duplicate values in " << name_ << " " << values; std::set seen; for (const std::string& val : values) { if (seen.find(val) != seen.end()) { std::cerr << ' ' << val; } seen.insert(val); } std::cerr << std::endl; } } } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/BaseProtocol.h0000664000175000017500000000421515232567717021101 0ustar alastairalastair/* * (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. */ // File BaseProtocol.h // Baudouin Raoult - (c) ECMWF Feb 12 #ifndef BaseProtocol_H #define BaseProtocol_H #include #include "eckit/io/Length.h" #include "eckit/serialisation/Streamable.h" #include "eckit/types/Types.h" namespace eckit { class Configuration; } namespace metkit::mars { class MarsRequest; class BaseProtocol : public eckit::Streamable { public: BaseProtocol(); BaseProtocol(eckit::Stream&); BaseProtocol(const eckit::Configuration&); virtual eckit::Length retrieve(const MarsRequest&) = 0; virtual void archive(const MarsRequest&, const eckit::Length&) = 0; virtual long read(void* buffer, long len) = 0; virtual long write(const void* buffer, long len) = 0; virtual void cleanup() = 0; virtual const eckit::StringDict& stats() const; // -- Overridden methods (from Streamable) static const eckit::ClassSpec& classSpec(); protected: virtual void print(std::ostream&) const = 0; void encode(eckit::Stream&) const override; private: friend std::ostream& operator<<(std::ostream& s, const BaseProtocol& p) { p.print(s); return s; } }; class ProtocolFactory { std::string name_; virtual BaseProtocol* make(const eckit::Configuration&) = 0; protected: ProtocolFactory(const std::string&); virtual ~ProtocolFactory(); public: static BaseProtocol* build(const eckit::Configuration&); static void list(std::ostream&); }; template class ProtocolBuilder : public ProtocolFactory { BaseProtocol* make(const eckit::Configuration& param) override { return new T(param); } public: ProtocolBuilder(const std::string& name) : ProtocolFactory(name) {} }; } // namespace metkit::mars #endif metkit-1.19.2/src/metkit/mars/TypeTime.h0000664000175000017500000000233115232567717020242 0ustar alastairalastair/* * (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. */ /// @file TypeTime.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @author Emanuele Danovaro /// @date April 2016 #ifndef metkit_TypeTime_H #define metkit_TypeTime_H #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeTime : public Type { public: // methods TypeTime(const std::string& name, const eckit::Value& settings); ~TypeTime() noexcept override = default; private: // methods void print(std::ostream& out) const override; bool expand(std::string& value, const MarsRequest& request) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars #endif metkit-1.19.2/src/metkit/mars/TypeLowercase.h0000664000175000017500000000223215232567717021270 0ustar alastairalastair/* * (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. */ /// @file TypeLowercase.h /// @author Emanuele Danovaro /// @date February 2024 #pragma once #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeLowercase : public Type { public: // methods TypeLowercase(const std::string& name, const eckit::Value& settings = eckit::Value()); ~TypeLowercase() noexcept override = default; private: // methods void print(std::ostream& out) const override; bool expand(std::string& value, const MarsRequest& request) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/DHSProtocol.h0000664000175000017500000000622215232567717020645 0ustar alastairalastair/* * (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. */ // File DHSProtocol.h // Baudouin Raoult - (c) ECMWF Feb 12 #ifndef DHSProtocol_H #define DHSProtocol_H #include #include #include #include #include "eckit/io/Length.h" #include "eckit/net/Endpoint.h" #include "eckit/net/TCPSocket.h" #include "eckit/serialisation/Streamable.h" #include "eckit/types/Types.h" #include "metkit/mars/BaseProtocol.h" #include "metkit/mars/ClientTask.h" #include "metkit/mars/MarsRequest.h" namespace metkit::mars { // --------------------------------------------------------------------------------------------------------------------- class BaseCallbackConnection : public eckit::Streamable { public: BaseCallbackConnection() = default; static BaseCallbackConnection* build(const eckit::Configuration& config, const std::string& host = ""); virtual const eckit::net::Endpoint& endpoint() const = 0; virtual eckit::net::TCPSocket& connect() = 0; void encode(eckit::Stream&) const override = 0; static const eckit::ClassSpec& classSpec(); }; // --------------------------------------------------------------------------------------------------------------------- class DHSProtocol : public BaseProtocol { public: DHSProtocol(const eckit::Configuration&); DHSProtocol(const eckit::Configuration&, const std::map& env); DHSProtocol(const std::string& name, const std::string& host, int port, bool forwardMessages = false); DHSProtocol(eckit::Stream&); ~DHSProtocol() override; // -- Overridden methods (from Streamable) std::string className() const override { return "DHSProtocol"; } const eckit::ReanimatorBase& reanimator() const override; static const eckit::ClassSpec& classSpec(); const eckit::StringDict& stats() const override { return stats_; } private: // -- Members std::unique_ptr callback_; eckit::net::TCPSocket socket_; std::string name_; std::string host_; int port_; std::string msg_; std::unique_ptr task_; bool done_; bool error_; bool sending_; bool forward_; MarsRequest env_; eckit::StringDict stats_; // -- Methods bool wait(eckit::Length&); // -- Overridden methods // From BaseProtocol eckit::Length retrieve(const MarsRequest& request) override; void archive(const MarsRequest& request, const eckit::Length&) override; long read(void* buffer, long len) override; long write(const void* buffer, long len) override; void cleanup() override; void print(std::ostream&) const override; void encode(eckit::Stream&) const override; }; // --------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars #endif metkit-1.19.2/src/metkit/mars/Quantile.h0000664000175000017500000000344415232567717020272 0ustar alastairalastair/* * (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. */ /// @file Quantile.h /// @author Emanuele Danovaro /// @date February 2022 #pragma once #include namespace metkit { //---------------------------------------------------------------------------------------------------------------------- class Quantile { public: Quantile(const std::string& value); Quantile(long num, long den); long num() const { return num_; } long den() const { return den_; } operator std::string(); Quantile& operator+=(const long& rhs); Quantile& operator-=(const long& rhs); protected: void print(std::ostream& s) const; private: void check() const; friend std::ostream& operator<<(std::ostream& s, const Quantile& q) { q.print(s); return s; } private: long num_; long den_; }; bool operator==(const Quantile& lhs, const Quantile& rhs); inline bool operator!=(const Quantile& lhs, const Quantile& rhs) { return !(rhs == lhs); } bool operator<(const Quantile& lhs, const Quantile& rhs); inline bool operator>(const Quantile& lhs, const Quantile& rhs) { return rhs < lhs; } inline bool operator<=(const Quantile& lhs, const Quantile& rhs) { return !(lhs > rhs); } inline bool operator>=(const Quantile& lhs, const Quantile& rhs) { return !(lhs < rhs); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit metkit-1.19.2/src/metkit/mars/Param.h0000664000175000017500000000405215232567717017544 0ustar alastairalastair/* * (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. */ // File Param.h // Baudouin Raoult - ECMWF Dec 97 #ifndef metkit_Param_H #define metkit_Param_H #include #include "eckit/persist/Bless.h" namespace eckit { class DumpLoad; } namespace metkit { //---------------------------------------------------------------------------------------------------------------------- class Param { public: Param() : table_(-1), value_(-1) {} Param(const std::string&); Param(long table, long value) : table_(table), value_(value) {} #include "metkit/mars/Param.b" ~Param() {} operator std::string() const; bool operator==(const Param& other) const { return value_ == other.value_ && table_ == other.table_; } bool operator!=(const Param& other) const { return value_ != other.value_ || table_ != other.table_; } bool operator<(const Param& other) const { return (value_ == other.value_) ? (table_ < other.table_) : (value_ < other.value_); } bool operator>(const Param& other) const { return (value_ == other.value_) ? (table_ > other.table_) : (value_ > other.value_); } long table() const { return table_; } long value() const { return value_; } long grib1value() const { return value_ % 1000; } long paramId() const; void dump(eckit::DumpLoad&) const; void load(eckit::DumpLoad&); protected: void print(std::ostream& s) const; private: long table_; long value_; friend std::ostream& operator<<(std::ostream& s, const Param& p) { p.print(s); return s; } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/ContextRule.h0000664000175000017500000000115715232567717020763 0ustar alastairalastair/* * (C) Copyright 2025- 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. */ #pragma once #include "metkit/mars/MarsRequest.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeInteger.h0000664000175000017500000000255215232567717020746 0ustar alastairalastair/* * (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. */ /// @file TypeInteger.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @author Emanuele Danovaro /// @date April 2016 #pragma once #include #include #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeInteger : public Type { public: // methods TypeInteger(const std::string& name, const eckit::Value& settings); protected: bool ok(const std::string& value, long& n) const; bool expand(std::string& value, const MarsRequest& request) const override; private: // methods void print(std::ostream& out) const override; private: // members struct Range { int lower_; int upper_; }; std::optional range_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeDate.cc0000664000175000017500000001330615232567717020363 0ustar alastairalastair/* * (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 "metkit/mars/TypeDate.h" #include #include #include #include "eckit/types/Date.h" #include "eckit/utils/StringTools.h" #include "eckit/utils/Tokenizer.h" #include "eckit/utils/Translator.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/TypeToByList.h" #include "metkit/mars/TypesFactory.h" namespace { //---------------------------------------------------------------------------------------------------------------------- static std::array months{"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"}; std::string month(const std::string& value) { if (std::isdigit(value[0])) { eckit::Translator s2i; int m = s2i(value); if (m > 12) { std::ostringstream oss; oss << value << " is not a valid month"; throw eckit::BadValue(oss.str()); } return months[m - 1]; } ASSERT(value.size() >= 3); const auto* it = std::find(months.begin(), months.end(), eckit::StringTools::lower(value.substr(0, 3))); if (it == months.end()) { std::ostringstream oss; oss << value << " is not a valid month name"; throw eckit::BadValue(oss.str()); } return *it; } long day(std::string& value) { if (value.empty()) { return -1; } eckit::Translator s2l; if (value[0] == '0' || value[0] == '-') { long n = s2l(value); if (n <= 0) { eckit::Date now(n); return now.day(); } } else { eckit::Tokenizer t("-"); std::vector tokens = t.tokenize(value); if (tokens.size() == 2) { if (std::isdigit(tokens[0][0]) && tokens[0].size() > 2) { // year-dayOfYear (e.g. 2018-23 ==> 20180123) eckit::Date d(s2l(tokens[0]), s2l(tokens[1])); return d.day(); } else { // month-day (i.e. TypeClimateDaily) return s2l(tokens[1]); } } if (tokens.size() == 1 && (!std::isdigit(value[0]) || value.size() <= 2)) { // month (i.e. TypeClimateMonthly) return -1; } eckit::Date date(value); return date.day(); } return -1; } bool filterByDay(const std::vector& filter, std::vector& values) { std::set days; eckit::Translator s2l; for (auto f : filter) { days.emplace(s2l(f)); } for (auto v = values.begin(); v != values.end();) { long d = day(*v); if (d != -1) { auto it = days.find(d); if (it != days.end()) { v++; continue; } } v = values.erase(v); } return !values.empty(); } } // namespace namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- TypeDate::TypeDate(const std::string& name, const eckit::Value& settings) : Type(name, settings) { toByList_ = std::make_unique>(*this, settings); multiple_ = true; filters_["day"] = &filterByDay; } void TypeDate::pass2(MarsRequest& request) { std::vector values = request.values(name_, true); if (values.size() == 1 && values[0] == "-1") { Type::expand(values, request); request.setValuesTyped(this, values); } } bool TypeDate::expand(std::string& value, const MarsRequest&) const { if (!value.empty()) { eckit::Translator s2l; eckit::Translator l2s; if (value[0] == '0' || value[0] == '-') { long n = s2l(value); if (n <= 0) { eckit::Date now(n); value = l2s(now.yyyymmdd()); } } else { eckit::Tokenizer t("-"); std::vector tokens = t.tokenize(value); if (tokens.size() == 2) { // month-day (i.e. TypeClimateDaily) if (std::isdigit(tokens[0][0]) && tokens[0].size() > 2) { // year-dayOfYear (e.g. 2018-23 ==> 20180123) eckit::Date d(s2l(tokens[0]), s2l(tokens[1])); value = l2s(d.yyyymmdd()); } else { // month-day (i.e. TypeClimateDaily) std::string m = month(tokens[0]); long d = s2l(tokens[1]); value = m + "-" + l2s(d); } } else { if (tokens.size() == 1 && (!std::isdigit(value[0]) || value.size() <= 2)) { // month (i.e. TypeClimateMonthly) value = month(tokens[0]); } else { eckit::Date date(value); value = l2s(date.yyyymmdd()); } } } } return true; } void TypeDate::print(std::ostream& out) const { out << "TypeDate[name=" << name_ << "]"; } static TypeBuilder type("date"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsLocation.h0000664000175000017500000000466215232567717021106 0ustar alastairalastair/* * (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. */ /// @author Tiago Quintino /// @date Dec 2015 #ifndef metkit_MarsLocation_H #define metkit_MarsLocation_H #include "eckit/config/Configuration.h" #include "metkit/mars/MarsRequest.h" namespace eckit { class JSON; class Stream; } // namespace eckit namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- /// MarsLocation represents a MarsRequest associated with the hostname and port. /// From this location, the data can be directly retrieved without going through a queueing system. /// It assumes all the data identified by the request can be got from the same location. /// This is useful to identify and retrieve data directly from memory. /// /// If we consider that a MarsRequest is analogue to a URI, then a MarsLocation is an analogue to a URL class MarsLocation { public: // methods // - Constructors MarsLocation(const MarsRequest& r, const std::string& hostname, int port); MarsLocation(eckit::Stream&); MarsLocation(const eckit::Configuration&); // -- Destructor ~MarsLocation(); // -- Operators operator eckit::Value() const; void json(eckit::JSON&) const; const MarsRequest& request() const; std::string hostname() const; int port() const; private: // members MarsRequest request_; std::string hostname_; int port_; private: // methods void print(std::ostream&) const; void encode(eckit::Stream&) const; // -- Class members friend std::ostream& operator<<(std::ostream& s, const MarsLocation& r) { r.print(s); return s; } friend eckit::JSON& operator<<(eckit::JSON& s, const MarsLocation& r) { r.json(s); return s; } friend eckit::Stream& operator<<(eckit::Stream& s, const MarsLocation& r) { r.encode(s); return s; } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/TypeExpver.cc0000664000175000017500000000277715232567717020771 0ustar alastairalastair/* * (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 #include "eckit/utils/StringTools.h" #include "eckit/utils/Translator.h" #include "metkit/mars/TypeExpver.h" #include "metkit/mars/TypesFactory.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- TypeExpver::TypeExpver(const std::string& name, const eckit::Value& settings) : Type(name, settings) {} bool TypeExpver::expand(std::string& value, const MarsRequest&) const { std::string v = eckit::StringTools::lower(eckit::StringTools::trim(value)); if (v == "all" || v == "any") { value = v; } else { std::ostringstream oss; oss << std::setfill('0') << std::setw(4) << eckit::StringTools::trim(value); value = oss.str(); } return true; } void TypeExpver::print(std::ostream& out) const { out << "TypeExpver[name=" << name_ << "]"; } static TypeBuilder type("expver"); //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit metkit-1.19.2/src/metkit/mars/TypeToByList.h0000664000175000017500000001124315232567717021057 0ustar alastairalastair/* * (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. */ /// @file TypeToByList.h /// @author Emanuele Danovaro /// @date March 2025 #pragma once #include "eckit/exception/Exceptions.h" #include "eckit/utils/StringTools.h" #include "eckit/utils/Translator.h" #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- template class TypeToByList : public ITypeToByList { private: // members const Type& type_; const std::string by_; public: // methods TypeToByList(const Type& type, const eckit::Value& settings) : type_(type), by_(settings.contains("by") ? settings["by"] : "1") {} virtual ~TypeToByList() = default; void expandRanges(std::vector& values, const MarsRequest& request) const override { if (values.size() == 1) { return; } static eckit::Translator s2el; static eckit::Translator s2by; static eckit::Translator el2s; std::vector newval; for (size_t i = 0; i < values.size(); ++i) { const std::string& s = values[i]; if (eckit::StringTools::lower(s) == "to" || eckit::StringTools::lower(s) == "t0") { // TimeUnit unit; if (newval.size() == 0) { std::ostringstream oss; oss << type_.name() << " list: 'to' must be preceeded by a starting value."; throw eckit::BadValue(oss.str()); } if (values.size() <= i + 1) { std::ostringstream oss; oss << type_.name() << " list: 'to' must be followed by an ending value."; throw eckit::BadValue(oss.str()); } EL from = s2el(type_.tidy(values[i - 1], request)); std::string to_s = type_.tidy(values[i + 1], request); EL to = s2el(to_s); BY by = s2by(by_); if (i + 2 < values.size() && eckit::StringTools::lower(values[i + 2]) == "by") { if (values.size() <= i + 3) { std::ostringstream oss; oss << type_.name() << " list: 'by' must be followed by a step size."; throw eckit::BadValue(oss.str()); } by = s2by(values[i + 3]); i += 2; } i++; if (by == BY{0}) { std::ostringstream oss; oss << type_.name() + ": 'by' value " << by << " cannot be zero"; throw eckit::BadValue(oss.str()); } if (from < to && by < BY{0}) { std::ostringstream oss; oss << type_.name() << ": impossible to define a sequence starting from " << from << " to " << to << " with step " << by; throw eckit::BadValue(oss.str()); } bool addBy = (from < to && by > BY{0}) || (from > to && by < BY{0}); if (from == to) { continue; } EL j = from; while (j != to) { std::string j_s; try { if (addBy) { j += by; } else { j -= by; } j_s = type_.tidy(el2s(j), request); } catch (...) { break; /// reached an invalid value } if (((from < to && j > to) || (from > to && j < to)) && j != to && j_s != to_s) { break; } newval.push_back(j_s); } } else { newval.push_back(type_.tidy(s, request)); } } std::swap(values, newval); } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsHandle.cc0000664000175000017500000001311215232567717020655 0ustar alastairalastair/* * (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 #include "eckit/config/Resource.h" #include "eckit/log/Bytes.h" #include "eckit/log/Log.h" #include "eckit/serialisation/HandleStream.h" #include "metkit/mars/MarsHandle.h" const unsigned long startCRC = 0xffffffffL; eckit::Reanimator MarsHandle::reanimator_; class MarsHandleStream : public eckit::HandleStream { MarsHandle& handle_; public: MarsHandleStream(MarsHandle& handle) : eckit::HandleStream(handle), handle_(handle) { handle_.streamMode_ = true; } ~MarsHandleStream() { handle_.streamMode_ = false; } }; void MarsHandle::encode(eckit::Stream& s) const { TCPHandle::encode(s); s << clientID_; s << doCRC_; } MarsHandle::MarsHandle(eckit::Stream& s) : TCPHandle(s), length_(0), total_(0), receiving_(false), streamMode_(false), doCRC_(false), crc_(startCRC) { s >> clientID_; if (s.endObjectFound()) { eckit::Log::info() << "Got old metkit without CRC" << std::endl; return; } s >> doCRC_; if (doCRC_) eckit::Log::info() << "Got new metkit with CRC" << std::endl; } MarsHandle::MarsHandle(const std::string& host, int port, unsigned long long clientID) : TCPHandle(host, port), clientID_(clientID), length_(0), total_(0), receiving_(false), streamMode_(false), doCRC_(false), crc_(startCRC) {} MarsHandle::~MarsHandle() {} std::string MarsHandle::title() const { std::ostringstream os; os << "Client["; os << eckit::net::TCPSocket::hostName(host_); os << ":" << port_ << "]"; return os.str(); } std::string MarsHandle::metricsTag() const { std::ostringstream os; os << "client@"; os << eckit::net::TCPSocket::hostName(host_); return os.str(); } eckit::Length MarsHandle::openForRead() { static long size = eckit::Resource("archiveSocketBufferSize", 0); connection_.bufferSize(size); eckit::TCPHandle::openForRead(); MarsHandleStream s(*this); s << clientID_; // Send info s << 'r'; // Send the request s >> length_; // Get the length back eckit::Log::status() << "Receiving " << eckit::Bytes(length_) << std::endl; total_ = 0; receiving_ = true; crc_ = startCRC; return length_; } void MarsHandle::openForWrite(const eckit::Length& length) { eckit::TCPHandle::openForWrite(length); MarsHandleStream s(*this); length_ = length; s << clientID_; // Send info s << 'w'; s << length_; // Send the length eckit::Log::status() << "Sending " << eckit::Bytes(length_) << std::endl; total_ = 0; receiving_ = false; } void MarsHandle::openForAppend(const eckit::Length&) { NOTIMP; } long MarsHandle::read(void* buffer, long length) { if (streamMode_) return TCPHandle::read(buffer, length); long long left = length_ - total_; if (left < length) { length = left; if (length == 0) return 0; } long len = TCPHandle::read(buffer, length); if (doCRC_) updateCRC(buffer, len); total_ += len; return len; } long MarsHandle::write(const void* buffer, long length) { long len = TCPHandle::write(buffer, length); total_ += len; return len; } void MarsHandle::close() { bool gotCRC = false; unsigned long version = 0; unsigned long crc = 0; if (length_ > 0 && total_ != length_) { TCPHandle::close(); eckit::Log::error() << "Recieved/Sent " << total_ << " bytes instead of " << length_ << std::endl; if (eckit::Exception::throwing()) { eckit::Log::error() << "A expection is already active" << std::endl; return; } throw eckit::ShortFile("Bad total in MarsHandle"); } if (receiving_) { crc_ ^= 0xffffffff; length_ = 0; // Try to read CRC try { MarsHandleStream s(*this); s >> version; unsigned long long c; s >> c; crc = ((unsigned long)(c)); gotCRC = true; } catch (std::exception& e) { eckit::Log::warning() << "Cannot read crc: " << e.what() << std::endl; } } if (doCRC_ && gotCRC) { eckit::Log::info() << "Local CRC " << crc_ << ", remote CRC " << crc << std::endl; ASSERT(version == 1); if (crc != crc_) { { FILE* p = popen("mail mab mar", "w"); if (p) { fprintf(p, "CRC error\n"); pclose(p); } } { eckit::PathName lock("~/locks/pause_if_crc_error"); while (lock.exists()) { eckit::Log::status() << "**** CRC ERROR ****" << std::endl; ::sleep(120); } } eckit::TCPHandle::close(); throw eckit::SeriousBug("Invalide checksum"); } } TCPHandle::close(); } eckit::Length MarsHandle::estimate() { return length_; } void MarsHandle::updateCRC(void* buffer, long length) {} const eckit::ClassSpec& MarsHandle::classSpec() { static const eckit::ClassSpec _classSpec = {&TCPHandle::classSpec(), "MarsHandle"}; return _classSpec; } metkit-1.19.2/src/metkit/mars/TypeFloat.cc0000664000175000017500000000537515232567717020562 0ustar alastairalastair/* * (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 "eckit/utils/Translator.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/TypeFloat.h" #include "metkit/mars/TypeToByList.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- TypeFloat::TypeFloat(const std::string& name, const eckit::Value& settings) : Type(name, settings) {} bool TypeFloat::expand(std::string& value, const MarsRequest&) const { bool dot = false; for (std::string::const_iterator j = value.begin(); j != value.end(); ++j) { switch (*j) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': break; case '.': dot = true; break; default: return false; } } // Strip leading zeros while (value.size() && value[0] == '0') { value = value.substr(1); } // Strip trailing zeros if (dot) { while (value.size() && value[value.size() - 1] == '0') { value = value.substr(0, value.size() - 1); } if (value.size() && value[value.size() - 1] == '.') { value = value.substr(0, value.size() - 1); } } if (value.empty()) { value = "0"; } return true; } void TypeFloat::print(std::ostream& out) const { out << "TypeFloat[name=" << name() << "]"; } static TypeBuilder type("float"); //---------------------------------------------------------------------------------------------------------------------- class TypeToByListFloat : public TypeFloat { public: TypeToByListFloat(const std::string& name, const eckit::Value& settings) : TypeFloat(name, settings) { toByList_ = std::make_unique>(*this, settings); multiple_ = true; } protected: void print(std::ostream& out) const override { out << "TypeToByListFloat[name=" << name() << "]"; } }; static TypeBuilder typeList("to-by-list-float"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeRange.h0000664000175000017500000000221415232567717020400 0ustar alastairalastair/* * (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. */ /// @file TypeRange.h /// @author Emanuele Danovaro /// @date February 2022 #pragma once #include "metkit/mars/Type.h" namespace metkit::mars { class StepRange; //---------------------------------------------------------------------------------------------------------------------- class TypeRange : public Type { public: // methods TypeRange(const std::string& name, const eckit::Value& settings); ~TypeRange() noexcept override = default; private: // methods void print(std::ostream& out) const override; bool expand(std::string& value, const MarsRequest& request) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeParam.h0000664000175000017500000000242115232567717020404 0ustar alastairalastair/* * (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. */ /// @file TypeParam.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @date April 2016 #pragma once #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeParam : public Type { public: // methods TypeParam(const std::string& name, const eckit::Value& settings); ~TypeParam() noexcept override = default; private: // methods eckit::ValueMap expandWith_; bool firstRule_; void print(std::ostream& out) const override; void reset() override; void pass2(MarsRequest& request) override; bool expand(std::string& value, const MarsRequest& request) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/Type.h0000664000175000017500000002004615232567717017426 0ustar alastairalastair/* * (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. */ /// @file Type.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @author Emanuele Danovaro /// @date April 2016 #pragma once #include #include #include #include #include #include #include "eckit/memory/Counted.h" #include "eckit/value/Value.h" #include "metkit/mars/MarsRequest.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- /// @brief abstract class - ContextRule subclasses are used to define a context. A MarsRequest matches a context, if it /// matches all its ContextRules class ContextRule { public: ContextRule(const std::string& k) : key_(k) {} virtual ~ContextRule() = default; const std::string& key() const { return key_; } virtual bool matches(MarsRequest req) const = 0; friend std::ostream& operator<<(std::ostream& s, const ContextRule& r) { r.print(s); return s; } protected: std::string key_; private: // methods virtual void print(std::ostream& out) const = 0; }; //---------------------------------------------------------------------------------------------------------------------- /// @brief A MarsRequest matches an Include ContextRule if at least one of the mars request values matches with the /// values associated with the Include rule class Include : public ContextRule { public: Include(const std::string& k, const std::set& vv) : ContextRule(k), vals_(vv) {} bool matches(MarsRequest req) const override { if (key_ == "_verb") { return (vals_.find(req.verb()) != vals_.end()); } if (!req.has(key_)) { return false; } for (const std::string& v : req.values(key_)) { if (vals_.find(v) != vals_.end()) { return true; } } return false; } private: // methods void print(std::ostream& out) const override { out << "Include[key=" << key_ << ",vals=[" << vals_ << "]]"; } private: std::set vals_; }; /// @brief A MarsRequest matches an Exclude ContextRule if none of the mars request values matches with the values /// associated with the Exclude rule class Exclude : public ContextRule { public: Exclude(const std::string& k, const std::set& vv) : ContextRule(k), vals_(vv) {} bool matches(MarsRequest req) const override { if (!req.has(key_)) { return false; } for (const std::string& v : req.values(key_)) { if (vals_.find(v) != vals_.end()) { return false; } } return true; } private: // methods void print(std::ostream& out) const override { out << "Exclude[key=" << key_ << ",vals=[" << vals_ << "]]"; } private: std::set vals_; }; /// @brief A MarsRequest matches an Undef ContextRule if the specified keyword is not defined in the mars request class Undef : public ContextRule { public: Undef(const std::string& k) : ContextRule(k) {} bool matches(MarsRequest req) const override { return !req.has(key_); } private: // methods void print(std::ostream& out) const override { out << "Undef[key=" << key_ << "]"; } }; /// @brief A MarsRequest matches an Undef ContextRule if the specified keyword is defined in the mars request class Def : public ContextRule { public: Def(const std::string& k) : ContextRule(k) {} bool matches(MarsRequest req) const override { return req.has(key_); } private: // methods void print(std::ostream& out) const override { out << "Def[key=" << key_ << "]"; } }; //---------------------------------------------------------------------------------------------------------------------- /// @brief a Context contains a list of ContextRule. A MarsRequest matches a context, if it matches all the ContextRules /// associated class Context { public: static std::unique_ptr parseContext(eckit::Value c); /// @note takes ownership of the rule void add(std::unique_ptr rule); size_t maxAxisIndex() const; bool matches(MarsRequest req) const; friend std::ostream& operator<<(std::ostream& s, const Context& x); private: // methods void print(std::ostream& out) const; private: std::vector> rules_; }; //---------------------------------------------------------------------------------------------------------------------- class ITypeToByList { public: virtual ~ITypeToByList() = default; virtual void expandRanges(std::vector& values, const MarsRequest& request) const = 0; }; //---------------------------------------------------------------------------------------------------------------------- class Type : public eckit::Counted { public: // methods Type(const std::string& name, const eckit::Value& settings); ~Type() noexcept override = default; virtual bool expand(std::string& value, const MarsRequest& request = {}) const; void expand(std::vector& values, const MarsRequest& request = {}) const; std::string tidy(const std::string& value, const MarsRequest& request = {}) const; virtual void setDefaults(MarsRequest& request); virtual void setInheritance(const std::vector& inheritance); virtual void check(const std::vector& values) const; virtual void clearDefaults(); virtual void reset(); virtual void pass2(MarsRequest& request); virtual void finalise(MarsRequest& request, bool strict); virtual const std::vector& flattenValues(const MarsRequest& request); virtual bool flatten() const; virtual bool multiple() const; virtual bool filter(const std::vector& filter, std::vector& values) const; virtual bool filter(const std::string& keyword, const std::vector& filter, std::vector& values) const; virtual bool matches(const std::vector& filter, const std::vector& values) const; const std::string& name() const; const std::string& category() const; friend std::ostream& operator<<(std::ostream& s, const Type& x); virtual size_t count(const std::vector& values) const; protected: // methods virtual bool hasGroups() const { return false; } virtual std::optional>> group(const std::string&) const { NOTIMP; } friend class MarsLanguage; void defaults(std::shared_ptr context, const std::vector& values); void set(std::shared_ptr context, const std::vector& values); void unset(std::shared_ptr context); protected: // members std::string name_; std::string category_; bool flatten_; bool multiple_; bool duplicates_; std::map, std::vector> defaults_; std::map, std::vector> sets_; std::set> unsets_; std::optional> inheritance_; std::unique_ptr toByList_; std::map&, std::vector&)>> filters_; private: // methods virtual void print(std::ostream& out) const = 0; void patchRequest(MarsRequest& request, const std::vector& values); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/Matcher.cc0000664000175000017500000001072415232567717020230 0ustar alastairalastair/* * (C) Copyright 2025- 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. */ /// @author Chris Bradley #include #include #include #include #include "eckit/exception/Exceptions.h" #include "eckit/utils/Overloaded.h" #include "eckit/utils/Regex.h" #include "eckit/utils/Tokenizer.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Matcher.h" namespace metkit::mars { // ---------------------------------------------------------------------------------------------------------------------- namespace { // helpers class MarsRequestAccessor : public RequestLike { public: explicit MarsRequestAccessor(const metkit::mars::MarsRequest& request) : request_(request) {} std::optional get(const std::string& keyword) const override { return request_.get(keyword); } private: const metkit::mars::MarsRequest& request_; }; inline std::string trim(const std::string& s) { auto start = std::find_if_not(s.begin(), s.end(), [](unsigned char ch) { return std::isspace(ch); }); auto end = std::find_if_not(s.rbegin(), s.rend(), [](unsigned char ch) { return std::isspace(ch); }).base(); if (start >= end) { return ""; // string is all whitespace } return std::string(start, end); } } // namespace // ---------------------------------------------------------------------------------------------------------------------- std::map parseMatchString(const std::string& expr) { if (expr.empty()) { throw eckit::BadValue("Empty match expression", Here()); } std::vector key_vals; eckit::Tokenizer(',')(expr, key_vals); eckit::Tokenizer equals('='); std::map out; for (const std::string& item : key_vals) { std::vector kv; equals(item, kv); if (kv.size() != 2) { throw eckit::BadValue("Invalid condition " + item + " in expression: " + expr, Here()); } const std::string& key = trim(kv[0]); const std::string& val = trim(kv[1]); auto [it, inserted] = out.try_emplace(key, val); if (!inserted) { throw eckit::BadValue("Duplicate key " + key + " in expression: " + expr, Here()); } } return out; } // ---------------------------------------------------------------------------------------------------------------------- Matcher::Matcher(std::map regexMap, Policy policy) : regexMap_{std::move(regexMap)}, policy_{policy} {} Matcher::Matcher(const std::string& expr, Policy policy) : regexMap_{parseMatchString(expr)}, policy_{policy} {} bool Matcher::match(const MarsRequest& request, MatchMissingPolicy matchOnMissing) const { return match(MarsRequestAccessor(request), matchOnMissing); } bool Matcher::match(const RequestLike& request, MatchMissingPolicy matchOnMissing) const { return std::all_of(regexMap_.begin(), regexMap_.end(), [&](const auto& kv) { const auto& keyword = kv.first; const auto& regex = kv.second; auto opt_values = request.get(keyword); if (!opt_values) return matchOnMissing == MatchOnMissing; auto pred = [®ex](const std::string& s) { return regex.match(s); }; // clang-format off return std::visit(eckit::Overloaded { [&](std::reference_wrapper value) { return pred(value.get()); }, [&](std::reference_wrapper> values) { const auto& vec = values.get(); if (policy_ == Policy::Any) { return std::any_of(vec.begin(), vec.end(), pred); } ASSERT(policy_ == Policy::All); return std::all_of(vec.begin(), vec.end(), pred); } }, *opt_values); // clang-format on }); } void Matcher::print(std::ostream& s) const { s << "{"; const char* sep = ""; for (const auto& [key, regex] : regexMap_) { s << sep << key << "=" << static_cast(regex); sep = ","; } s << "}"; } } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeLowercase.cc0000664000175000017500000000237615232567717021437 0ustar alastairalastair/* * (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 "eckit/utils/StringTools.h" #include "metkit/mars/TypeLowercase.h" #include "metkit/mars/TypesFactory.h" #include #include #include namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- TypeLowercase::TypeLowercase(const std::string& name, const eckit::Value& settings) : Type(name, settings) {} void TypeLowercase::print(std::ostream& out) const { out << "TypeLowercase[name=" << name_ << "]"; } bool TypeLowercase::expand(std::string& value, const MarsRequest&) const { value = eckit::StringTools::lower(value); return true; } static TypeBuilder type("lowercase"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsLocation.cc0000664000175000017500000000470615232567717021243 0ustar alastairalastair/* * (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 #include #include "eckit/config/Configuration.h" #include "eckit/config/LocalConfiguration.h" #include "eckit/config/Resource.h" #include "eckit/log/JSON.h" #include "eckit/log/Log.h" #include "eckit/utils/MD5.h" #include "eckit/utils/StringTools.h" #include "eckit/utils/Translator.h" #include "metkit/mars/MarsLocation.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- MarsLocation::MarsLocation(const MarsRequest& r, const std::string& hostname, int port) : request_(r), hostname_(hostname), port_(port) {} MarsLocation::MarsLocation(eckit::Stream& s) : request_(s) { s >> hostname_; s >> port_; NOTIMP; // FIXME: added this constructor just to get develop branches to compile } MarsLocation::MarsLocation(const eckit::Configuration& c) : request_(c.getString("request")), hostname_(c.getString("server")), port_(c.getInt("port")) {} MarsLocation::~MarsLocation() {} metkit::mars::MarsLocation::operator eckit::Value() const { eckit::Value dict = eckit::Value::makeMap(); dict["request"] = request_; dict["server"] = hostname_; dict["port"] = port_; return dict; } void MarsLocation::encode(eckit::Stream& s) const { s << request_; s << hostname_; s << port_; } const MarsRequest& MarsLocation::request() const { return request_; } std::string MarsLocation::hostname() const { return hostname_; } int MarsLocation::port() const { return port_; } void MarsLocation::print(std::ostream& s) const { s << request_ << ',' << std::endl; s << "hostname=" << hostname_ << ',' << std::endl; s << "port=" << port_ << std::endl; } void MarsLocation::json(eckit::JSON& s) const { s.startObject(); s << "request" << request_; s << "hostname" << hostname_; s << "port" << port_; s.endObject(); } //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit metkit-1.19.2/src/metkit/mars/MarsExpandContext.h0000664000175000017500000000175015232567717022115 0ustar alastairalastair/* * (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. */ /// @author Manuel Fuentes /// @author Baudouin Raoult /// @author Tiago Quintino /// @date Sep 96 #pragma once #include namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class MarsExpandContext { void info(std::ostream&) const {} public: friend std::ostream& operator<<(std::ostream& s, const MarsExpandContext& r) { return s; } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeMixed.h0000664000175000017500000000242615232567717020417 0ustar alastairalastair/* * (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. */ /// @file TypeMixed.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @date April 2016 #ifndef metkit_TypeMixed_H #define metkit_TypeMixed_H #include "metkit/mars/Type.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- class TypeMixed : public Type { public: // methods TypeMixed(const std::string& name, const eckit::Value& settings); ~TypeMixed() noexcept override; private: // methods void print(std::ostream& out) const override; bool expand(std::string& value, const MarsRequest& request) const override; std::vector, Type*>> types_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/TypesFactory.cc0000664000175000017500000000551615232567717021304 0ustar alastairalastair/* * (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 "eckit/exception/Exceptions.h" #include "eckit/thread/AutoLock.h" #include "eckit/value/Value.h" #include "metkit/mars/TypesFactory.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- TypesFactory::TypesFactory(const std::string& name) : name_(name) { TypesRegistry::instance().add(name, this); } TypesFactory::~TypesFactory() { TypesRegistry::instance().remove(name_); } Type* TypesRegistry::build(const std::string& keyword, const eckit::Value& settings) { std::string name; if (settings["type"].isList()) { name = "mixed"; } else { name = std::string(settings["type"]); } eckit::AutoLock lock(mutex_); std::map::const_iterator j = m_.find(name); if (j == m_.end()) { eckit::Log::error() << "No TypesFactory for [" << name << "]" << std::endl; eckit::Log::error() << "KeywordTypes are:" << std::endl; for (j = m_.begin(); j != m_.end(); ++j) eckit::Log::error() << " " << (*j).first << std::endl; throw eckit::SeriousBug(std::string("No TypesFactory called ") + name); } return (*j).second->make(keyword, settings); } void TypesRegistry::list(std::ostream& s) { eckit::AutoLock lock(mutex_); s << "["; bool first = true; std::map::const_iterator j = m_.begin(); while (j != m_.end()) { if (!first) s << ","; s << (j++)->first; first = false; } s << "]"; } Type* TypesFactory::build(const std::string& keyword, const eckit::Value& settings) { return TypesRegistry::instance().build(keyword, settings); } void TypesFactory::list(std::ostream& s) { TypesRegistry::instance().list(s); } TypesRegistry& TypesRegistry::instance() { static TypesRegistry instance; return instance; } void TypesRegistry::add(const std::string& name, TypesFactory* f) { eckit::AutoLock lock(mutex_); ASSERT(m_.find(name) == m_.end()); m_[name] = f; } void TypesRegistry::remove(const std::string& name) { eckit::AutoLock lock(mutex_); m_.erase(name); } //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit metkit-1.19.2/src/metkit/mars/ParamID.h0000664000175000017500000002217715232567717017771 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @author Tiago Quintino /// @author Simon Smart /// @date Febuary 2019 #ifndef metkit_ParamID_H #define metkit_ParamID_H #include #include #include #include #include "eckit/config/Resource.h" #include "eckit/filesystem/PathName.h" #include "eckit/log/Log.h" #include "eckit/runtime/Metrics.h" #include "eckit/types/Types.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/Param.h" namespace metkit { //---------------------------------------------------------------------------------------------------------------------- enum class NormalisationMode { Strict, // Exact match + wind conversion Loose, // Strict + partial match (dropping tables defined in param-matching.yaml) FullTableDropping }; //---------------------------------------------------------------------------------------------------------------------- class ParamID { public: // types struct WindFamily { Param u_; Param v_; Param vo_; Param d_; WindFamily(const std::string& u, const std::string& v, const std::string& vo, const std::string& d) : u_(u), v_(v), vo_(vo), d_(d) {} }; public: // methods template static void normalise(const REQUEST_T& r, std::vector& req, const AXIS_T& axis, bool& windConversion, NormalisationMode mode = ParamID::normalisationMode()); template [[deprecated]] static void normalise(const REQUEST_T& r, std::vector& req, const AXIS_T& axis, bool& windConversion, bool fullTableDropping, bool forceGRIBParamID = false); static const std::vector& getWindFamilies(); static const std::vector& getDropTables(); static NormalisationMode normalisationMode(); [[deprecated]] static bool fullTableDropping() { return normalisationMode() == NormalisationMode::FullTableDropping; } static const std::set& getMlParamsSingleLevel(); }; //---------------------------------------------------------------------------------------------------------------------- inline long replaceTable(size_t table, long paramid) { return (table * 1000 + paramid % 1000); } template void ParamID::normalise(const REQUEST_T& request, std::vector& req, const AXIS_T& axis, bool& windConversion, bool fullTableDropping, bool forceGRIBParamID) { normalise(request, req, axis, windConversion, forceGRIBParamID ? NormalisationMode::Strict : (fullTableDropping ? NormalisationMode::FullTableDropping : NormalisationMode::Loose)); } template void ParamID::normalise(const REQUEST_T& request, std::vector& req, const AXIS_T& axis, bool& windConversion, NormalisationMode mode) { static const bool useGRIBParamID = eckit::Resource("useGRIBParamID", false); bool strict = useGRIBParamID || mode == NormalisationMode::Strict; const std::vector& windFamilies(getWindFamilies()); std::vector> tableDropped; std::set inAxis; std::map inAxisParamID; std::set wind; for (typename AXIS_T::const_iterator j = axis.begin(); j != axis.end(); ++j) { Param p = *j; inAxis.emplace(p); inAxisParamID[p.paramId()] = p; } std::vector newreq; newreq.reserve(req.size()); for (const auto& r : req) { if (inAxis.find(r) != inAxis.end()) { // Perfect match - look no further newreq.push_back(r); } else { // r is normalised to ParamID long paramid = r.paramId(); const auto& ap = inAxisParamID.find(paramid); // ParamID representation matching - look no further if (ap != inAxisParamID.end()) { newreq.push_back(ap->second); } else { // Special case for U/V - exact match bool ok = false; for (const auto& wf : windFamilies) { if ((paramid == wf.u_.paramId() || paramid == wf.v_.paramId() || (!strict && (paramid == wf.u_.grib1value() || paramid == wf.v_.grib1value()))) && inAxis.find(wf.vo_) != inAxis.end() && inAxis.find(wf.d_) != inAxis.end()) { if (paramid == wf.u_.paramId() || (!strict && paramid == wf.u_.grib1value())) newreq.push_back(wf.u_); else newreq.push_back(wf.v_); wind.emplace(wf.vo_); wind.emplace(wf.d_); windConversion = true; ok = true; break; } } // Partial match (only it table has not been specified by user) if (!ok && !strict && r.table() == 0 && paramid < 1000) { const std::vector& dropTables = ParamID::getDropTables(); for (auto t : dropTables) { auto ap = inAxisParamID.find(replaceTable(t, paramid)); if (ap != inAxisParamID.end()) { // ParamID representation matching - look no further newreq.push_back(ap->second); ok = true; break; } } if (!ok) { // Special case for U/V - partial match for (const auto& wf : windFamilies) { if (paramid == wf.u_.paramId() || paramid == wf.v_.paramId()) { for (auto t : dropTables) { auto vo = inAxisParamID.find(replaceTable(t, wf.vo_.paramId())); auto d = inAxisParamID.find(replaceTable(t, wf.d_.paramId())); if (vo != inAxisParamID.end() && d != inAxisParamID.end()) { bool grib1 = vo->second.table() > 0; if (paramid == wf.u_.paramId()) newreq.push_back(grib1 ? Param(t, paramid) : Param(0, replaceTable(t, paramid))); else newreq.push_back(grib1 ? Param(t, paramid) : Param(0, replaceTable(t, paramid))); wind.emplace(vo->second); wind.emplace(d->second); windConversion = true; ok = true; break; } } if (ok) break; } } } if (mode == NormalisationMode::FullTableDropping && !ok) { // Backward compatibility - Partial match (drop completely table information) for (const auto& ap : inAxisParamID) { if (ap.first % 1000 == paramid % 1000) { newreq.push_back(ap.second); ok = true; tableDropped.push_back(std::make_pair(r, ap.second)); break; } } } } } } } req = newreq; for (const auto& w : wind) { bool exist = false; for (eckit::Ordinal i = 0; i < req.size(); i++) if (req[i] == w) { exist = true; break; } if (!exist) { req.push_back(w); } } if (tableDropped.size() > 0) { eckit::MetricsPrefix prefix("paramid_normalisation"); { std::ostringstream oss; oss << request; eckit::Metrics::set("user_request", oss.str()); } { std::ostringstream oss; oss << tableDropped; eckit::Metrics::set("params", oss.str()); } } } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/TypeRegex.cc0000664000175000017500000000351715232567717020563 0ustar alastairalastair/* * (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 "metkit/mars/TypeRegex.h" #include "eckit/parser/JSONParser.h" #include "eckit/utils/StringTools.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/TypesFactory.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- TypeRegex::TypeRegex(const std::string& name, const eckit::Value& settings) : Type(name, settings), uppercase_(false) { if (settings.contains("uppercase")) { uppercase_ = settings["uppercase"]; } eckit::Value r = settings["regex"]; if (r.isList()) { for (size_t i = 0; i < r.size(); ++i) { regex_.push_back(std::string(r[i])); } } else { regex_.push_back(std::string(r)); } } bool TypeRegex::expand(std::string& value, const MarsRequest&) const { for (std::vector::const_iterator j = regex_.begin(); j != regex_.end(); ++j) { if ((*j).match(value)) { if (uppercase_) { value = eckit::StringTools::upper(value); } return true; } } return false; } void TypeRegex::print(std::ostream& out) const { out << "TypeRegex[name=" << name_ << "]"; } static TypeBuilder type("regex"); //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit metkit-1.19.2/src/metkit/mars/TypeEnum.h0000664000175000017500000000415515232567717020256 0ustar alastairalastair/* * (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. */ /// @file TypeEnum.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @date April 2016 #pragma once #include #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeEnum : public Type { public: // methods TypeEnum(const std::string& name, const eckit::Value& settings); ~TypeEnum() noexcept override = default; private: // methods bool hasGroups() const override { return hasGroups_; } std::optional>> group( const std::string& value) const override; void print(std::ostream& out) const override; void reset() override; bool expand(std::string& value, const MarsRequest& request) const override; std::map::const_iterator find(const std::string& value) const; std::vector parseEnumValue(const eckit::Value& val, bool allowDuplicates = false) const; void addValue(const std::string& value, uint16_t idx, bool allowDuplicates) const; uint16_t parseValueNames(const eckit::Value& names, bool allowDuplicates) const; void readValuesFile() const; private: // members std::string valuesFile_; bool uppercase_ = false; mutable bool hasGroups_ = false; mutable std::vector>> groups_; mutable std::map values_; // map of acceptable values (included aliases) mutable std::once_flag readValues_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsHandle.h0000664000175000017500000000375115232567717020527 0ustar alastairalastair/* * (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. */ // File MarsHandle.h // Baudouin Raoult - ECMWF Oct 96 #ifndef MarsHandle_H #define MarsHandle_H #include "eckit/io/Length.h" #include "eckit/io/TCPHandle.h" #include "eckit/serialisation/Stream.h" class MarsHandle : public eckit::TCPHandle { public: // -- Contructors MarsHandle(const std::string& host, int port, unsigned long long); MarsHandle(eckit::Stream&); // -- Destructor ~MarsHandle(); // -- Overridden methods // From eckit::DataHandle virtual eckit::Length openForRead() override; virtual void openForWrite(const eckit::Length&) override; virtual void openForAppend(const eckit::Length&) override; virtual void close() override; virtual long read(void*, long) override; virtual long write(const void*, long) override; virtual eckit::Length estimate() override; virtual std::string title() const override; virtual std::string metricsTag() const override; virtual bool moveable() const override { return true; } // From Streamable virtual void encode(eckit::Stream&) const override; virtual const eckit::ReanimatorBase& reanimator() const override { return reanimator_; } // -- Class methods static const eckit::ClassSpec& classSpec(); private: // -- Members unsigned long long clientID_; eckit::Length length_; eckit::Length total_; bool receiving_; bool streamMode_; bool doCRC_; unsigned long crc_; // -- Methods void updateCRC(void*, long); // -- Class members static eckit::Reanimator reanimator_; friend class MarsHandleStream; }; #endif metkit-1.19.2/src/metkit/mars/RequestEnvironment.cc0000664000175000017500000000377615232567717022533 0ustar alastairalastair/* * (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 "metkit/mars/RequestEnvironment.h" #include #include #include #include #include #include "eckit/runtime/Main.h" #include "eckit/system/SystemInfo.h" #include "eckit/utils/Tokenizer.h" namespace metkit::mars { RequestEnvironment::RequestEnvironment(const RequestEnvironment& other) : env_(other.env_) {} void RequestEnvironment::update(const std::map& env) { // Split string on '/' and ignore empty splits const auto split = [](const std::string& str) -> std::vector { std::vector result; eckit::Tokenizer parse("/"); parse(str, result); return result; }; for (const auto& [k, v] : env) { env_->values(k, split(v)); } } void RequestEnvironment::initialize(const std::map& env) { RequestEnvironment& re = inst(); std::lock_guard lock(re.init_); re.env_.emplace("environ"); re.env_->setValue("host", eckit::Main::hostname()); re.env_->setValue("user", eckit::system::SystemInfo::instance().userName()); re.env_->setValue("pid", getpid()); re.env_->setValue("client", "unknown"); re.update(env); } RequestEnvironment& RequestEnvironment::inst() { static RequestEnvironment e; return e; } const RequestEnvironment& RequestEnvironment::instance() { auto& re = RequestEnvironment::inst(); std::lock_guard lock(re.init_); if (!re.env_) { throw eckit::SeriousBug("RequestEnvironment not initialized"); } return re; } } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypesFactory.h0000664000175000017500000000431315232567717021140 0ustar alastairalastair/* * (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. */ /// @file TypesFactory.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @date April 2016 #ifndef metkit_TypesFactory_H #define metkit_TypesFactory_H #include #include "eckit/memory/NonCopyable.h" #include "eckit/types/Types.h" namespace eckit { class Value; } namespace metkit { namespace mars { class Type; class TypesFactory; //---------------------------------------------------------------------------------------------------------------------- class TypesRegistry : private eckit::NonCopyable { eckit::Mutex mutex_; std::map m_; public: static TypesRegistry& instance(); void add(const std::string& name, TypesFactory* f); void remove(const std::string& name); Type* build(const std::string& keyword, const eckit::Value&); void list(std::ostream& s); }; /// A self-registering factory for producing TypesFactory instances class TypesFactory { public: virtual Type* make(const std::string& keyword, const eckit::Value& settings) const = 0; static Type* build(const std::string& keyword, const eckit::Value& settings); static void list(std::ostream& s); protected: TypesFactory(const std::string&); ~TypesFactory(); std::string name_; }; /// Templated specialisation of the self-registering factory, /// that does the self-registration, and the construction of each object. template class TypeBuilder : public TypesFactory { Type* make(const std::string& keyword, const eckit::Value& settings) const override { return new T(keyword, settings); } public: TypeBuilder(const std::string& name) : TypesFactory(name) {} }; //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/StepRange.h0000664000175000017500000000701215232567717020373 0ustar alastairalastair/* * (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. */ // File StepRange.h // Baudouin Raoult - ECMWF Dec 97 #ifndef metkit_StepRange_H #define metkit_StepRange_H #include #include "eckit/persist/Bless.h" #include "eckit/types/Time.h" #include "eckit/types/Types.h" namespace eckit { class DumpLoad; } namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class StepRange { public: // -- Exceptions // None // -- Contructors StepRange(const std::string&); explicit StepRange(eckit::Time from = eckit::Time(0), eckit::Time to = eckit::Time(0)) : from_(from / 3600.), to_(to / 3600.) { if (from != eckit::Time(0) && to == eckit::Time(0)) { to_ = from_; } } explicit StepRange(double from, double to = 0) : StepRange(eckit::Time(std::lround(from * 3600.), true), eckit::Time(std::lround(to * 3600.), true)) {} #include "metkit/mars/StepRange.b" // -- Destructor ~StepRange() {} // -- Convertors // None // -- Operators // None operator std::string() const; std::string toString(bool useDays = false, bool alwaysUseHours = false) const; StepRange& operator+=(const eckit::Time& step) { from_ += step / 3600.; to_ += step / 3600.; return *this; } StepRange& operator-=(const eckit::Time& step) { from_ -= step / 3600.; to_ -= step / 3600.; return *this; } bool operator==(const StepRange& other) const { return from_ == other.from_ && to_ == other.to_; } bool operator!=(const StepRange& other) const { return from_ != other.from_ || to_ != other.to_; } bool operator<(const StepRange& other) const { return (from_ != other.from_) ? (from_ < other.from_) : (to_ < other.to_); } bool operator<=(const StepRange& other) const { return (from_ != other.from_) ? (from_ <= other.from_) : (to_ <= other.to_); } bool operator>(const StepRange& other) const { return other < *this; } // -- Methods double from() const { return from_; } double to() const { return to_; } void dump(eckit::DumpLoad&) const; void load(eckit::DumpLoad&); // -- Overridden methods // None // -- Class members // None // -- Class methods // None // Uncomment for persistent, remove otherwise protected: // -- Members // None // -- Methods void print(std::ostream& s, bool useDays = false, bool alwaysUseHours = false) const; // -- Overridden methods // None // -- Class members // None // -- Class methods // None private: // No copy allowed // -- Members double from_; double to_; // -- Methods // None // -- Overridden methods // None // -- Class members // None // -- Class methods // None // -- Friends friend std::ostream& operator<<(std::ostream& s, const StepRange& p) { p.print(s); return s; } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars #endif metkit-1.19.2/src/metkit/mars/TypeRegex.h0000664000175000017500000000246615232567717020427 0ustar alastairalastair/* * (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. */ /// @file TypeRegex.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @date April 2016 #ifndef metkit_TypeRegex_H #define metkit_TypeRegex_H #include "eckit/utils/Regex.h" #include "metkit/mars/Type.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- class TypeRegex : public Type { public: // methods TypeRegex(const std::string& name, const eckit::Value& settings); ~TypeRegex() noexcept override = default; private: // methods void print(std::ostream& out) const override; bool expand(std::string& value, const MarsRequest& request) const override; std::vector regex_; bool uppercase_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/TypeMixed.cc0000664000175000017500000000512215232567717020551 0ustar alastairalastair/* * (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 "metkit/mars/TypeMixed.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/TypesFactory.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- TypeMixed::TypeMixed(const std::string& name, const eckit::Value& settings) : Type(name, settings) { eckit::Value types = settings["type"]; eckit::Value cfg; for (size_t i = 0; i < types.size(); ++i) { if (types[i].isString()) { cfg = settings; cfg["type"] = types[i]; Type* k = TypesFactory::build(name + "." + std::string(types[i]), cfg); k->attach(); types_.emplace_back(nullptr, k); } else { // it is a subtype, potentially with a Context cfg = types[i]; eckit::Value type = cfg["type"]; std::unique_ptr c; if (cfg.contains("context")) { c = Context::parseContext(cfg["context"]); } Type* k = TypesFactory::build(name + "." + std::to_string(i) + "." + std::string(type), cfg); k->attach(); types_.emplace_back(std::move(c), k); } } } TypeMixed::~TypeMixed() noexcept { for (auto it = types_.begin(); it != types_.end(); it++) { (*it).second->detach(); } } void TypeMixed::print(std::ostream& out) const { out << "TypeMixed[name=" << name_; for (auto it = types_.begin(); it != types_.end(); it++) { out << "," << *((*it).second); } out << "]"; } bool TypeMixed::expand(std::string& value, const MarsRequest& request) const { for (auto it = types_.begin(); it != types_.end(); it++) { if ((*it).first == nullptr || (*it).first->matches(request)) { std::string tmp = value; if ((*it).second->expand(tmp, request)) { value = tmp; return true; } } } return false; } static TypeBuilder type("mixed"); //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit metkit-1.19.2/src/metkit/mars/ClientTask.h0000664000175000017500000001171715232567717020553 0ustar alastairalastair/* * (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. */ // File ClientTask.h // Baudouin Raoult - ECMWF Oct 96 #ifndef metkit_ClientTask_H #define metkit_ClientTask_H #include // #include "eckit/bases/Watcher.h" #include "eckit/exception/Exceptions.h" #include "eckit/io/DataHandle.h" // #include "eckit/log/UserChannel.h" #include "eckit/transaction/TxnEvent.h" #include "metkit/mars/MarsRequest.h" namespace metkit { namespace mars { class ClientTask { public: // -- Contructors ClientTask(const MarsRequest&, const MarsRequest&, const std::string& name, int port, unsigned long long id = 0); // ClientTask(eckit::Stream&); // -- Destructor ~ClientTask(); // -- Convertors // None // -- Operators // None // -- Methods // void dump(std::ostream&) const; // void json(eckit::JSON&) const; // MarsInfo info() const; // const MarsID& id() const { return transactionID(); } // const MarsRequest& request() const { return request_; } // const MarsRequest& environ() const { return environ_; } // eckit::Length transferData(const eckit::PathName&); // eckit::PathName transferData(); // /// @returns eckit::DataHandle with the data that will be received, but does not give ownership of the // eckit::DataHandle eckit::DataHandle& getDataHandle(); // void sendData(eckit::DataHandle&); // void sendHandle(eckit::DataHandle&); // void acknowledge(eckit::Stream& s) { send(s,'a'); } // void patch(); // bool authenticated() const; // eckit::DataHandle& dataHandle() { return *handle_; } // // Report // void success(); // void failure(const std::string&); // void retry(const std::string&); // void infoMsg(const std::string&); // void warningMsg(const std::string&); // void errorMsg(const std::string&); // void notifyClient(const std::string&); // void notifyStart(); // void queueTime(); // void ping(); // void sendCost(); // void sendChecksum(const std::string&); // // Queuing // double startingPriority() const { return startingPriority_;} // void startingPriority(double p) { startingPriority_ = p;} // // Cost // Cost& cost() { return cost_; } // const Cost& cost() const { return cost_; } // void costChanged(); // // Called by PipeProcess // void send(eckit::Stream&); // void reply(eckit::Stream&); // bool error(std::exception&,int); // void done(); // Logging // Mars tree // void push(const std::string&,const std::string&); // void pop(); // void reset(); // const eckit::StringList& treeNames() const { return treeNames_; } // const eckit::StringList& treeValues() const { return treeValues_; } // // For the metkit void send(eckit::Stream&) const; char receive(eckit::Stream&) const; // -- Overridden methods // From Streamble // virtual void encode(eckit::Stream&) const override; // virtual const eckit::ReanimatorBase& reanimator() const { return reanimator_; } // From watcher // virtual void watch(); // -- Class methods // static const eckit::ClassSpec& classSpec() { return classSpec_;} // static void recover(TxnRecoverer&); // static void find(TxnFinder&); // static std::string commandName(); // None protected: // -- Members MarsRequest request_; MarsRequest environ_; private: // -- Members // unsigned long long txnID_; // unused unsigned long long metkitID_; int port_; std::string host_; std::unique_ptr handle_; std::string checksum_; // Not sent over streams // double startingPriority_; // Cost cost_; // time_t lastPing_; // time_t queueTime_; // eckit::StringList treeValues_; // eckit::StringList treeNames_; // bool authenticated_; // -- Methods // void print(std::ostream&) const; // void send(eckit::Stream&,char) const; // -- Overridden methods // None // -- Class members // static eckit::ClassSpec classSpec_; // static eckit::Reanimator reanimator_; // -- Class methods // None // friend std::ostream& operator<<(std::ostream& s, const ClientTask& r) // { r.print(s); return s; } // friend eckit::JSON& operator<<(eckit::JSON& s, const ClientTask& r) // { r.json(s); return s; } }; } // namespace mars } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/DHSProtocol.cc0000664000175000017500000004367715232567717021022 0ustar alastairalastair/* * (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. */ // File DHSProtocol.cc // Baudouin Raoult - (c) ECMWF Feb 12 #include "metkit/mars/DHSProtocol.h" #include "eckit/config/Configuration.h" #include "eckit/config/Resource.h" #include "eckit/log/Log.h" #include "eckit/net/Endpoint.h" #include "eckit/net/IPAddress.h" #include "eckit/net/TCPClient.h" #include "eckit/net/TCPServer.h" #include "eckit/net/TCPSocket.h" #include "eckit/net/TCPStream.h" #include "eckit/utils/StringTools.h" #include "eckit/utils/Translator.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/ClientTask.h" #include "metkit/mars/RequestEnvironment.h" using namespace eckit; using eckit::net::Endpoint; namespace metkit::mars { static Reanimator dhsProtocolReanimator; // --------------------------------------------------------------------------------------------------------------------- namespace { constexpr const int DEFAULT_CALLBACK_PROXY_PORT = 9707; // Normally we would let the Endpoint constructor do this unpacking itself, but we want to set a default port Endpoint unpackHostPort(const std::string& hoststr) { auto bits = StringTools::split(":", hoststr); ASSERT(!bits.empty() && bits.size() < 3); int port = DEFAULT_CALLBACK_PROXY_PORT; if (bits.size() == 2) { port = Translator()(bits[1]); } return {bits[0], port}; } Endpoint selectProxyHost(const std::vector proxies) { return unpackHostPort(proxies[std::rand() % proxies.size()]); } Endpoint selectProxyHost(const Configuration& config) { if (config.has("proxyHost")) { return unpackHostPort(config.getString("proxyHost")); } if (config.has("proxyHosts")) { return selectProxyHost(config.getStringVector("proxyHosts")); } throw UserError("Neither proxyHosts nor proxyHost specified in configuration"); } } // namespace // Implement the default callback behaviour. Client opens a socket that can be connected to by // the server or data mover class SimpleCallback : public BaseCallbackConnection { public: SimpleCallback() : callback_(Resource("$MARS_DHS_LOCALPORT", 0)), callbackEndpoint_(computeEndpoint()) { LOG_DEBUG_LIB(LibMetkit) << "Simple callback. local port=" << callback_.localPort() << " host=" << callbackEndpoint_.host() << " port=" << callbackEndpoint_.port() << std::endl; } explicit SimpleCallback(const Configuration&) : SimpleCallback() {} explicit SimpleCallback(Stream&) : SimpleCallback() {} private: const Endpoint& endpoint() const override { return callbackEndpoint_; } net::TCPSocket& connect() override { return callback_.accept(); } net::EphemeralTCPServer callback_; Endpoint callbackEndpoint_; Endpoint computeEndpoint() const { static std::string callbackHost = Resource("$MARS_DHS_CALLBACK_HOST", ""); static int callbackPort = Resource("$MARS_DHS_CALLBACK_PORT", 0); if (callbackHost.empty()) { return Endpoint{net::IPAddress::hostAddress(callback_.localHost()).asString(), callback_.localPort()}; } if (callbackPort == 0) { return Endpoint{callbackHost, callback_.localPort()}; } return Endpoint{callbackHost, callbackPort}; } public: static const ClassSpec& classSpec() { static ClassSpec spec = {&BaseCallbackConnection::classSpec(), "SimpleCallback"}; return spec; } protected: const ReanimatorBase& reanimator() const override { return reanimator_; } void encode(Stream&) const override {} static Reanimator reanimator_; }; Reanimator SimpleCallback::reanimator_; // --------------------------------------------------------------------------------------------------------------------- // Implement a callback via the callback proxy. First we open a connection to the proxy. This then returns to us // the host/port that it has made available for the server/mover to connect to. It then proxies the connection // to our host/port class ProxyCallback : public BaseCallbackConnection { public: explicit ProxyCallback(const Endpoint& proxyhost) : control_(net::TCPClient().connect(proxyhost)), proxyHost_(proxyhost) { LOG_DEBUG_LIB(LibMetkit) << "Proxy callback. proxyhost=" << proxyhost.host() << " proxyport=" << proxyhost.port() << std::endl; std::string localAddr = net::IPAddress::hostAddress(callback_.localHost()).asString(); int localPort = callback_.localPort(); bool passive = false; control_ << localAddr; control_ << localPort; control_ << passive; remoteAddr_ = Endpoint(control_); } explicit ProxyCallback(const Configuration& config) : ProxyCallback(selectProxyHost(config)) {} explicit ProxyCallback(Stream& s) : ProxyCallback(Endpoint(s)) {} private: const Endpoint& endpoint() const override { return remoteAddr_; } net::TCPSocket& connect() override { // FIXME: Check that the callback connection is still alive... return callback_.accept(); } net::TCPStream control_; net::EphemeralTCPServer callback_; Endpoint proxyHost_; Endpoint remoteAddr_; public: static const ClassSpec& classSpec() { static ClassSpec spec = {&BaseCallbackConnection::classSpec(), "ProxyCallback"}; return spec; } protected: const ReanimatorBase& reanimator() const override { return reanimator_; } void encode(Stream& s) const override { s << proxyHost_; } static Reanimator reanimator_; }; Reanimator ProxyCallback::reanimator_; // --------------------------------------------------------------------------------------------------------------------- // Implement a callback via the callback proxy using passive mode. First we open a connection to the proxy. // This then returns to us the host/port that it has made available for the server/mover to connect to. // We then need to open connections to the proxy for the callbacks to connect to. class PassiveProxyCallback : public BaseCallbackConnection { public: explicit PassiveProxyCallback(const Endpoint& proxyhost, bool useProxyHostAsCallback = true) : control_(net::TCPClient().connect(proxyhost)), proxyHost_(proxyhost) { LOG_DEBUG_LIB(LibMetkit) << "Passive proxy callback. proxyhost=" << proxyhost << std::endl; std::string localAddr = ""; int localPort = -1; bool passive = true; control_ << localAddr; control_ << localPort; control_ << passive; remoteAddr_ = Endpoint(control_); LOG_DEBUG_LIB(LibMetkit) << "Remote address. host=" << remoteAddr_ << std::endl; passiveAddr_ = Endpoint(control_); if (useProxyHostAsCallback) { passiveAddr_ = Endpoint(proxyhost.host(), passiveAddr_.port()); } LOG_DEBUG_LIB(LibMetkit) << "Passive address. host=" << passiveAddr_ << std::endl; control_ >> passiveCheck_; LOG_DEBUG_LIB(LibMetkit) << "Passive address. host=" << passiveAddr_ << " check=" << passiveCheck_ << std::endl; } explicit PassiveProxyCallback(const Configuration& config) : PassiveProxyCallback(selectProxyHost(config), config.getBool("useProxyHostAsCallback", true)) {} explicit PassiveProxyCallback(Stream& s) : PassiveProxyCallback(Endpoint(s)) {} private: const Endpoint& endpoint() const override { return remoteAddr_; } net::TCPSocket& connect() override { // FIXME: Check that the callback connection is still alive... ASSERT(!socket_.isConnected()); socket_ = net::TCPClient().connect(passiveAddr_); net::InstantTCPStream s(socket_); s << passiveCheck_; return socket_; } net::TCPStream control_; net::TCPSocket socket_; Endpoint proxyHost_; Endpoint remoteAddr_; Endpoint passiveAddr_; unsigned long long passiveCheck_; public: static const ClassSpec& classSpec() { static ClassSpec spec = {&BaseCallbackConnection::classSpec(), "PassiveProxyCallback"}; return spec; } protected: const ReanimatorBase& reanimator() const override { return reanimator_; } void encode(Stream& s) const override { s << proxyHost_; } static Reanimator reanimator_; }; Reanimator PassiveProxyCallback::reanimator_; // --------------------------------------------------------------------------------------------------------------------- const ClassSpec& BaseCallbackConnection::classSpec() { static ClassSpec spec = {&Streamable::classSpec(), "BaseCallbackConnection"}; return spec; } BaseCallbackConnection* BaseCallbackConnection::build(const Configuration& config, const std::string& host) { if (config.has("proxyHost") || config.has("proxyHosts") || (config.getBool("passiveProxy", true) && config.getBool("useHostAsProxy", false))) { if (config.getBool("passiveProxy", true)) { if (config.getBool("useHostAsProxy", false)) { return new PassiveProxyCallback{unpackHostPort(host)}; } return new PassiveProxyCallback{config}; } return new ProxyCallback{config}; } static bool passiveProxy = Resource("$MARS_DHS_PASSIVE_PROXY", true); static std::vector proxyHosts = Resource>("$MARS_DHS_CALLBACK_PROXY_HOST", {}); if (!proxyHosts.empty()) { Endpoint proxyHost = selectProxyHost(proxyHosts); if (passiveProxy) { return new PassiveProxyCallback(proxyHost); } else { return new ProxyCallback(proxyHost); } } return new SimpleCallback{config}; } // --------------------------------------------------------------------------------------------------------------------- DHSProtocol::DHSProtocol(const std::string& name, const std::string& host, int port, bool forwardMessages) : callback_(new SimpleCallback{}), name_(name), host_(host), port_(port), done_(false), error_(false), sending_(false), forward_(forwardMessages) {} DHSProtocol::DHSProtocol(const Configuration& params) : BaseProtocol(params), name_(params.getString("name")), port_(params.getInt("port", 9000)), done_(false), error_(false), sending_(false), forward_(false) { if (params.has("hosts")) { std::vector hosts = params.getStringVector("hosts"); host_ = hosts.at(std::rand() % hosts.size()); } else { ASSERT(params.has("host")); host_ = params.getString("host"); } env_ = RequestEnvironment::instance().request(); callback_.reset(BaseCallbackConnection::build(params, host_)); } DHSProtocol::DHSProtocol(const Configuration& params, const std::map& env) : DHSProtocol(params) { RequestEnvironment requestEnv{RequestEnvironment::instance()}; requestEnv.update(env); env_ = requestEnv.request(); } DHSProtocol::DHSProtocol(Stream& s) : BaseProtocol(s), callback_(Reanimator::reanimate(s)) { s >> name_; s >> host_; s >> port_; s >> done_; s >> error_; s >> sending_; s >> forward_; env_ = MarsRequest(s); } /// @attention this dtor may throw in cleanup()! DHSProtocol::~DHSProtocol() { done_ = true; cleanup(); } const ReanimatorBase& DHSProtocol::reanimator() const { return dhsProtocolReanimator; } const ClassSpec& DHSProtocol::classSpec() { static ClassSpec spec = {&BaseProtocol::classSpec(), "DHSProtocol"}; return spec; } Length DHSProtocol::retrieve(const MarsRequest& request) { Endpoint callbackEndpoint = callback_->endpoint(); LOG_DEBUG_LIB(LibMetkit) << "DHSProtocol: call back on " << callbackEndpoint << std::endl; task_.reset(new ClientTask(request, env_, callbackEndpoint.host(), callbackEndpoint.port())); net::TCPStream s(net::TCPClient().connect(host_, port_)); task_->send(s); ASSERT(task_->receive(s) == 'a'); // Acknoledgement Length result = 0; while (wait(result)) {} LOG_DEBUG_LIB(LibMetkit) << "DHSProtocol::retrieve " << result << std::endl; return result; } void DHSProtocol::archive(const MarsRequest& request, const Length& size) { Endpoint callbackEndpoint = callback_->endpoint(); LOG_DEBUG_LIB(LibMetkit) << "DHSProtocol::archive " << size << std::endl; LOG_DEBUG_LIB(LibMetkit) << "DHSProtocol: call back on " << callbackEndpoint << std::endl; task_.reset(new ClientTask(request, env_, callbackEndpoint.host(), callbackEndpoint.port())); net::TCPStream s(net::TCPClient().connect(host_, port_)); task_->send(s); ASSERT(task_->receive(s) == 'a'); // Acknoledgement Length result = size; while (wait(result)) {} LOG_DEBUG_LIB(LibMetkit) << "DHSProtocol: archive completed." << std::endl; } void DHSProtocol::cleanup() { if (socket_.isConnected()) { if (sending_) { unsigned long version = 1; unsigned long long crc = 0; try { net::InstantTCPStream s(socket_); s << version; s << crc; } catch (std::exception& e) { Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl; Log::error() << "** Exception is ignored" << std::endl; } } socket_.close(); } sending_ = false; if (!done_) { Length result = 0; while (wait(result)) { ; } } if (error_) { error_ = false; throw UserError(std::string("Error from [") + name_ + "]: " + msg_); } } void DHSProtocol::print(std::ostream& s) const { s << "DHSProtocol[" << name_ << "]"; } void DHSProtocol::encode(Stream& s) const { BaseProtocol::encode(s); callback_->encode(s); s << name_; s << host_; s << port_; s << done_; s << error_; s << sending_; s << forward_; s << env_; } long DHSProtocol::read(void* buffer, long len) { return socket_.read(buffer, len); } long DHSProtocol::write(const void* buffer, long len) { return socket_.write(buffer, len); } bool DHSProtocol::wait(Length& size) { for (;;) { if (socket_.isConnected()) socket_.close(); socket_ = callback_->connect(); net::InstantTCPStream s(socket_); char code = task_->receive(s); LOG_DEBUG_LIB(LibMetkit) << "DHSProtocol: code [" << code << "]" << std::endl; std::string msg; long long bytes; switch (code) { /* OK */ case 'o': done_ = true; return false; /* read source */ case 'r': bytes = size; LOG_DEBUG_LIB(LibMetkit) << "DHSProtocol:r [" << bytes << "]" << std::endl; s << bytes; sending_ = true; return false; /* get */ case 'h': NOTIMP; case 'w': s >> bytes; LOG_DEBUG_LIB(LibMetkit) << "DHSProtocol:w " << bytes << std::endl; size = bytes; return false; case 'm': NOTIMP; case 'X': NOTIMP; case 'e': s >> msg_; Log::error() << msg_ << " [" << name_ << "]" << std::endl; error_ = true; done_ = true; return false; case 'y': /* retry */ NOTIMP; case 'I': /* info */ s >> msg; Log::info() << msg << " [" << name_ << "]" << std::endl; if (forward_) { Log::userInfo() << msg << " [" << name_ << "]" << std::endl; } break; case 'W': /* warning */ s >> msg; Log::warning() << msg << " [" << name_ << "]" << std::endl; if (forward_) { Log::userWarning() << msg << " [" << name_ << "]" << std::endl; } break; case 'D': /* debug */ s >> msg; LOG_DEBUG_LIB(LibMetkit) << msg << " [" << name_ << "]" << std::endl; if (forward_) { Log::userInfo() << msg << " [" << name_ << "]" << std::endl; } break; case 'E': /* error */ s >> msg; Log::error() << msg << " [" << name_ << "]" << std::endl; if (forward_) { Log::userError() << msg << " [" << name_ << "]" << std::endl; } break; case 'N': /* notification */ NOTIMP; case 'p': /* ping */ s << 'p'; break; case 's': /* statistics */ { int n; s >> n; std::string key, value; for (int i = 0; i < n; i++) { s >> key >> value; stats_[key] = value; Log::info() << "DHSProtocol:s " << key << "=" << value << std::endl; } } break; case 'S': /* notification start */ NOTIMP; case 't': /* new timeout */ NOTIMP; default: throw Exception(std::string("Unknown code [") + code + "]"); } } } static ProtocolBuilder builder("dhsbase"); } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/Parameter.h0000664000175000017500000000414215232567717020424 0ustar alastairalastair/* * (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. */ /// @author Manuel Fuentes /// @author Baudouin Raoult /// @author Tiago Quintino /// @date Sep 96 #ifndef metkit_Parameter_H #define metkit_Parameter_H #include "eckit/types/Date.h" #include "eckit/types/Double.h" #include "eckit/types/Time.h" #include "eckit/utils/Translator.h" #include "eckit/value/Value.h" namespace eckit { class JSON; class MD5; } // namespace eckit namespace metkit { namespace mars { class Type; class MarsRequest; //---------------------------------------------------------------------------------------------------------------------- class Parameter { public: // methods Parameter(); ~Parameter(); Parameter(const std::vector& values, Type* = 0); Parameter(const Parameter&); Parameter& operator=(const Parameter&); bool operator<(const Parameter&) const; const std::vector& values() const { return values_; } void values(const std::vector& values); bool filter(const std::vector& filter); bool filter(const std::string& keyword, const std::vector& filter); bool matches(const std::vector& matches) const; void merge(const Parameter& p); Type& type() const { return *type_; } const std::string& name() const; size_t count() const; private: // methods void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const Parameter& p) { p.print(s); return s; } private: // members Type* type_; std::vector values_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit #endif metkit-1.19.2/src/metkit/mars/MarsParser.cc0000664000175000017500000001111515232567717020717 0ustar alastairalastair/* * (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. */ /// @file MarsParser.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @date Jun 2012 #include "metkit/mars/MarsParser.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- std::string MarsParser::parseString(char quote) { consume(quote); std::string s; for (;;) { char c = next(true); if (c == '\\') { c = next(true); switch (c) { case '"': s += '"'; break; case '\'': s += '\''; break; case '\\': s += '\\'; break; case '/': s += '/'; break; case 'b': s += '\b'; break; case 'f': s += '\f'; break; case 'n': s += '\n'; break; case 'r': s += '\r'; break; case 't': s += '\t'; break; case 'u': throw StreamParser::Error(std::string("JSONTokenizer::parseString \\uXXXX format not supported")); default: throw StreamParser::Error(std::string("JSONTokenizer::parseString invalid \\ char '") + c + "'"); } } else { if (c == quote) { return s; // void(s); } s += c; } } } static bool inindent(char c) { return isalnum(c) || c == '_' || c == ':' || c == '-' || c == '.' || c == '@'; } void MarsParser::quoted(std::ostream& out, const std::string& value) { char quote = 0; for (std::string::const_iterator j = value.begin(); j != value.end(); ++j) { if (!inindent(*j)) { quote = '"'; break; } } if (quote) { out << quote << value << quote; } else { out << value; } } std::string MarsParser::parseIndents() { std::ostringstream oss; oss << parseIndent(); for (;;) { char c = peek(true); while (c == ' ') { next(true); c = peek(true); } if (!inindent(c)) { break; } oss << " " << parseIndent(); } return oss.str(); } std::string MarsParser::parseValue() { char c = peek(); if (c == '\"' || c == '\'') { return parseString(c); } return parseIndents(); } std::vector MarsParser::parseValues() { std::vector v(1, parseValue()); char c = peek(); while (c == '/') { consume('/'); v.push_back(parseValue()); c = peek(); } return v; } std::string MarsParser::parseIndent() { char c = peek(); std::string s; while (inindent(c)) { s += next(true); c = peek(true); } return s; } std::string MarsParser::parseVerb() { char c = peek(); if (!isalpha(c) && c != '_') { throw StreamParser::Error(std::string("MarsParser::parseVerb invalid char '") + c + "'", line_ + 1); } return parseIndent(); } MarsParsedRequest MarsParser::parseRequest() { MarsParsedRequest r(parseVerb(), line_ + 1); char c = peek(); while (c == ',') { consume(','); std::string key = parseIndents(); consume('='); r.values(key, parseValues()); c = peek(); } if (c == '.') { consume('.'); } return r; } MarsParser::MarsParser(std::istream& in) : StreamParser(in, true, "#") {} std::vector MarsParser::parse() { std::vector result; while (peek() != 0) { result.push_back(parseRequest()); } return result; } void MarsParser::parse(MarsParserCallback& cb) { while (peek() != 0) { auto r = parseRequest(); cb(r); } } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsRequest.cc0000664000175000017500000003702315232567717021121 0ustar alastairalastair/* * (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 "eckit/log/JSON.h" #include "eckit/log/Log.h" #include "eckit/types/Types.h" #include "eckit/utils/MD5.h" #include "eckit/utils/StringTools.h" #include "eckit/message/Message.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsParser.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/ParamID.h" #include "metkit/mars/TypeAny.h" namespace metkit { namespace mars { //---------------------------------------------------------------------------------------------------------------------- MarsRequest::MarsRequest() {} MarsRequest::MarsRequest(const std::string& s) : verb_(s) { ASSERT(s.find(',') == std::string::npos); } MarsRequest::MarsRequest(const std::string& s, const std::map& values) : verb_(s) { for (auto j = values.begin(); j != values.end(); ++j) { const std::string& param = (*j).first; const std::string& value = (*j).second; params_.push_back(Parameter(std::vector(1, value), new TypeAny(param))); } } MarsRequest::MarsRequest(const std::string& s, const eckit::Value& values) : verb_(s) { eckit::ValueMap m = values; for (auto j = m.begin(); j != m.end(); ++j) { const std::string& param = (*j).first; const eckit::Value& value = (*j).second; if (value.isList()) { std::vector vals; eckit::fromValue(vals, value); params_.push_back(Parameter(vals, new TypeAny(param))); } else { params_.push_back(Parameter(std::vector(1, value), new TypeAny(param))); } } } MarsRequest::MarsRequest(const eckit::message::Message& message) : verb_("message") { eckit::message::StringSetter setter(*this); message.getMetadata(setter); } MarsRequest::MarsRequest(eckit::Stream& s, bool lowercase) { int size; s >> verb_; if (lowercase) verb_ = eckit::StringTools::lower(verb_); s >> size; for (int i = 0; i < size; i++) { std::string param; int count; s >> param; if (lowercase) param = eckit::StringTools::lower(param); s >> count; std::vector v; v.reserve(count); for (int k = 0; k < count; k++) { std::string value; s >> value; v.push_back(value); } params_.push_back(Parameter(v, new TypeAny(param))); } } void MarsRequest::encode(eckit::Stream& s) const { s << verb_; int size = params_.size(); s << size; for (std::list::const_iterator i = params_.begin(); i != params_.end(); ++i) { s << (*i).name(); const std::vector& v = (*i).values(); int size = v.size(); // For backward compatibility s << size; for (std::vector::const_iterator k = v.begin(); k != v.end(); ++k) { s << *k; } } } bool MarsRequest::empty() const { return params_.empty(); } void MarsRequest::print(std::ostream& s) const { dump(s, "", "", true); } void MarsRequest::dump(std::ostream& s, const char* cr, const char* tab, bool verb) const { std::list::const_iterator begin = params_.begin(); std::list::const_iterator end = params_.end(); if (verb) { s << verb_ << ','; } std::string separator = ""; if (begin != end) { s << separator << cr << tab; separator = ","; int a = 0; for (std::list::const_iterator i = begin; i != end; ++i) { if (a++) { s << ','; s << cr << tab; } int b = 0; s << (*i).name() // << "." << (*i).second.type() << "="; const std::vector& v = (*i).values(); for (std::vector::const_iterator k = v.begin(); k != v.end(); ++k) { if (b++) { s << '/'; } MarsParser::quoted(s, *k); } // s << " {" << (*i).type().category() << "}"; } } s << cr << cr; } void MarsRequest::json(eckit::JSON& s, bool array) const { s.startObject(); // s << "_verb" << verb_; std::list::const_iterator begin = params_.begin(); std::list::const_iterator end = params_.end(); for (std::list::const_iterator i = begin; i != end; ++i) { s << (*i).name(); const std::vector& v = (*i).values(); bool list = v.size() != 1 || (array && (*i).type().multiple()); if (list) { s.startList(); } for (std::vector::const_iterator k = v.begin(); k != v.end(); ++k) { s << (*k); } if (list) { s.endList(); } } s.endObject(); } void MarsRequest::md5(eckit::MD5& md5) const { std::ostringstream oss; oss << *this; md5.add(oss.str()); } void MarsRequest::unsetValues(const std::string& name) { std::list::iterator i = find(name); if (i != params_.end()) { params_.erase(i); } } void MarsRequest::setValuesTyped(Type* type, const std::vector& values) { std::list::iterator i = find(type->name()); if (i != params_.end()) { (*i) = Parameter(values, type); } else { params_.push_back(Parameter(values, type)); } } bool MarsRequest::filter(const MarsRequest& filter) { for (std::list::iterator i = params_.begin(); i != params_.end(); ++i) { if ((*i).name() == "date") { std::list::const_iterator j = filter.find("day"); if (j != filter.params_.end()) { if (!(*i).filter("day", (*j).values())) { return false; } } } std::list::const_iterator j = filter.find((*i).name()); if (j == filter.params_.end()) { continue; } if (!(*i).filter((*j).values())) { return false; } } return true; } bool MarsRequest::matches(const MarsRequest& matches) const { std::vector params = matches.params(); for (std::vector::const_iterator j = params.begin(); j != params.end(); ++j) { std::list::const_iterator k = find(*j); if (k == params_.end()) { return false; } if (!(*k).matches(matches.values(*j))) { return false; } } return true; } void MarsRequest::values(const std::string& name, const std::vector& v) { std::list::iterator i = find(name); if (i != params_.end()) { (*i).values(v); } else { params_.push_back(Parameter(v, new TypeAny(name))); } } size_t MarsRequest::countValues(const std::string& name) const { std::list::const_iterator i = find(name); if (i != params_.end()) { return (*i).values().size(); } return 0; } bool MarsRequest::has(const std::string& name) const { return find(name) != params_.end(); } bool MarsRequest::is(const std::string& name, const std::string& value) const { std::list::const_iterator i = find(name); if (i != params_.end()) { const std::vector& v = (*i).values(); return v.size() == 1 && v[0] == value; } return false; } const std::vector& MarsRequest::values(const std::string& name, bool emptyOk) const { std::list::const_iterator i = find(name); if (i == params_.end()) { if (emptyOk) { static std::vector empty; return empty; } std::ostringstream oss; oss << "No parameter called '" << name << "' in request " << *this; throw eckit::UserError(oss.str()); } return (*i).values(); } std::optional>> MarsRequest::get( const std::string& keyword) const { std::list::const_iterator i = find(keyword); if (i == params_.end()) { return std::nullopt; } return std::cref((*i).values()); } const std::string& MarsRequest::operator[](const std::string& name) const { std::list::const_iterator i = find(name); if (i == params_.end()) { std::ostringstream oss; oss << "Parameter '" << name << "' is undefined"; throw eckit::UserError(oss.str()); } const std::vector& c = (*i).values(); if (c.size() > 1) { std::ostringstream oss; oss << "Parameter '" << name << "' has more than one value"; throw eckit::UserError(oss.str()); } return c[0]; } void MarsRequest::getParams(std::vector& p) const { p.clear(); for (std::list::const_iterator i = params_.begin(); i != params_.end(); ++i) { p.push_back((*i).name()); } } size_t MarsRequest::count() const { size_t result = 1; const std::set& paramIdsSingleLevel = ParamID::getMlParamsSingleLevel(); bool ml = false; size_t levels = 1; size_t params = 0; size_t paramsSingleLevel = 0; bool hasLevelOne = false; for (const auto& p : params_) { if (p.values().size() == 1 && (p.values().at(0) == "all" || p.values().at(0) == "any")) { return 0; } if (p.name() == "levelist") { levels = p.values().size(); // For a parameter matching paramIdsSingleLevel, we can only include one value, and for level=1 hasLevelOne = std::find(p.values().begin(), p.values().end(), "1") != p.values().end(); } else { if (p.name() == "param") { for (const auto& v : p.values()) { paramsSingleLevel += paramIdsSingleLevel.count(v); } params = p.values().size() - paramsSingleLevel; } else { if (p.name() == "levtype") { ml = std::find(p.values().begin(), p.values().end(), "ml") != p.values().end(); } result *= p.count(); } } } if ((params + paramsSingleLevel) > 0) { if (ml) { result *= (levels * params + (hasLevelOne ? paramsSingleLevel : 0)); } else { result *= (levels * (params + paramsSingleLevel)); } } return result; } std::vector MarsRequest::params() const { std::vector p; getParams(p); return p; } MarsRequest::operator eckit::Value() const { NOTIMP; } // recursively expand along keys in expvalues void expand_along_keys(const MarsRequest& prototype, const std::vector>>& expvalues, std::vector& requests, size_t i) { if (i == expvalues.size()) { requests.push_back(prototype); return; } const std::string& key = expvalues[i].first; const std::vector& values = expvalues[i].second; MarsRequest req(prototype); for (auto& value : values) { req.setValue(key, value); expand_along_keys(req, expvalues, requests, i + 1); } } std::vector MarsRequest::split(const std::vector& keys) const { size_t n = 1; LOG_DEBUG_LIB(LibMetkit) << "Splitting request with keys" << keys << std::endl; std::vector>> expvalues; for (auto& key : keys) { std::vector v = values(key, true); // ok to be empty LOG_DEBUG_LIB(LibMetkit) << "splitting along key " << key << " n values " << v.size() << " values " << v << std::endl; if (v.empty()) continue; n *= v.size(); expvalues.emplace_back(std::make_pair(key, v)); } std::vector requests; requests.reserve(n); if (n == 1) { requests.push_back(*this); return requests; } expand_along_keys(*this, expvalues, requests, 0); return requests; } std::vector MarsRequest::split(const std::string& key) const { std::vector keys = {key}; return split(keys); } void MarsRequest::merge(const MarsRequest& other) { for (auto& param : params_) { LOG_DEBUG_LIB(LibMetkit) << "Merging parameter " << param << std::endl; auto it = other.find(param.name()); if (it != other.params_.end()) param.merge(*it); } } MarsRequest MarsRequest::subset(const std::set& keys) const { MarsRequest req(verb_); for (std::list::const_iterator it = params_.begin(); it != params_.end(); ++it) { if (keys.find(it->name()) != keys.end()) { req.params_.push_back(*it); } } return req; } MarsRequest MarsRequest::extract(const std::string& category) const { MarsRequest req(verb_); for (std::list::const_iterator it = params_.begin(); it != params_.end(); ++it) { if (it->type().category() == category) { req.params_.push_back(*it); } } return req; } void MarsRequest::verb(const std::string& verb) { verb_ = verb; } bool MarsRequest::operator<(const MarsRequest& other) const { if (verb_ != other.verb_) { return verb_ < other.verb_; } return params_ < other.params_; } void MarsRequest::setValue(const std::string& name, const char* value) { std::string v(value); setValue(name, v); } const std::string& MarsRequest::verb() const { return verb_; } std::list::const_iterator MarsRequest::find(const std::string& name) const { for (std::list::const_iterator i = params_.begin(); i != params_.end(); ++i) { if ((*i).name() == name) { return i; } } return params_.end(); } std::list::iterator MarsRequest::find(const std::string& name) { for (std::list::iterator i = params_.begin(); i != params_.end(); ++i) { if ((*i).name() == name) { return i; } } return params_.end(); } void MarsRequest::erase(const std::string& name) { auto it = find(name); if (it != params_.end()) { params_.erase(it); } } std::string MarsRequest::asString() const { std::ostringstream oss; oss << *this; return oss.str(); } //---------------------------------------------------------------------------------------------------------------------- std::vector MarsRequest::parse(std::istream& in, bool strict) { MarsParser parser(in); MarsExpansion expand(true, strict); return expand.expand(parser.parse()); } MarsRequest MarsRequest::parse(const std::string& s, bool strict) { std::istringstream in(s); std::vector v = parse(in, strict); ASSERT(v.size() == 1); return v[0]; } //---------------------------------------------------------------------------------------------------------------------- } // namespace mars } // namespace metkit metkit-1.19.2/src/metkit/mars/TypeTime.cc0000664000175000017500000000347215232567717020407 0ustar alastairalastair/* * (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 #include #include "metkit/mars/TypeTime.h" #include "metkit/mars/TypeToByList.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- TypeTime::TypeTime(const std::string& name, const eckit::Value& settings) : Type(name, settings) { toByList_ = std::make_unique>(*this, settings); multiple_ = true; } bool TypeTime::expand(std::string& value, const MarsRequest&) const { eckit::Time time(value); std::ostringstream oss; if (time.seconds() != 0) { oss << "Cannot normalise time '" << value << "' - seconds not supported"; throw eckit::BadValue(oss.str(), Here()); } if (time.hours() >= 24) { oss << "Cannot normalise time '" << value << "' - " << time.hours() << " hours > 24 not supported"; throw eckit::BadValue(oss.str(), Here()); } oss << std::setfill('0') << std::setw(2) << time.hours() << std::setfill('0') << std::setw(2) << time.minutes(); value = oss.str(); return true; } void TypeTime::print(std::ostream& out) const { out << "TypeTime[name=" << name_ << "]"; } static TypeBuilder type("time"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeToByListQuantile.cc0000664000175000017500000000503015232567717022715 0ustar alastairalastair/* * (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 #include "metkit/mars/TypeToByListQuantile.h" #include "eckit/exception/Exceptions.h" #include "eckit/utils/StringTools.h" #include "eckit/utils/Tokenizer.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/Quantile.h" #include "metkit/mars/TypeToByList.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- TypeToByListQuantile::TypeToByListQuantile(const std::string& name, const eckit::Value& settings) : Type(name, settings) { eckit::Value values = settings["denominators"]; if (!values.isList()) { values = MarsLanguage::jsonFile(values); ASSERT(values.isList()); } for (size_t i = 0; i < values.size(); ++i) { const eckit::Value& val = values[i]; if (val.isNumber()) { long v = val; if (denominators_.find(v) == denominators_.end()) { denominators_.emplace(v); } else { std::ostringstream oss; oss << "Redefined " << v << "-quantile"; throw eckit::SeriousBug(oss.str()); } } } LOG_DEBUG_LIB(LibMetkit) << "TypeToByListQuantile name=" << name << " denominators " << denominators_ << std::endl; toByList_ = std::make_unique>(*this, settings); multiple_ = true; } void TypeToByListQuantile::print(std::ostream& out) const { out << "TypeToByListQuantile[name=" << name_ << "]"; } bool TypeToByListQuantile::expand(std::string& value, const MarsRequest&) const { Quantile q(value); if (denominators_.find(q.den()) == denominators_.end()) { std::ostringstream oss; oss << name_ << ": " << q.den() << "-quantile not supported."; throw eckit::BadValue(oss.str()); } return true; } static TypeBuilder type("to-by-list-quantile"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeRange.cc0000664000175000017500000000355515232567717020547 0ustar alastairalastair/* * (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 #include "metkit/mars/TypeRange.h" #include "eckit/exception/Exceptions.h" #include "eckit/utils/StringTools.h" #include "eckit/utils/Tokenizer.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/Quantile.h" #include "metkit/mars/StepRange.h" #include "metkit/mars/TypeTime.h" #include "metkit/mars/TypeToByList.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class ExtendedTime : public eckit::Time { public: ExtendedTime(long seconds = 0) : Time(seconds, true) {} ExtendedTime(const std::string& time) : Time(time, true) {} }; //---------------------------------------------------------------------------------------------------------------------- TypeRange::TypeRange(const std::string& name, const eckit::Value& settings) : Type(name, settings) { toByList_ = std::make_unique>(*this, settings); multiple_ = true; } void TypeRange::print(std::ostream& out) const { out << "TypeRange[name=" << name_ << "]"; } bool TypeRange::expand(std::string& value, const MarsRequest&) const { value = StepRange{value}; return true; } static TypeBuilder type("range"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeDate.h0000664000175000017500000000233115232567717020221 0ustar alastairalastair/* * (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. */ /// @file TypeDate.h /// @author Baudouin Raoult /// @author Tiago Quintino /// @author Emanuele Danovaro /// @date April 2016 #pragma once #include "metkit/mars/Type.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- class TypeDate : public Type { public: // methods TypeDate(const std::string& name, const eckit::Value& settings); ~TypeDate() noexcept override = default; private: // methods void print(std::ostream& out) const override; void pass2(MarsRequest& request) override; bool expand(std::string& value, const MarsRequest& request) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsLanguage.h0000664000175000017500000000562315232567717021057 0ustar alastairalastair/* * (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. */ /// @author Manuel Fuentes /// @author Baudouin Raoult /// @author Tiago Quintino /// @date Sep 96 #pragma once #include #include #include #include #include #include "eckit/memory/NonCopyable.h" #include "metkit/mars/MarsRequest.h" namespace metkit::mars { class Context; class FlattenCallback; class Type; //---------------------------------------------------------------------------------------------------------------------- enum class ModifierType { DEFAULT, SET, UNSET }; //---------------------------------------------------------------------------------------------------------------------- class MarsLanguage : private eckit::NonCopyable { public: // methods MarsLanguage(const std::string& verb); ~MarsLanguage(); MarsRequest expand(const MarsRequest& r, bool inherit, bool strict); void reset(); const std::string& verb() const; void flatten(const MarsRequest& request, FlattenCallback& callback); static eckit::PathName languageYamlFile(); Type* type(const std::string& name) const; bool isData(const std::string& keyword) const; bool isPostProc(const std::string& keyword) const; bool isSink(const std::string& keyword) const; const std::set& sinkKeywords() const; public: // class methods static std::string expandVerb(const std::string& verb); static std::string bestMatch(const std::string& name, const std::vector& values, bool fail, bool quiet, bool fullMatch, const std::map& aliases = {}); static eckit::Value jsonFile(const std::string& name); private: // methods void flatten(const MarsRequest& request, const std::vector& params, size_t i, MarsRequest& result, FlattenCallback& callback); void parseModifier(ModifierType typ, std::shared_ptr ctx, size_t maxIndex, const eckit::Value& mod); private: // members std::string verb_; std::map types_; std::set dataKeywords_; std::set sinkKeywords_; std::set postProcKeywords_; std::vector> typesByAxisOrder_; std::vector keywords_; std::map aliases_; mutable std::map cache_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsExpansion.h0000664000175000017500000000373415232567717021301 0ustar alastairalastair/* * (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. */ /// @author Manuel Fuentes /// @author Baudouin Raoult /// @author Tiago Quintino /// @date Sep 96 #ifndef metkit_MarsExpansion_H #define metkit_MarsExpansion_H #include "eckit/memory/NonCopyable.h" #include #include #include #include "metkit/mars/MarsParsedRequest.h" #include "metkit/mars/MarsRequest.h" namespace metkit::mars { class MarsLanguage; //---------------------------------------------------------------------------------------------------------------------- class FlattenCallback { public: virtual void operator()(const MarsRequest&) = 0; protected: virtual ~FlattenCallback() = default; }; class ExpandCallback { public: virtual void operator()(const MarsRequest&) = 0; protected: virtual ~ExpandCallback() = default; }; //---------------------------------------------------------------------------------------------------------------------- class MarsExpansion : public eckit::NonCopyable { public: MarsExpansion(bool inherit, bool strict = false); ~MarsExpansion(); void reset(); MarsRequest expand(const MarsRequest&); std::vector expand(const std::vector&); void expand(const MarsRequest&, ExpandCallback&); void flatten(const MarsRequest&, FlattenCallback&); private: MarsLanguage& language(const std::string& verb); std::map languages_; bool inherit_; bool strict_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars #endif metkit-1.19.2/src/metkit/mars/Param.cc0000664000175000017500000000346515232567717017711 0ustar alastairalastair/* * (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 "metkit/mars/Param.h" #include "eckit/exception/Exceptions.h" #include "eckit/persist/DumpLoad.h" #include "eckit/utils/Tokenizer.h" using namespace eckit; namespace metkit { //---------------------------------------------------------------------------------------------------------------------- Param::Param(const std::string& s) { Tokenizer parse("."); std::vector result; parse(s, result); switch (result.size()) { case 1: value_ = atol(result[0].c_str()); table_ = 0; break; case 2: value_ = atol(result[0].c_str()); table_ = atol(result[1].c_str()); break; default: throw eckit::BadValue("Invalid param"); } } Param::operator std::string() const { std::ostringstream os; os << *this; return os.str(); } void Param::print(std::ostream& s) const { if (table_) s << value_ << '.' << table_; else s << value_; } void Param::dump(DumpLoad& a) const { a.dump(value_); a.dump(table_); } void Param::load(DumpLoad& a) { a.load(value_); a.load(table_); } long Param::paramId() const { size_t t = table_ == 0 ? value_ / 1000 : table_; return (t == 128 ? 0 : t * 1000) + value_ % 1000; } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit metkit-1.19.2/src/metkit/mars/RequestEnvironment.h0000664000175000017500000000351315232567717022362 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @author Tiago Quintino #pragma once #include #include #include #include #include "metkit/mars/MarsRequest.h" namespace metkit::mars { /// Encapsulates the environment to be sent to the mars-server. /// This uses a mars request with 'environ' as the verb and each environment /// variable is treated as keyword with its respective value. class RequestEnvironment { public: RequestEnvironment(const RequestEnvironment& other); /// Create the actual "mars-request" /// @return the request const MarsRequest& request() const { return *env_; } /// @brief Initialize the RequestEnvironment with a map of KEYWORD:VALUE(s). /// @param env, map of KEYWORD:VALUE (or KEYWORD:VALUE1/VALUE2/.../VALUEn) to add to the environment. static void initialize(const std::map& env); /// @brief Update the RequestEnvironment with a map of KEYWORD:VALUE(s). /// @param env, map of KEYWORD:VALUE (or KEYWORD:VALUE1/VALUE2/.../VALUEn) to add to the environment. void update(const std::map& env); /// Access instance of RequestEnvironment /// @return RequestEnvironment static const RequestEnvironment& instance(); private: RequestEnvironment() = default; static RequestEnvironment& inst(); std::optional env_ = std::nullopt; std::recursive_mutex init_; }; } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsParsedRequest.cc0000664000175000017500000000130615232567717022253 0ustar alastairalastair/* * (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 "metkit/mars/MarsParsedRequest.h" namespace metkit::mars { MarsParsedRequest::MarsParsedRequest(const std::string& verb, size_t line) : MarsRequest(verb), line_(line) {} void MarsParsedRequest::info(std::ostream& out) const { out << " Request starting line " << line_; } } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/TypeInteger.cc0000664000175000017500000000571215232567717021105 0ustar alastairalastair/* * (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 "eckit/utils/Translator.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/TypeInteger.h" #include "metkit/mars/TypeToByList.h" #include "metkit/mars/TypesFactory.h" namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- TypeInteger::TypeInteger(const std::string& name, const eckit::Value& settings) : Type(name, settings) { // check if the settings contain a range if (settings.contains("range") && settings["range"].size() == 2) { range_ = {settings["range"][0], settings["range"][1]}; } } void TypeInteger::print(std::ostream& out) const { out << "TypeInteger[name=" << name() << "]"; } bool TypeInteger::ok(const std::string& value, long& n) const { n = 0; long sign = 1; for (std::string::const_iterator j = value.begin(); j != value.end(); ++j) { switch (*j) { case '-': if (j == value.begin()) { sign = -1; } else { return false; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n *= 10; n += (*j) - '0'; break; default: return false; } } n *= sign; return !range_ || (n >= range_->lower_ && n <= range_->upper_); } bool TypeInteger::expand(std::string& value, const MarsRequest&) const { long n = 0; if (ok(value, n)) { static eckit::Translator l2s; value = l2s(n); return true; } return false; } static TypeBuilder type("integer"); //---------------------------------------------------------------------------------------------------------------------- class TypeToByListInt : public TypeInteger { public: TypeToByListInt(const std::string& name, const eckit::Value& settings) : TypeInteger(name, settings) { toByList_ = std::make_unique>(*this, settings); multiple_ = true; } protected: void print(std::ostream& out) const override { out << "TypeToByListInt[name=" << name() << "]"; } }; static TypeBuilder typeList("to-by-list"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/ClientTask.cc0000664000175000017500000000345515232567717020711 0ustar alastairalastair/* * (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 #include #include "metkit/mars/ClientTask.h" #include "metkit/mars/MarsHandle.h" namespace metkit { namespace mars { // Call by the clien code ClientTask::ClientTask(const MarsRequest& r, const MarsRequest& e, const std::string& host, int port, unsigned long long id) : request_(r), environ_(e), metkitID_(id), port_(port), host_(host), handle_() { // Try something unique (per machine) // Warning: Servers recovers time(0) from ID // to compute request age. Not good if (!metkitID_) { typedef unsigned long long ull; metkitID_ = (ull(::getpid()) << ull(32 + 16)) | (ull(::pthread_self()) << ull(32)) | (ull(::time(0)) & ull(0xffffffff)); } handle_.reset(new MarsHandle(host_, port_, metkitID_)); } ClientTask::~ClientTask() {} void ClientTask::send(eckit::Stream& s) const { unsigned long long dummy = 0; s.startObject(); s << "MarsTask"; /* send mars request id */ s << dummy; /* Send requests */ s << request_; s << environ_; /* Send cb info */ s << host_; s << port_; s << metkitID_; /* Send datahandle */ s << *handle_; s.endObject(); } char ClientTask::receive(eckit::Stream& s) const { unsigned long long id; char mode; s >> id; ASSERT(id == metkitID_); s >> mode; return mode; } } // namespace mars } // namespace metkit metkit-1.19.2/src/metkit/mars/TypeParam.cc0000664000175000017500000003537615232567717020561 0ustar alastairalastair/* * (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 "metkit/mars/TypeParam.h" #include "eckit/config/Resource.h" #include "eckit/log/Log.h" #include "eckit/parser/YAMLParser.h" #include "eckit/thread/AutoLock.h" #include "eckit/types/Types.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/TypesFactory.h" using eckit::Log; using metkit::LibMetkit; namespace { static eckit::Mutex* local_mutex = 0; static pthread_once_t once = PTHREAD_ONCE_INIT; class Matcher { std::string name_; eckit::Value values_; public: Matcher(const std::string& name, const eckit::Value values); bool match(const metkit::mars::MarsRequest& request, bool partial = false) const; friend std::ostream& operator<<(std::ostream& out, const Matcher& matcher) { out << matcher.name_ << "=" << matcher.values_; return out; } }; Matcher::Matcher(const std::string& name, const eckit::Value values) : name_(name), values_(values) { if (!values_.isList()) { values_ = eckit::Value::makeList(values_); } } bool Matcher::match(const metkit::mars::MarsRequest& request, bool partial) const { std::vector vals = request.values(name_, true); if (vals.size() == 0) { return partial; } for (size_t i = 0; i < values_.size(); i++) { std::string v = values_[i]; if (v == vals[0]) { return true; } } return false; } //---------------------------------------------------------------------------------------------------------------------- class Rule { std::vector matchers_; std::vector values_; mutable std::map mapping_; static std::vector defaultValues_; static std::map defaultMapping_; public: bool match(const metkit::mars::MarsRequest& request, bool partial = false) const; std::string lookup(const std::string& s, bool fail) const; long toParamid(const std::string& param) const; Rule(const eckit::Value& matchers, const eckit::Value& setters, const eckit::Value& ids); static void setDefault(const eckit::Value& setters, const eckit::Value& ids); void print(std::ostream& out) const { out << "{"; const char* sep = ""; for (std::vector::const_iterator j = matchers_.begin(); j != matchers_.end(); ++j) { out << sep << (*j); sep = ","; } out << "}"; } friend std::ostream& operator<<(std::ostream& out, const Rule& rule) { rule.print(out); return out; } }; std::vector Rule::defaultValues_; std::map Rule::defaultMapping_; void Rule::setDefault(const eckit::Value& values, const eckit::Value& ids) { std::map precedence; for (size_t i = 0; i < values.size(); ++i) { const eckit::Value& id = values[i]; std::string first = id; defaultValues_.push_back(first); const eckit::Value& aliases = ids[id]; if (aliases.isNil()) { LOG_DEBUG_LIB(LibMetkit) << "No aliases for " << id << std::endl; continue; } for (size_t j = 0; j < aliases.size(); ++j) { std::string v = aliases[j]; if (defaultMapping_.find(v) != defaultMapping_.end()) { if (precedence[v] <= j) { LOG_DEBUG_LIB(LibMetkit) << "Redefinition ignored: param " << v << "='" << first << "', keeping previous value of '" << defaultMapping_[v] << "' " << std::endl; continue; } else { LOG_DEBUG_LIB(LibMetkit) << "Redefinition of param " << v << "='" << first << "', overriding previous value of '" << defaultMapping_[v] << "' " << std::endl; precedence[v] = j; } } else { precedence[v] = j; } defaultMapping_[v] = first; defaultValues_.push_back(v); } } } Rule::Rule(const eckit::Value& matchers, const eckit::Value& values, const eckit::Value& ids) { std::map precedence; const eckit::Value& keys = matchers.keys(); for (size_t i = 0; i < keys.size(); ++i) { std::string name = keys[i]; matchers_.push_back(Matcher(name, matchers[name])); } for (size_t i = 0; i < values.size(); ++i) { const eckit::Value& id = values[i]; std::string first = id; values_.push_back(first); const eckit::Value& aliases = ids[id]; if (aliases.isNil()) { LOG_DEBUG_LIB(LibMetkit) << "No aliases for " << id << " " << *this << std::endl; continue; } for (size_t j = 0; j < aliases.size(); ++j) { std::string v = aliases[j]; if (mapping_.find(v) != mapping_.end()) { if (precedence[v] <= j) { LOG_DEBUG_LIB(LibMetkit) << "Redefinition ignored: param " << v << "='" << first << "', keeping previous value of '" << mapping_[v] << "' " << *this << std::endl; continue; } else { LOG_DEBUG_LIB(LibMetkit) << "Redefinition of param " << v << "='" << first << "', overriding previous value of '" << mapping_[v] << "' " << *this << std::endl; precedence[v] = j; } } else { precedence[v] = j; } mapping_[v] = {first}; values_.push_back(v); } } } bool Rule::match(const metkit::mars::MarsRequest& request, bool partial) const { for (std::vector::const_iterator j = matchers_.begin(); j != matchers_.end(); ++j) { if (!(*j).match(request, partial)) { return false; } } return true; } std::string Rule::lookup(const std::string& s, bool fail) const { size_t table = 0; size_t param = 0; size_t* n = ¶m; bool ok = true; for (std::string::const_iterator k = s.begin(); k != s.end(); ++k) { switch (*k) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': (*n) *= 10; (*n) += (*k) - '0'; break; case '.': if (n == ¶m) { n = &table; } else { ok = false; } break; default: ok = false; break; } } if (ok && param > 0) { std::ostringstream oss; if (table == 128) { table = 0; } if (table > 0 && param >= 1000) { throw eckit::UserError("Unrecognised format for parameter " + s, Here()); } oss << table * 1000 + param; std::string p = oss.str(); for (std::vector::const_iterator j = values_.begin(); j != values_.end(); ++j) { if ((*j) == p) { return p; } } for (std::vector::const_iterator j = defaultValues_.begin(); j != defaultValues_.end(); ++j) { if ((*j) == p) { return p; } } throw eckit::UserError("Cannot match parameter " + p, Here()); } std::string paramid = metkit::mars::MarsLanguage::bestMatch(s, values_, false, false, true, mapping_); if (!paramid.empty()) { return paramid; } return metkit::mars::MarsLanguage::bestMatch(s, defaultValues_, fail, false, false, defaultMapping_); } static std::vector* rules = nullptr; } // namespace static void init() { local_mutex = new eckit::Mutex(); rules = new std::vector(); const eckit::Value ids = eckit::YAMLParser::decodeFile(LibMetkit::paramIDYamlFile()); ASSERT(ids.isOrderedMap()); eckit::ValueMap merge; static bool metkitLegacyParamCheck = eckit::Resource("metkitLegacyParamCheck;$METKIT_LEGACY_PARAM_CHECK", false); static bool metkitRawParam = eckit::Resource("metkitRawParam;$METKIT_RAW_PARAM", false); if (metkitLegacyParamCheck || (!metkitRawParam)) { eckit::Value r = eckit::YAMLParser::decodeFile(LibMetkit::paramYamlFile()); ASSERT(r.isList()); const eckit::Value rs = eckit::YAMLParser::decodeFile(LibMetkit::paramStaticYamlFile()); ASSERT(rs.isList()); // merge r and rs for (size_t i = 0; i < r.size(); ++i) { const eckit::Value& rule = r[i]; if (!rule.isList()) { rule.dump(Log::error()) << std::endl; } ASSERT(rule.isList()); ASSERT(rule.size() == 2); merge.emplace(rule[0], rule[1]); } for (size_t i = 0; i < rs.size(); ++i) { const eckit::Value& rule = rs[i]; if (!rule.isList()) { rule.dump(Log::error()) << std::endl; } ASSERT(rule.isList()); ASSERT(rule.size() == 2); auto it = merge.find(rule[0]); if (it == merge.end()) { merge.emplace(rule[0], rule[1]); } else { it->second += rule[1]; } } } if (metkitLegacyParamCheck) { for (auto it = merge.begin(); it != merge.end(); it++) { (*rules).push_back(Rule(it->first, it->second, ids)); } return; } auto keys = ids.keys(); Rule::setDefault(keys, ids); if (metkitRawParam) { // empty rule, to enable default (*rules).push_back(Rule(eckit::Value::makeMap(), eckit::Value::makeList(), eckit::Value::makeMap())); return; } std::set shortnames; std::set associatedIDs; const eckit::Value pc = eckit::YAMLParser::decodeFile(LibMetkit::shortnameContextYamlFile()); ASSERT(pc.isList()); for (size_t i = 0; i < pc.size(); i++) { shortnames.emplace(pc[i]); } for (size_t i = 0; i < keys.size(); i++) { auto el = ids.element(keys[i]); for (size_t j = 0; j < el.size(); j++) { if (shortnames.find(el[j]) != shortnames.end()) { associatedIDs.emplace(keys[i]); } } } for (auto it = merge.begin(); it != merge.end(); it++) { auto listIDs = eckit::Value::makeList(); for (size_t j = 0; j < it->second.size(); j++) { if (associatedIDs.find(it->second[j]) != associatedIDs.end()) { listIDs.append(it->second[j]); } } if (listIDs.size() > 0) { (*rules).push_back(Rule{it->first, listIDs, ids}); } } (*rules).push_back(Rule{eckit::Value::makeMap(), eckit::Value::makeList(), eckit::Value::makeMap()}); } namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- TypeParam::TypeParam(const std::string& name, const eckit::Value& settings) : Type(name, settings), firstRule_(false) { if (settings.contains("expand_with")) { expandWith_ = settings["expand_with"]; } if (settings.contains("first_rule")) { firstRule_ = settings["first_rule"]; } } void TypeParam::print(std::ostream& out) const { out << "TypeParam[name=" << name_ << "]"; } void TypeParam::pass2(MarsRequest& request) { pthread_once(&once, init); bool fail = true; const Rule* rule = 0; std::vector values = request.values(name_, true); if (values.size() == 1 && values[0] == "all") { return; } eckit::AutoLock lock(local_mutex); for (const auto& r : *rules) { if (r.match(request)) { rule = &r; break; } } if (!rule) { Log::warning() << "TypeParam: cannot find a context to expand 'param' in " << request << std::endl; if (firstRule_) { bool found = false; for (const auto& r : *rules) { if (r.match(request, true)) { for (std::vector::iterator j = values.begin(); j != values.end() && !rule; ++j) { std::string& s = (*j); try { s = r.lookup(s, fail); rule = &r; Log::warning() << "TypeParam: using 'first matching rule' option " << r << std::endl; } catch (...) { } } } } } else if (expandWith_.size()) { MarsRequest tmp(request); for (auto j = expandWith_.begin(); j != expandWith_.end(); ++j) { if (!tmp.has((*j).first)) { tmp.setValue((*j).first, (*j).second); } } for (const auto& r : *rules) { if (r.match(tmp)) { rule = &r; Log::warning() << "TypeParam using 'expand with' option " << *rule << std::endl; break; } } } if (!rule) { std::ostringstream oss; oss << "TypeParam: cannot find a context to expand 'param' in " << request; throw eckit::SeriousBug(oss.str()); } } for (std::vector::iterator j = values.begin(); j != values.end(); ++j) { std::string& s = (*j); try { s = rule->lookup(s, fail); } catch (...) { Log::error() << *rule << std::endl; throw; } } request.setValuesTyped(this, values); } bool TypeParam::expand(std::string&, const MarsRequest&) const { // Work done on pass2() return true; } void TypeParam::reset() { // cache_.clear(); Type::reset(); } static TypeBuilder type("param"); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsRequestHandle.cc0000664000175000017500000001010715232567717022227 0ustar alastairalastair/* * (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. */ // File MarsRequestHandle.cc // Baudouin Raoult - (c) ECMWF Feb 12 #include "metkit/mars/MarsRequestHandle.h" #include "eckit/types/Types.h" #include "eckit/utils/StringTools.h" #include "metkit/config/LibMetkit.h" namespace metkit::mars { static eckit::Reanimator marsRequestHandleReanimator; const eckit::ReanimatorBase& MarsRequestHandle::reanimator() const { return marsRequestHandleReanimator; } const eckit::ClassSpec& MarsRequestHandle::classSpec() { static eckit::ClassSpec spec = {&eckit::DataHandle::classSpec(), "MarsRequestHandle"}; return spec; } bool shortName(const std::string& prefix, const std::string& s) { if (prefix.size() > s.size()) return false; return std::equal(prefix.begin(), prefix.end(), s.begin()); } MarsRequestHandle::MarsRequestHandle(eckit::Stream& s) : eckit::DataHandle(s), request_(s), protocol_(eckit::Reanimator::reanimate(s)), opened_(false) {} MarsRequestHandle::MarsRequestHandle(const MarsRequest& request, BaseProtocol* protocol) : request_(request), protocol_(protocol), opened_(false) { LOG_DEBUG_LIB(LibMetkit) << "MarsRequestHandle::MarsRequestHandle: request: " << request << " protocol: " << protocol << std::endl; ASSERT(protocol); } MarsRequestHandle::MarsRequestHandle(const metkit::mars::MarsRequest& request, const eckit::Configuration& database) : request_(request.verb()), protocol_(ProtocolFactory::build(database)), opened_(false) { for (const auto& p : request.parameters()) { request_.values(p.name(), p.values()); } } MarsRequestHandle::~MarsRequestHandle() { if (opened_) { close(); } } const eckit::StringDict& MarsRequestHandle::stats() const { return protocol_->stats(); } eckit::Length MarsRequestHandle::openForRead() { eckit::Log::info() << "MarsRequestHandle::openForRead[" << request_ << "]" << std::endl; ASSERT(!opened_); opened_ = true; const std::string v(eckit::StringTools::lower(request_.verb())); ASSERT(v == "retrieve" || v == "stage" || v == "list" || v == "get"); return protocol_->retrieve(request_); } void MarsRequestHandle::openForWrite(const eckit::Length& size) { eckit::Log::info() << "MarsRequestHandle::openForWrite[" << request_ << "]" << std::endl; ASSERT(!opened_); opened_ = true; ASSERT(eckit::StringTools::lower(request_.verb()) == "archive"); protocol_->archive(request_, size); } void MarsRequestHandle::openForAppend(const eckit::Length&) { eckit::Log::info() << "MarsRequestHandle::openForAppend[" << request_ << "]" << std::endl; ASSERT(!opened_); opened_ = true; NOTIMP; } long MarsRequestHandle::read(void* buffer, long len) { ASSERT(opened_); try { return protocol_->read(buffer, len); } catch (eckit::Exception& e) { std::ostringstream oss; oss << "Exception " << e.what() << " caught in MarsRequestHandle::read(" << *protocol_ << ")"; eckit::Log::error() << oss.str() << std::endl; throw RetryTransfer(oss.str()); } } long MarsRequestHandle::write(const void* buffer, long len) { ASSERT(opened_); return protocol_->write(buffer, len); } void MarsRequestHandle::close() { eckit::Log::info() << "MarsRequestHandle::close[" << request_ << "]" << std::endl; opened_ = false; protocol_->cleanup(); } void MarsRequestHandle::print(std::ostream& s) const { s << "MarsRequestHandle[" << *protocol_ << "," << request_ << "]"; } void MarsRequestHandle::encode(eckit::Stream& s) const { eckit::DataHandle::encode(s); s << request_; s << *protocol_; } bool MarsRequestHandle::canSeek() const { return false; } } // namespace metkit::mars metkit-1.19.2/src/metkit/mars/MarsRequest.h0000664000175000017500000001304515232567717020761 0ustar alastairalastair/* * (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. */ /// @author Manuel Fuentes /// @author Baudouin Raoult /// @author Tiago Quintino /// @date Sep 96 #ifndef metkit_MarsRequest_H #define metkit_MarsRequest_H #include #include "eckit/value/Value.h" #include "metkit/mars/Parameter.h" namespace eckit { class MD5; namespace message { class Message; } } // namespace eckit struct metkit_marsrequest_t; namespace metkit::mars { class Type; class MarsRequest; //---------------------------------------------------------------------------------------------------------------------- class MarsRequest { public: // methods MarsRequest(); explicit MarsRequest(const std::string&); explicit MarsRequest(eckit::Stream&, bool lowercase = false); MarsRequest(const std::string&, const std::map&); MarsRequest(const std::string&, const eckit::Value&); explicit MarsRequest(const eckit::message::Message&); ~MarsRequest() = default; bool operator<(const MarsRequest& other) const; // eckit::Value& operator[](const std::string&); const std::string& operator[](const std::string&) const; operator eckit::Value() const; const std::string& verb() const; size_t countValues(const std::string&) const; bool has(const std::string&) const; bool is(const std::string& param, const std::string& value) const; const std::vector& values(const std::string&, bool emptyOk = false) const; // Returns reference to values or nullopt if not found std::optional>> get(const std::string& keyword) const; template size_t getValues(const std::string& name, std::vector& v, bool emptyOk = false) const; void getParams(std::vector&) const; std::vector params() const; std::list& parameters() { return params_; } const std::list& parameters() const { return params_; } void verb(const std::string&); void values(const std::string&, const std::vector&); template void setValue(const std::string& name, const T& value); void setValue(const std::string& name, const char* value); void unsetValues(const std::string&); /// Splits a MARS request into multiple requests along the provided key std::vector split(const std::string& keys) const; /// Splits a MARS request into multiple requests along the indicated keys std::vector split(const std::vector& keys) const; /// Merges one MarsRequest into another /// @todo Improve performance -- uses O(N^2) search / merge in std::list's void merge(const MarsRequest& other); /// Create a new MarsRequest from this one with only the given set of keys MarsRequest subset(const std::set&) const; void json(eckit::JSON&, bool array = false) const; void md5(eckit::MD5&) const; void dump(std::ostream&, const char* cr = "\n", const char* tab = "\t", bool verb = true) const; void setValuesTyped(Type*, const std::vector&); bool filter(const MarsRequest& filter); bool matches(const MarsRequest& filter) const; bool empty() const; size_t count() const; MarsRequest extract(const std::string& category) const; void erase(const std::string& param); std::string asString() const; public: // static methods static MarsRequest parse(const std::string& s, bool strict = false); static std::vector parse(std::istream&, bool strict = false); /// Implementation in api/metkit_c.cc static const MarsRequest& fromOpaque(const metkit_marsrequest_t* request); private: // members std::string verb_; std::list params_; private: // methods void print(std::ostream&) const; void encode(eckit::Stream&) const; std::list::const_iterator find(const std::string&) const; std::list::iterator find(const std::string&); // -- Class members static eckit::ClassSpec classSpec_; static eckit::Reanimator reanimator_; friend std::ostream& operator<<(std::ostream& s, const MarsRequest& r) { r.print(s); return s; } friend eckit::JSON& operator<<(eckit::JSON& s, const MarsRequest& r) { r.json(s); return s; } friend eckit::Stream& operator<<(eckit::Stream& s, const MarsRequest& r) { r.encode(s); return s; } }; template size_t MarsRequest::getValues(const std::string& name, std::vector& v, bool emptyOk) const { const std::vector& s = values(name, emptyOk); eckit::Translator t; v.clear(); for (std::vector::const_iterator j = s.begin(); j != s.end(); ++j) { v.push_back(t(*j)); } return v.size(); } template void MarsRequest::setValue(const std::string& name, const T& value) { eckit::Translator t; std::vector v(1, t(value)); values(name, v); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars #endif metkit-1.19.2/src/metkit/mars/MarsLanguage.cc0000664000175000017500000004314315232567717021214 0ustar alastairalastair/* * (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 "metkit/mars/MarsLanguage.h" #include #include #include #include "eckit/config/Resource.h" #include "eckit/log/Log.h" #include "eckit/parser/YAMLParser.h" #include "eckit/types/Types.h" #include "eckit/utils/StringTools.h" #include "metkit/config/LibMetkit.h" #include "metkit/hypercube/HyperCube.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/Type.h" #include "metkit/mars/TypesFactory.h" //---------------------------------------------------------------------------------------------------------------------- static pthread_once_t once = PTHREAD_ONCE_INIT; static eckit::Value languages_; static std::vector modifiers_{}; static std::set verbs_; static std::map verbAliases_; static void init() { languages_ = eckit::YAMLParser::decodeFile(metkit::LibMetkit::languageYamlFile()); for (const auto& file : metkit::LibMetkit::modifiersYamlFiles()) { modifiers_.push_back(eckit::YAMLParser::decodeFile(file)); } const eckit::Value verbs = languages_.keys(); for (size_t i = 0; i < verbs.size(); ++i) { verbs_.insert(verbs[i]); eckit::Value lang = languages_[verbs[i]]; if (lang.contains("_aliases")) { eckit::Value aliases = lang["_aliases"]; ASSERT(aliases.isList()); for (size_t j = 0; j < aliases.size(); ++j) { verbAliases_.emplace(aliases[j], verbs[i]); } } } } namespace metkit::mars { //---------------------------------------------------------------------------------------------------------------------- void MarsLanguage::parseModifier(ModifierType typ, std::shared_ptr ctx, size_t maxIndex, const eckit::Value& mod) { eckit::Value keys; if (typ == ModifierType::UNSET) { ASSERT(mod.isList()); keys = mod; } else { ASSERT(mod.isMap()); keys = mod.keys(); } for (size_t j = 0; j < keys.size(); ++j) { std::string key = keys[j]; auto it = types_.find(key); if (it != types_.end()) { ASSERT(!isData(key) || maxIndex <= metkit::hypercube::AxisOrder::instance().index(key)); if (typ == ModifierType::UNSET) { it->second->unset(ctx); } else { eckit::Value vv = mod[key]; std::vector vals; if (vv.isList()) { for (size_t k = 0; k < vv.size(); ++k) { vals.push_back(vv[k]); } } else { vals.push_back(vv); } if (typ == ModifierType::DEFAULT) it->second->defaults(ctx, vals); else if (typ == ModifierType::SET) it->second->set(ctx, vals); } } } } MarsLanguage::MarsLanguage(const std::string& verb) { pthread_once(&once, init); verb_ = MarsLanguage::expandVerb(verb); eckit::Value lang = languages_[verb_]; eckit::Value params = lang.keys(); eckit::Value options = lang["_options"]; for (size_t i = 0; i < params.size(); ++i) { std::string keyword = params[i]; eckit::Value settings = lang[keyword]; if (keyword[0] == '_') { continue; } ASSERT(types_.find(keyword) == types_.end()); if (options.contains(keyword)) { eckit::ValueMap m = options[keyword]; for (auto j = m.begin(); j != m.end(); ++j) { settings[(*j).first] = (*j).second; } } auto [it, success] = types_.emplace(keyword, TypesFactory::build(keyword, settings)); it->second->attach(); keywords_.push_back(keyword); std::optional aliases; if (settings.contains("aliases")) { aliases = settings["aliases"]; } if (settings.contains("category") && settings["category"] == "data") { dataKeywords_.insert(keyword); if (aliases) { for (size_t j = 0; j < aliases->size(); ++j) { dataKeywords_.insert((*aliases)[j]); } } } if (settings.contains("category") && settings["category"] == "postproc") { postProcKeywords_.insert(keyword); if (aliases) { for (auto j = 0; j < aliases->size(); ++j) { postProcKeywords_.insert((*aliases)[j]); } } } if (settings.contains("category") && settings["category"] == "sink") { sinkKeywords_.insert(keyword); if (aliases) { for (auto j = 0; j < aliases->size(); ++j) { sinkKeywords_.insert((*aliases)[j]); } } } if (aliases) { for (size_t j = 0; j < aliases->size(); ++j) { aliases_[(*aliases)[j]] = keyword; keywords_.push_back((*aliases)[j]); } } } // load modifiers and associate to types for (const auto& modifierFile : modifiers_) { ASSERT(modifierFile.isList()); for (size_t i = 0; i < modifierFile.size(); ++i) { eckit::Value mod = modifierFile[i]; ASSERT(mod.isMap()); ASSERT(mod.contains("context")); std::shared_ptr ctx = Context::parseContext(mod["context"]); size_t maxIndex = ctx->maxAxisIndex(); if (mod.contains("defaults")) { parseModifier(ModifierType::DEFAULT, ctx, maxIndex, mod["defaults"]); } if (mod.contains("set")) { parseModifier(ModifierType::SET, ctx, maxIndex, mod["set"]); } if (mod.contains("unset")) { parseModifier(ModifierType::UNSET, ctx, maxIndex, mod["unset"]); } } } if (lang.contains("_clear_defaults")) { const auto& keywords = lang["_clear_defaults"]; for (auto i = 0; i < keywords.size(); ++i) { if (auto iter = types_.find(keywords[i]); iter != types_.end()) { iter->second->clearDefaults(); } } } for (const std::string& a : hypercube::AxisOrder::instance().axes()) { Type* t = nullptr; auto it = types_.find(a); if (it != types_.end()) { t = it->second; } typesByAxisOrder_.emplace_back(a, t); } for (auto& [k, t] : types_) { if (dataKeywords_.find(k) == dataKeywords_.end()) { typesByAxisOrder_.emplace_back(k, t); } } } bool MarsLanguage::isData(const std::string& keyword) const { return (dataKeywords_.find(keyword) != dataKeywords_.end()); } bool MarsLanguage::isPostProc(const std::string& keyword) const { return (postProcKeywords_.find(keyword) != postProcKeywords_.end()); } bool MarsLanguage::isSink(const std::string& keyword) const { return (sinkKeywords_.find(keyword) != sinkKeywords_.end()); } const std::set& MarsLanguage::sinkKeywords() const { return sinkKeywords_; } MarsLanguage::~MarsLanguage() { for (auto& [k, t] : types_) { t->detach(); } } eckit::PathName MarsLanguage::languageYamlFile() { return metkit::LibMetkit::languageYamlFile(); } void MarsLanguage::reset() { for (auto& [k, t] : types_) { t->reset(); } } eckit::Value MarsLanguage::jsonFile(const std::string& name) { // TODO: cache eckit::PathName path = metkit::LibMetkit::configFile(name); LOG_DEBUG_LIB(LibMetkit) << "MarsLanguage loading jsonFile " << path << std::endl; std::ifstream in(path.asString().c_str()); if (!in) { throw eckit::CantOpenFile(path); } eckit::YAMLParser parser(in); return parser.parse(); } static bool isnumeric(const std::string& s) { for (size_t i = 0; i < s.length(); i++) { if (!::isdigit(s[i])) { return false; } } return s.length() > 0; } std::string MarsLanguage::bestMatch(const std::string& name, const std::vector& values, bool fail, bool quiet, bool fullMatch, const std::map& aliases) { size_t score = (fullMatch ? name.length() : 1); std::vector best; for (size_t i = 0; i < values.size(); ++i) { const std::string& value = values[i]; size_t len = std::min(name.length(), value.length()); size_t s = 0; // Prefix comparison, break if there is a difference for (size_t j = 0; j < len; ++j) { if (::tolower(name[j]) == ::tolower(value[j])) { s++; } else { break; } } // if value and name are the same (in lowercase), look up aliases and return the alias // otherwise return the value (which is name) if (s == value.length() && s == name.length()) { if (aliases.find(value) != aliases.end()) { return aliases.find(value)->second; } return value; } // If fullmatch == true: // only option is s==score, otherwise the break above would have triggered // so best is only filled with exact matches // If fullmatch == false: // score keeps track of the best value found, if a better value (in terms of matching prefixes) // is found, clear the best vector and push the new best. If all matches are equally good, keep them in the best // vector if (s >= score) { if (s > score) { best.clear(); } best.push_back(value); score = s; } } if (!quiet && best.size() > 0) { std::cerr << "Matching '" << name << "' with " << best << std::endl; } static bool strict = eckit::Resource("$METKIT_LANGUAGE_STRICT_MODE", true); if (best.size() == 1) { // If the best entry is a number or not name (why is this even needed) if (isnumeric(best[0]) && (best[0] != name)) { best.clear(); } else { if (strict) { if (best[0] != name) { std::ostringstream oss; oss << "Cannot match [" << name << "] in " << values; throw eckit::UserError(oss.str()); } } if (aliases.find(best[0]) != aliases.end()) { return aliases.find(best[0])->second; } return best[0]; } } // In case the match is empty, return or fail, depending on the fail flag static std::string empty; if (best.empty()) { if (!fail) { return empty; } std::ostringstream oss; oss << "Cannot match [" << name << "] in " << values; throw eckit::UserError(oss.str()); } // Check the existing aliases for mappings of the matches to the canonical parameter name // A set is used to de-duplicate the findings std::set names; for (std::vector::const_iterator j = best.begin(); j != best.end(); ++j) { const auto k = aliases.find(*j); if (k == aliases.end()) { names.insert(*j); } else { names.insert((*k).second); } } // If there is only on match return the given one if (names.size() == 1) { if (aliases.find(best[0]) != aliases.end()) { return aliases.find(best[0])->second; } return best[0]; } // Otherwise there is an ambiguity and we want to return or fail (depending on the fail flag) if (!fail) { return empty; } std::ostringstream oss; oss << "Ambiguous value '" << name << "' could be"; for (std::vector::const_iterator j = best.begin(); j != best.end(); ++j) { auto k = aliases.find(*j); if (k == aliases.end()) { oss << " '" << *j << "'"; } else { oss << " '" << *j << "' ("; oss << (*k).second; oss << ")"; } } throw eckit::UserError(oss.str()); } std::string MarsLanguage::expandVerb(const std::string& verb) { pthread_once(&once, init); std::string v = eckit::StringTools::lower(verb); auto vv = verbs_.find(v); if (vv != verbs_.end()) { return v; } auto aa = verbAliases_.find(v); if (aa != verbAliases_.end()) { return aa->second; } std::ostringstream oss; oss << "Verb " << v << " is not supported"; throw eckit::UserError(oss.str()); } class TypeHidden : public Type { bool flatten() const override { return false; } void print(std::ostream& out) const override { out << "TypeHidden"; } bool expand(std::string&, const MarsRequest&) const override { return true; } public: TypeHidden() : Type("hidden", eckit::Value()) { attach(); } }; Type* MarsLanguage::type(const std::string& name) const { auto k = types_.find(name); if (k == types_.end()) { if (name[0] == '_') { static TypeHidden hidden; return &hidden; } throw eckit::SeriousBug("Cannot find a type for '" + name + "'"); } return k->second; } MarsRequest MarsLanguage::expand(const MarsRequest& r, bool inherit, bool strict) { MarsRequest result(verb_); try { std::vector> sortedParams; std::map paramSet; std::vector params; for (const auto& PP : r.params()) { auto c = cache_.find(PP); if (c != cache_.end()) { paramSet.emplace((*c).second, PP); } else { std::string p = eckit::StringTools::lower(PP); paramSet.emplace(cache_[p] = bestMatch(p, keywords_, true, false, true, aliases_), PP); } } { // sort the parameters, following the AxisOrder for (const auto& k : metkit::hypercube::AxisOrder::instance().axes()) { auto it = paramSet.find(k); if (it != paramSet.end()) { sortedParams.emplace_back(k, it->second); paramSet.erase(it); } } for (const auto& [k, PP] : paramSet) { sortedParams.emplace_back(k, PP); } } for (const auto& [p, PP] : sortedParams) { std::vector values = r.values(PP); if (values.size() == 1) { const std::string& s = eckit::StringTools::lower(values[0]); if (s == "off") { result.unsetValues(p); type(p)->reset(); continue; } if (s == "all" && type(p)->multiple()) { result.setValue(p, "all"); continue; } } auto t = type(p); t->expand(values, result); result.setValuesTyped(t, values); t->check(values); } if (inherit) { for (const auto& [k, t] : typesByAxisOrder_) { if (t != nullptr && result.countValues(k) == 0) { t->setDefaults(result); } } result.getParams(params); for (std::vector::const_iterator k = params.begin(); k != params.end(); ++k) { type(*k)->setInheritance(result.values(*k)); } } result.getParams(params); for (std::vector::const_iterator k = params.begin(); k != params.end(); ++k) { type(*k)->pass2(result); } for (const auto& [k, t] : typesByAxisOrder_) { if (t != nullptr) t->finalise(result, strict); } } catch (std::exception& e) { std::ostringstream oss; oss << e.what() << " request=" << r << ", expanded=" << result; throw eckit::UserError(oss.str()); } return result; } const std::string& MarsLanguage::verb() const { return verb_; } void MarsLanguage::flatten(const MarsRequest& request, const std::vector& params, size_t i, MarsRequest& result, FlattenCallback& callback) { if (i == params.size()) { callback(result); return; } const std::string& param = params[i]; Type* t = type(param); if (!t->flatten()) { flatten(request, params, i + 1, result, callback); return; } const std::vector& values = t->flattenValues(request); for (std::vector::const_iterator j = values.begin(); j != values.end(); ++j) { result.setValue(param, *j); flatten(request, params, i + 1, result, callback); } } void MarsLanguage::flatten(const MarsRequest& request, FlattenCallback& callback) { std::vector params; request.getParams(params); MarsRequest result(request); flatten(request, params, 0, result, callback); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::mars metkit-1.19.2/src/metkit/hypercube/0000775000175000017500000000000015232567717017356 5ustar alastairalastairmetkit-1.19.2/src/metkit/hypercube/HyperCube.cc0000664000175000017500000002332615232567717021561 0ustar alastairalastair/* * (C) Copyright 2017- 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 "metkit/hypercube/HyperCube.h" #include #include "eckit/exception/Exceptions.h" #include "eckit/parser/YAMLParser.h" #include "metkit/mars/MarsLanguage.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Type.h" namespace metkit::hypercube { AxisOrder::AxisOrder() { eckit::Value axis = eckit::YAMLParser::decodeFile(axisYamlFile()); const eckit::Value axesNames = axis["axes"]; for (size_t i = 0; i < axesNames.size(); ++i) { axisIndex_[axesNames[i]] = i + 1; axes_.push_back(axesNames[i]); } } size_t AxisOrder::index(const std::string& axis) const { auto it = axisIndex_.find(axis); if (it == axisIndex_.end()) { return 0; } return it->second; } AxisOrder& AxisOrder::instance() { static AxisOrder instance; return instance; } class Axis { public: Axis(const std::string& name, const std::vector& values) : name_(name), values_(values) {} size_t size() const { return values_.size(); } const std::string& name() const { return name_; } int indexOf(const std::string& v) const { auto j = std::find(values_.begin(), values_.end(), v); if (j == values_.end()) { return -1; } return j - values_.begin(); } const std::string& valueOf(size_t index) const { if (values_.size() <= index) { std::ostringstream oss; oss << "Axis::valueOf no value for [axis=" << name() << ",index=" << index << "]"; throw eckit::UserError(oss.str()); } return values_[index]; } friend std::ostream& operator<<(std::ostream& s, const Axis& a) { s << "Axis[" << a.name_ << "]:"; for (size_t i = 0; i < a.values_.size(); ++i) { s << " " << a.values_[i]; } return s; } private: std::string name_; std::vector values_; }; HyperCube::HyperCube(const metkit::mars::MarsRequest& request) : verb_(request.verb()), cube_(std::vector()) { std::vector dimensions; for (auto& name : AxisOrder::instance().axes()) { std::vector values = request.values(name, true); if (!values.empty()) { Axis* a = new Axis(name, values); axes_.push_back(a); axesByName_[name] = a; dimensions.push_back(values.size()); } } cube_ = eckit::HyperCube(dimensions); count_ = cube_.count(); set_ = std::vector(count_, true); } HyperCube::~HyperCube() { for (auto& a : axes_) { delete a; } } bool HyperCube::contains(const metkit::mars::MarsRequest& r) const { int idx = indexOf(r); return (idx >= 0) and set_[idx]; } bool HyperCube::clear(int idx) { if (idx < 0) return false; if (!set_[idx]) return false; set_[idx] = false; count_--; return true; } bool HyperCube::clear(const metkit::mars::MarsRequest& r) { int idx = indexOf(r); return clear(idx); } int HyperCube::indexOf(const metkit::mars::MarsRequest& r) const { std::vector coords; for (auto& a : axes_) { const std::vector& values = r.values(a->name(), true); if (values.size() == 0) { std::ostringstream oss; oss << "HyperCube::indexOf no value for [" << a->name() << "] in request " << r; throw eckit::UserError(oss.str()); } if (values.size() > 1) { std::ostringstream oss; oss << "HyperCube::indexOf too many values for [" << a->name() << "] in request " << r; throw eckit::UserError(oss.str()); } int n = a->indexOf(values[0]); if (n < 0) { return -1; } coords.push_back(n); } return cube_.index(coords); } enum requestRelation { EMBEDDED, ADJACENT, DISJOINT }; requestRelation getRelation(const metkit::mars::MarsRequest& base, const size_t& baseSize, const metkit::mars::MarsRequest& additional, const size_t additionalSize) { metkit::mars::MarsRequest tmp(base); tmp.merge(additional); // creates the bounding box request /// @todo: building a hypercube *JUST* to get the size? Seems like we can do better size_t sizeAfter = tmp.count(); if (sizeAfter == baseSize) return requestRelation::EMBEDDED; if (baseSize + additionalSize == sizeAfter) return requestRelation::ADJACENT; return requestRelation::DISJOINT; } // Returns true only if the last request was merged into an adjacent bool mergeLast(std::vector>& requests) { size_t last = requests.size() - 1; size_t candidateIdx = std::numeric_limits::max(); size_t candidateSize = 0; // check if the new request is embedded or adjacent to existing requests for (size_t j = 0; j < requests.size() - 1; j++) { requestRelation relation = getRelation(requests[j].first, requests[j].second, requests[last].first, requests[last].second); if (relation == requestRelation::EMBEDDED) { requests.pop_back(); return false; } if (relation == requestRelation::ADJACENT) { // We can merge the two requests // Though we will only merge with the largest adjacent request, so dont merge yet. size_t combinedSize = requests[j].second + requests[last].second; if (candidateSize < combinedSize) { candidateIdx = j; candidateSize = combinedSize; } } } // Merge with the largest adjacent request if one was found if (candidateIdx != std::numeric_limits::max()) { requests[candidateIdx].first.merge(requests[last].first); requests[candidateIdx].second += requests[last].second; requests.pop_back(); return true; } return false; } std::vector> HyperCube::request(const std::set& idxs) const { using IndexSet = std::set; ASSERT(idxs.size() > 0); if (idxs.size() <= 1) { return {{requestOf(*idxs.begin()), 1}}; } // -- helper lambdas -------------------------------------------- // Partition the cube into a set of slices along the given axis. const auto sliceAlongAxis = [&](const IndexSet& set, std::size_t axis) { std::map slices; std::vector coords(axes_.size()); for (std::size_t idx : set) { cube_.coordinates(idx, coords); slices[coords[axis]].insert(idx); } return slices; }; // Pick the axis which can be partitioned into the smallest number of slices (>1). const auto pickBestAxis = [&](const IndexSet& set) -> std::size_t { std::size_t bestAxis = 0; std::size_t bestNSlices = std::numeric_limits::max(); for (std::size_t axis = 0; axis < axes_.size(); ++axis) { const std::size_t nSlices = sliceAlongAxis(set, axis).size(); if (nSlices > 1 && nSlices < bestNSlices) { bestAxis = axis; bestNSlices = nSlices; } } ASSERT(bestNSlices != std::numeric_limits::max()); return bestAxis; }; // --------------------------------------------------------------- const std::size_t axis = pickBestAxis(idxs); const auto slices = sliceAlongAxis(idxs, axis); std::vector> result; // Process each slice recursively, appending and merging on the fly. for (const auto& [coord, subIdxs] : slices) { auto subRequests = request(subIdxs); // recursion result.insert(result.end(), subRequests.begin(), subRequests.end()); while (mergeLast(result)) {} } return result; } std::vector HyperCube::aggregatedRequests(bool remaining) const { if (countVacant() == (remaining ? 0 : size())) return std::vector{}; std::set idxs; for (size_t i = 0; i < set_.size(); ++i) { if (set_[i] == remaining) idxs.emplace(i); } std::vector> requests = request(idxs); std::vector out; for (auto req : requests) out.push_back(req.first); return out; } metkit::mars::MarsRequest HyperCube::requestOf(size_t index) const { metkit::mars::MarsRequest request(verb_); std::vector coords(axes_.size()); cube_.coordinates(index, coords); for (size_t i = 0; i < axes_.size(); i++) { request.setValue(axes_[i]->name(), axes_[i]->valueOf(coords[i])); } return request; } size_t HyperCube::count() const { return count_; } size_t HyperCube::countVacant() const { return count_; } size_t HyperCube::fieldOrdinal(const metkit::mars::MarsRequest& r, bool noholes) const { int idx = indexOf(r); ASSERT(idx >= 0); if (noholes) { size_t p = 0; for (size_t i = 0; i < idx; ++i) { if (set_[i]) { p++; } } return p; } return idx; } void HyperCube::print(std::ostream& s) const { NOTIMP; ///@todo } } // namespace metkit::hypercube metkit-1.19.2/src/metkit/hypercube/HyperCubePayloaded.h0000664000175000017500000000362215232567717023243 0ustar alastairalastair/* * (C) Copyright 2017- 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. */ /// @author Emanuele Danovaro /// @date Aug 2020 #ifndef metkit_HyperCubePayloaded_H #define metkit_HyperCubePayloaded_H #include "metkit/hypercube/HyperCube.h" namespace metkit { namespace hypercube { template class Deduplicator { public: virtual bool toReplace(const T& existing, const T& replacement) const = 0; }; template class HyperCubePayloaded : public HyperCube { public: HyperCubePayloaded(const metkit::mars::MarsRequest& request, const Deduplicator& deduplicator) : HyperCube(request), dedup_(deduplicator) { // throw error for default dedup } void add(const metkit::mars::MarsRequest& request, T payload) { int idx = indexOf(request); ASSERT(0 <= idx && idx < size()); auto entry = entries_.find(idx); if (entry == entries_.end()) { entries_.emplace(idx, payload); clear(idx); } else { if (dedup_.toReplace(entry->second, payload)) { entry->second = payload; } } } const T& at(size_t idx) { ASSERT(0 <= idx); ASSERT(idx < size()); return entries_[idx]; } bool find(size_t idx, T& payload) { auto it = entries_.find(idx); if (it == entries_.end()) return false; payload = it->second; return true; } private: const Deduplicator& dedup_; std::map entries_; }; } // namespace hypercube } // namespace metkit #endif metkit-1.19.2/src/metkit/hypercube/HyperCube.h0000664000175000017500000000534515232567717021424 0ustar alastairalastair/* * (C) Copyright 2017- 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. */ /// @author Baudouin Raoult /// @date Aug 2017 #ifndef metkit_HyperCube_H #define metkit_HyperCube_H #include #include #include #include #include "eckit/utils/HyperCube.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/MarsRequest.h" namespace metkit { namespace hypercube { class Axis; class AxisOrder { public: // methods static AxisOrder& instance(); const std::vector& axes() { return axes_; } size_t index(const std::string& axis) const; private: // methods AxisOrder(); eckit::PathName axisYamlFile() { return "~metkit/share/metkit/axis.yaml"; } private: // members std::vector axes_; std::map axisIndex_; }; class HyperCube { public: HyperCube(const metkit::mars::MarsRequest&); ~HyperCube(); bool contains(const metkit::mars::MarsRequest&) const; bool clear(const metkit::mars::MarsRequest&); size_t count() const; size_t countVacant() const; size_t size() const { return cube_.count(); } size_t fieldOrdinal(const metkit::mars::MarsRequest&, bool noholes = true) const; std::vector vacantRequests() const { return aggregatedRequests(true); } std::vector requests() const { return aggregatedRequests(false); } protected: std::vector aggregatedRequests(bool remaining) const; int indexOf(const metkit::mars::MarsRequest&) const; bool clear(int index); metkit::mars::MarsRequest requestOf(size_t index) const; // Given a set of indices, build the *minimal* collection of Mars requests that cover them. // Each entry in the result vector is: { merged_request, number_of_points_covered_by_that_request } /// @note: This does not take into account whether the point is "set" or not std::vector> request(const std::set& idxs) const; private: std::string verb_; std::vector axes_; std::map axesByName_; std::vector set_; eckit::HyperCube cube_; size_t count_; void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const HyperCube& p) { p.print(s); return s; } }; } // namespace hypercube } // namespace metkit #endif metkit-1.19.2/src/metkit/codes/0000775000175000017500000000000015232567717016465 5ustar alastairalastairmetkit-1.19.2/src/metkit/codes/GRIBDecoder.cc0000664000175000017500000001464015232567717021012 0ustar alastairalastair/* * (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 "eckit/config/Resource.h" #include "eckit/exception/Exceptions.h" #include "eckit/io/Buffer.h" #include "eckit/message/Message.h" #include "eckit/serialisation/MemoryStream.h" #include "metkit/codes/GRIBDecoder.h" #include "metkit/codes/api/CodesAPI.h" #include "eccodes.h" #include #include namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- bool GRIBDecoder::match(const eckit::message::Message& msg) const { size_t len = msg.length(); const char* p = static_cast(msg.data()); return len >= 4 and ((p[0] == 'G' and p[1] == 'R' and p[2] == 'I' and p[3] == 'B') or (p[0] == 'T' and p[1] == 'I' and p[2] == 'D' and p[3] == 'E') or (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 { static std::string gribToRequestNamespace = eckit::Resource("gribToRequestNamespace", "mars"); std::string nameSpace = options.nameSpace ? *options.nameSpace : gribToRequestNamespace; auto h = codesHandleFromMessage({static_cast(msg.data()), msg.length()}); for (const auto& k : h->keys(nameSpace)) { auto name = k.name(); if (name[0] == '_') continue; // skip silly underscores in GRIB // Get key size to see if it is an array // Only continue for scalar values if (h->size(name) != 1) { continue; } switch (options.valueRepresentation) { case eckit::message::ValueRepresentation::String: { // 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; // 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>) { gather.setValue(name, k.getString()); } else { // Unhandled types are all array types - the prior call checking `size != 1` only allows // for scalars. throw eckit::Exception( std::string("Unexpected type when accessing GRIB message metadata ") + typeid(v).name(), Here()); } }, k.get()); break; } } } // Explicit override for param (kludge for paramID handling) if (h->has("paramId")) { gather.setValue("param", h->getString("paramId")); } // Look for request embbeded in GRIB message if (h->has("localDefinitionNumber") && h->getLong("localDefinitionNumber") == 191) { /* TODO: Not grib2 compatible, but speed-up process */ if (h->has("freeFormData")) { auto buffer = h->getBytes("freeFormData"); eckit::MemoryStream s(buffer.data(), buffer.size()); int count; s >> count; // Number of requests ASSERT(count == 1); std::string tmp; s >> tmp; // verb s >> count; for (int i = 0; i < count; i++) { std::string keyword, value; int n; s >> keyword; std::transform(keyword.begin(), keyword.end(), keyword.begin(), tolower); s >> n; // Number of values ASSERT(n == 1); s >> value; std::transform(value.begin(), value.end(), value.begin(), tolower); gather.setValue(keyword, value); } } } } eckit::Buffer GRIBDecoder::decode(const eckit::message::Message& msg) const { std::size_t size = msg.getSize("values"); eckit::Buffer buf(size * sizeof(double)); msg.getDoubleArray("values", reinterpret_cast(buf.data()), size); return buf; } void GRIBDecoder::print(std::ostream& s) const { s << "GRIBDecoder[]"; } //---------------------------------------------------------------------------------------------------------------------- static GRIBDecoder gribDecoder; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/codes/BufrContent.h0000664000175000017500000000215315232567717021070 0ustar alastairalastair/* * (C) Copyright 2017- 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. */ /// @author Baudouin Raoult /// @author Emanuele Danovaro /// @date Mar 2022 #pragma once #include "metkit/codes/CodesDataContent.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- class BufrContent : public CodesDataContent { public: BufrContent(std::unique_ptr handle); virtual ~BufrContent() = default; protected: using CodesDataContent::transform; void transform(const eckit::OrderedStringDict& dict) override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/codes/CodesDataContent.cc0000664000175000017500000001230115232567717022153 0ustar alastairalastair/* * (C) Copyright 2017- 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 "metkit/codes/CodesDataContent.h" #include "eccodes.h" #include "eckit/exception/Exceptions.h" #include "eckit/io/DataHandle.h" #include "eckit/io/MemoryHandle.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- CodesDataContent::CodesDataContent(std::unique_ptr handle) : handle_(std::move(handle)), offset_(handle_->getLong("offset")) {} CodesDataContent::CodesDataContent(std::unique_ptr handle, eckit::Offset offset) : handle_(std::move(handle)), offset_(std::move(offset)) {} //---------------------------------------------------------------------------------------------------------------------- size_t CodesDataContent::length() const { return handle_->messageSize(); } //---------------------------------------------------------------------------------------------------------------------- void CodesDataContent::write(eckit::DataHandle& handle) const { auto data = handle_->messageData(); if (handle.write(data.data(), data.size()) != data.size()) { std::ostringstream oss; oss << "Write error to data handle " << handle; throw eckit::WriteError(oss.str(), Here()); } } //---------------------------------------------------------------------------------------------------------------------- eckit::DataHandle* CodesDataContent::readHandle() const { auto data = handle_->messageData(); return new eckit::MemoryHandle(data.data(), data.size()); } //---------------------------------------------------------------------------------------------------------------------- void CodesDataContent::print(std::ostream& s) const { s << "CodesDataContent[]"; } //---------------------------------------------------------------------------------------------------------------------- std::string CodesDataContent::getString(const std::string& key) const { return handle_->getString(key); } //---------------------------------------------------------------------------------------------------------------------- long CodesDataContent::getLong(const std::string& key) const { return handle_->getLong(key); } //---------------------------------------------------------------------------------------------------------------------- double CodesDataContent::getDouble(const std::string& key) const { return handle_->getDouble(key); } //---------------------------------------------------------------------------------------------------------------------- void CodesDataContent::getDoubleArray(const std::string& key, std::vector& values) const { values = handle_->getDoubleArray(key); } //---------------------------------------------------------------------------------------------------------------------- void CodesDataContent::getFloatArray(const std::string& key, std::vector& values) const { values = handle_->getFloatArray(key); } //---------------------------------------------------------------------------------------------------------------------- size_t CodesDataContent::getSize(const std::string& key) const { return handle_->size(key); } //---------------------------------------------------------------------------------------------------------------------- void CodesDataContent::getDoubleArray(const std::string& key, double* data, size_t len) const { auto arr = handle_->getDoubleArray(key); ASSERT(len == arr.size()); std::copy(arr.begin(), arr.end(), data); } //---------------------------------------------------------------------------------------------------------------------- void CodesDataContent::getFloatArray(const std::string& key, float* data, size_t len) const { auto arr = handle_->getFloatArray(key); ASSERT(len == arr.size()); std::copy(arr.begin(), arr.end(), data); } //---------------------------------------------------------------------------------------------------------------------- void CodesDataContent::transform(const eckit::OrderedStringDict& dict) { for (auto& [key, value] : dict) { handle_->set(key, value); } } //---------------------------------------------------------------------------------------------------------------------- eckit::Offset CodesDataContent::offset() const { return offset_; } //---------------------------------------------------------------------------------------------------------------------- const void* CodesDataContent::data() const { return handle_->messageData().data(); } //---------------------------------------------------------------------------------------------------------------------- const CodesHandle& CodesDataContent::codesHandle() const { return *handle_.get(); } CodesHandle& CodesDataContent::codesHandle() { return *handle_.get(); } } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/codes/GRIBDecoder.h0000664000175000017500000000245715232567717020657 0ustar alastairalastair/* * (C) Copyright 2020- 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. */ /// @author Baudouin Raoult /// @date Jun 2020 #pragma once #include "eckit/message/Decoder.h" #include "eckit/message/Message.h" #include "eckit/io/Buffer.h" namespace metkit::codes { //---------------------------------------------------------------------------------------------------------------------- class GRIBDecoder : public eckit::message::MessageDecoder { public: // methods private: // methods bool match(const eckit::message::Message&) const override; void print(std::ostream&) const override; void getMetadata(const eckit::message::Message& msg, eckit::message::MetadataGatherer&, const eckit::message::GetMetadataOptions&) const override; eckit::Buffer decode(const eckit::message::Message& msg) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::codes metkit-1.19.2/src/metkit/codes/BUFRDecoder.h0000664000175000017500000000260415232567717020664 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @date Jun 2020 #pragma once #include "eckit/message/Decoder.h" #include "eckit/message/Message.h" #include "eckit/io/Buffer.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- class BUFRDecoder : public eckit::message::MessageDecoder { public: // methods static bool typeBySubtype(long subtype, long& type); private: // methods bool match(const eckit::message::Message&) const override; void print(std::ostream&) const override; void getMetadata(const eckit::message::Message& msg, eckit::message::MetadataGatherer&, const eckit::message::GetMetadataOptions&) const override; eckit::Buffer decode(const eckit::message::Message& msg) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/codes/CodesSplitter.cc0000664000175000017500000000530215232567717021560 0ustar alastairalastair/* * (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 "metkit/codes/CodesSplitter.h" #include "eccodes.h" #include "eckit/io/PeekHandle.h" #include "eckit/message/Message.h" #include "metkit/codes/CodesDataContent.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/codes/api/CodesTypes.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- CodesSplitter::CodesSplitter(eckit::PeekHandle& handle) : Splitter(handle) {} CodesSplitter::~CodesSplitter() {} eckit::message::Message CodesSplitter::next() { eckit::Offset off = handle_.position(); std::unique_ptr codesHandle; try { codesHandle = codesHandleFromStream([&](uint8_t* buffer, int64_t len) -> int64_t { return handle_.read(buffer, len); }); } catch (const CodesWrongLength& e) { // METK-103 - used in bufr-sanity-check tool // Resetting handle and skipping header (ie. GRIB or BUFR) // Allows ignoring messages inbetween and continuing handle_.seek(off + eckit::Offset(4)); throw e; } // Handle EOF if (!codesHandle) { return eckit::message::Message(); } return eckit::message::Message(new CodesDataContent(std::move(codesHandle), off)); } void CodesSplitter::print(std::ostream& s) const { s << "CodesSplitter[]"; } } // namespace codes } // namespace metkit //---------------------------------------------------------------------------------------------------------------------- namespace eckit { namespace message { template <> bool SplitterBuilder::match(eckit::PeekHandle& handle) const { unsigned char c0 = handle.peek(0); unsigned char c1 = handle.peek(1); unsigned char c2 = handle.peek(2); unsigned char c3 = handle.peek(3); if (c0 == 'G' and c1 == 'R' and c2 == 'I' and c3 == 'B') { return true; } if (c0 == 'B' and c1 == 'U' and c2 == 'F' and c3 == 'R') { return true; } if (c0 == 'B' and c1 == 'U' and c2 == 'D' and c3 == 'G') { return true; } if (c0 == 'T' and c1 == 'I' and c2 == 'D' and c3 == 'E') { return true; } return false; } } // namespace message } // namespace eckit static eckit::message::SplitterBuilder splitter; metkit-1.19.2/src/metkit/codes/LibEccodes.cc0000664000175000017500000000237115232567717020773 0ustar alastairalastair/* * (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. */ /// @author Olivier Iffrig /// @date October 2019 #include "eccodes.h" #include "metkit/codes/LibEccodes.h" namespace metkit { //---------------------------------------------------------------------------------------------------------------------- REGISTER_LIBRARY(LibEccodes); LibEccodes::LibEccodes() : Library("eccodes") {} const LibEccodes& LibEccodes::instance() { static LibEccodes libeccodes; return libeccodes; } std::string LibEccodes::version() const { return ECCODES_VERSION_STR; } std::string LibEccodes::gitsha1(unsigned int count) const { std::string sha1(codes_get_git_sha1()); if (sha1.empty()) { return "not available"; } return sha1.substr(0, std::min(count, 40u)); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit metkit-1.19.2/src/metkit/codes/BufrContent.cc0000664000175000017500000000222715232567717021230 0ustar alastairalastair/* * (C) Copyright 2017- 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 "metkit/codes/BufrContent.h" #include "eccodes.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- BufrContent::BufrContent(std::unique_ptr handle) : CodesDataContent(std::move(handle)) {} //---------------------------------------------------------------------------------------------------------------------- void BufrContent::transform(const eckit::OrderedStringDict& dict) { for (const auto& [key, value] : dict) { handle_->set(key, std::stol(value)); } } //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/codes/BUFRDecoder.cc0000664000175000017500000001311015232567717021014 0ustar alastairalastair/* * (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 #include #include "eckit/config/Resource.h" #include "eckit/message/Message.h" #include "eckit/parser/YAMLParser.h" #include "metkit/codes/BUFRDecoder.h" #include "metkit/codes/api/CodesAPI.h" #include "metkit/codes/api/CodesTypes.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/MarsRequest.h" #include "eckit/exception/Exceptions.h" #include "eccodes.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- static std::map subtypes_; static pthread_once_t once = PTHREAD_ONCE_INIT; static void readTable() { eckit::PathName bufrSubtypesPath = eckit::Resource("bufrSubtypesPath;$BUFR_SUBTYPES_PATH", LibMetkit::bufrSubtypesYamlFile()); const eckit::Value bufrSubtypes = eckit::YAMLParser::decodeFile(bufrSubtypesPath); const eckit::Value subtypes = bufrSubtypes["subtypes"]; ASSERT(subtypes.isList()); for (size_t i = 0; i < subtypes.size(); ++i) { const eckit::Value s = subtypes[i]; ASSERT(s.isList()); ASSERT(s.size() == 2); subtypes_[s[0]] = s[1]; } } bool BUFRDecoder::typeBySubtype(long subtype, long& type) { pthread_once(&once, readTable); auto s = subtypes_.find(subtype); if (s != subtypes_.end()) { type = s->second; return true; } return false; } //---------------------------------------------------------------------------------------------------------------------- bool BUFRDecoder::match(const eckit::message::Message& msg) const { size_t len = msg.length(); const char* p = static_cast(msg.data()); return len >= 4 and p[0] == 'B' and p[1] == 'U' and p[2] == 'F' and p[3] == 'R'; } //---------------------------------------------------------------------------------------------------------------------- void BUFRDecoder::getMetadata(const eckit::message::Message& msg, eckit::message::MetadataGatherer& gather, const eckit::message::GetMetadataOptions& options) const { auto h(codesHandleFromMessage({static_cast(msg.data()), msg.length()})); /* // BUFR Performance improvement: https://confluence.ecmwf.int/display/UDOC/Performance+improvement+by+skipping+some+keys+-+ecCodes+BUFR+FAQ if ((unsigned long)(options.filter & eckit::message::MetadataFilter::IncludeExtraKeyAttributes) != 0) { CODES_CHECK(codes_set_long(h.get(), "skipExtraKeyAttributes", 1), 0); } */ // we need to instruct ecCodes to unpack the data values: // https://confluence.ecmwf.int/display/ECC/bufr_keys_iterator h->set("unpack", 1); for (const auto& k : h->keys()) { auto name = k.name(); if (name == "subsetNumber") { continue; } // Get key size to see if it is an array // Only continue for scalar values if (h->size(name) != 1) { continue; } switch (options.valueRepresentation) { case eckit::message::ValueRepresentation::String: { 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) { gather.setValue(name, std::forward(v)); } else if constexpr (std::is_same_v>) { gather.setValue(name, k.getString()); } else { // Unhandled types are all array types - the prior call checking `size != 1` only allows for // scalars. throw eckit::Exception( std::string("Unexpected type when accessing BURF message metadata ") + typeid(v).name(), Here()); } }, k.get()); break; } } } } //---------------------------------------------------------------------------------------------------------------------- eckit::Buffer BUFRDecoder::decode(const eckit::message::Message& msg) const { std::size_t size = msg.getSize("numericValues"); eckit::Buffer buf(size * sizeof(double)); msg.getDoubleArray("numericValues", reinterpret_cast(buf.data()), size); return buf; } //---------------------------------------------------------------------------------------------------------------------- void BUFRDecoder::print(std::ostream& s) const { s << "BUFRDecoder[]"; } //---------------------------------------------------------------------------------------------------------------------- static BUFRDecoder bufrDecoder; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/codes/CodesSplitter.h0000664000175000017500000000205315232567717021422 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @date Jun 2020 #pragma once #include "eckit/message/Splitter.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- class CodesSplitter : public eckit::message::Splitter { public: CodesSplitter(eckit::PeekHandle&); ~CodesSplitter(); private: // methods eckit::message::Message next() override; void print(std::ostream&) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/codes/CodesHandleDeleter.h0000664000175000017500000000231015232567717022310 0ustar alastairalastair/* * (C) Copyright 2022- 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. */ /// @author Philipp Geier /// @author Simon Smart /// @date Nov 2022 #pragma once #include #include //---------------------------------------------------------------------------------------------------------------------- namespace std { template <> struct default_delete { void operator()(codes_handle* h) { ::codes_handle_delete(h); } }; template <> struct default_delete { void operator()(codes_keys_iterator* it) { ::codes_keys_iterator_delete(it); } }; template <> struct default_delete { void operator()(codes_bufr_keys_iterator* it) { ::codes_bufr_keys_iterator_delete(it); } }; } // namespace std //---------------------------------------------------------------------------------------------------------------------- metkit-1.19.2/src/metkit/codes/api/0000775000175000017500000000000015232567717017236 5ustar alastairalastairmetkit-1.19.2/src/metkit/codes/api/CodesTypes.h0000664000175000017500000000553515232567717021501 0ustar alastairalastair/* * (C) Copyright 2025- 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. */ #pragma once #include #include #include #include #include #include "eckit/exception/Exceptions.h" namespace metkit::codes { /// Exception that is thrown by the API to wrap eccodes errors class CodesException : public eckit::Exception { public: CodesException(const std::string& reason, const eckit::CodeLocation& l = eckit::CodeLocation()); }; /// More specialized exception that is thrown in specific cases when a message could not be read properly, usually /// indicating corruption. class CodesWrongLength : public CodesException { public: CodesWrongLength(const std::string& reason, const eckit::CodeLocation& l = eckit::CodeLocation()); }; /// Enum to classify types of handles enum class Product { GRIB, BUFR, }; /// Enum that redefines eccodes specific key types enum class NativeType { Undefined, Long, Double, String, Bytes, Section, Label, Missing, }; /// Sum type of possible values returned by eccodes. using CodesValue = std::variant, std::vector, std::vector, std::vector, std::vector>; //---------------------------------------------------------------------------------------------------------------------- /// To be replaced with std::span in C++20. template struct Span { using element_type = T; using value_type = std::remove_cv_t; const value_type* data_ = nullptr; std::size_t size_ = 0; const value_type* data() { return data_; } std::size_t size() { return size_; } Span() = default; Span(const T* p, std::size_t n) : data_(p), size_(n) {} // from std::vector template Span(const std::vector& v) : data_(v.data()), size_(v.size()) {} // from std::array template Span(const std::array& arr) : data_(arr.data()), size_(N) {} // from C array template Span(const value_type (&arr)[N]) : data_(arr), size_(N) {} // from std::basic_string template ::value, bool> = true> Span(const std::basic_string& s) : data_(s.data()), size_(s.size()) {} }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::codes metkit-1.19.2/src/metkit/codes/api/KeyIterator.cc0000664000175000017500000000345515232567717022016 0ustar alastairalastair/* * (C) Copyright 2025- 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 "metkit/codes/api/KeyIterator.h" #include "eccodes.h" namespace metkit::codes { KeyIteratorFlags operator|(KeyIteratorFlags a, KeyIteratorFlags b) { return static_cast(static_cast >(a) | static_cast >(b)); } KeyIteratorFlags operator&(KeyIteratorFlags a, KeyIteratorFlags b) { return static_cast(static_cast >(a) & static_cast >(b)); } KeyIteratorFlags operator^(KeyIteratorFlags a, KeyIteratorFlags b) { return static_cast(static_cast >(a) ^ static_cast >(b)); } KeyIteratorFlags operator~(KeyIteratorFlags a) { return static_cast(~static_cast >(a)); } bool hasFlag(KeyIteratorFlags value, KeyIteratorFlags flag) { return (static_cast >(value) & static_cast >(flag)) != 0; } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::codes metkit-1.19.2/src/metkit/codes/api/CodesTypes.cc0000664000175000017500000000141615232567717021631 0ustar alastairalastair/* * (C) Copyright 2025- 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 "metkit/codes/api/CodesTypes.h" namespace metkit::codes { CodesException::CodesException(const std::string& reason, const eckit::CodeLocation& l) : eckit::Exception(std::string("CodesException: ") + reason, l) {}; CodesWrongLength::CodesWrongLength(const std::string& reason, const eckit::CodeLocation& l) : CodesException(reason, l) {}; } // namespace metkit::codes metkit-1.19.2/src/metkit/codes/api/CodesAPI.cc0000664000175000017500000007334715232567717021152 0ustar alastairalastair/* * (C) Copyright 2025- 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 "metkit/codes/api/CodesAPI.h" #include "metkit/codes/api/CodesTypes.h" #include "eckit/log/CodeLocation.h" #include "eccodes.h" #include "metkit/config/LibMetkit.h" namespace std { template <> struct default_delete { void operator()(codes_handle* h) { ::codes_handle_delete(h); } }; template <> struct default_delete { void operator()(codes_keys_iterator* it) { ::codes_keys_iterator_delete(it); } }; template <> struct default_delete { void operator()(codes_iterator* it) { ::codes_grib_iterator_delete(it); } }; } // namespace std namespace metkit::codes { namespace { void throwOnError(int code, const eckit::CodeLocation& l, const char* details) { if (code != 0) { std::string msg = std::string(details) + std::string(": ") + std::string(codes_get_error_message(code)); if (code == GRIB_WRONG_LENGTH) { throw CodesWrongLength(msg, l); } else { throw CodesException(msg, l); } } }; void throwOnError(int code, const eckit::CodeLocation& l, const char* details, const std::string& key) { if (code != 0) { std::string msg = std::string(details) + std::string(": ") + std::string(codes_get_error_message(code)) + std::string(" for key ") + key; if (code == GRIB_WRONG_LENGTH) { throw CodesWrongLength(msg, l); } else { throw CodesException(msg, l); } } }; /// Concrete implementation of CodesHandle. /// ConcreteCodesHandle provides the common C++ wrapper implementation around /// a codes_handle* independently of the ownership model. class ConcreteCodesHandle : public CodesHandle { public: virtual ~ConcreteCodesHandle() {}; size_t messageSize() const override; Span messageData() const override; bool isDefined(const std::string& key) const override; bool isMissing(const std::string& key) const override; bool has(const std::string& key) const override; void setMissing(const std::string& key) override; void set(const std::string& key, const std::string& value) override; void set(const std::string& key, double value) override; void set(const std::string& key, long value) override; void set(const std::string& key, Span value) override; void set(const std::string& key, Span value) override; void set(const std::string& key, Span value) override; void set(const std::string& key, Span value) override; void set(const std::string& key, Span value) override; void set(const std::string& key, Span value) override; void forceSet(const std::string& key, Span value) override; void forceSet(const std::string& key, Span value) override; size_t size(const std::string& key) const override; size_t length(const std::string& key) const override; CodesValue get(const std::string& key) const override; NativeType type(const std::string& key) const override; long getLong(const std::string& key) const override; double getDouble(const std::string& key) const override; std::string getString(const std::string& key) const override; std::vector getLongArray(const std::string& key) const override; std::vector getDoubleArray(const std::string& key) const override; std::vector getFloatArray(const std::string& key) const override; std::vector getStringArray(const std::string& key) const override; std::vector getBytes(const std::string& key) const override; /// Clones the underyling handle. /// Uses `codes_handle_clone` internally. /// @return Unique pointer to a cloned `CodesHandle` instance. std::unique_ptr clone() const override; /// Copy the message into a new allocated buffer /// @param data Pointer to an allocated array /// @param size Size of the allocated array void copyInto(uint8_t* data, size_t size) const override; KeyRange keys(KeyIteratorFlags flags = KeyIteratorFlags::AllKeys, std::optional ns = std::optional{}) const override; KeyRange keys(Namespace ns) const override; GeoRange values() const override; /// Release the raw `codes_handle*` - used to pass ownership out of C++ (e.g. python) virtual void* release() override = 0; protected: codes_handle* raw() const { if (!handle()) { throw CodesException("CodesHandle has been released.", Here()); } return handle(); } virtual codes_handle* handle() const = 0; }; /// Concrete implementation of CodesHandle. /// OwningCodesHandle is a owning container around a codes_handle* that /// makes the C API accessible to C++ for any codes_handle*. /// It will properly delete the underlying codes_handle* on /// construction. class OwningCodesHandle : public ConcreteCodesHandle { public: OwningCodesHandle(std::unique_ptr handle) : handle_{std::move(handle)} {}; virtual ~OwningCodesHandle() {}; OwningCodesHandle(OwningCodesHandle&&) = default; OwningCodesHandle& operator=(OwningCodesHandle&&) = default; /// Release the raw `codes_handle*` - used to pass ownership out of C++ (e.g. python) void* release() override { return handle_.release(); }; private: codes_handle* handle() const override { return handle_.get(); } std::unique_ptr handle_; }; /// Concrete implementation of CodesHandle. /// NonOwningCodesHandle wraps a borrowed codes_handle* without taking /// ownership of its lifetime. class NonOwningCodesHandle : public ConcreteCodesHandle { public: NonOwningCodesHandle(codes_handle* handle) : handle_{handle} {}; virtual ~NonOwningCodesHandle() {}; NonOwningCodesHandle(NonOwningCodesHandle&&) = default; NonOwningCodesHandle& operator=(NonOwningCodesHandle&&) = default; void* release() override { throw CodesException("CodesHandle::release() is not supported on a non-owning handle.", Here()); }; private: codes_handle* handle() const override { return handle_; } codes_handle* handle_; }; size_t ConcreteCodesHandle::messageSize() const { size_t size; throwOnError(codes_get_message_size(raw(), &size), Here(), "CodesHandle::messageSize()"); return size; } bool ConcreteCodesHandle::isDefined(const std::string& key) const { return codes_is_defined(raw(), key.c_str()) == 1; } bool ConcreteCodesHandle::isMissing(const std::string& key) const { int err = 0; bool res = codes_is_missing(raw(), key.c_str(), &err) == 1; throwOnError(err, Here(), "CodesHandle::isMissing()", key); return res; } bool ConcreteCodesHandle::has(const std::string& key) const { return isDefined(key) && !isMissing(key); } /// Set a key to its missing value void ConcreteCodesHandle::setMissing(const std::string& key) { throwOnError(codes_set_missing(raw(), key.c_str()), Here(), "CodesHandle::setMissing()", key); } void ConcreteCodesHandle::set(const std::string& key, const std::string& value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API: " + key << " = '" << value << "'" << std::endl; size_t size = value.size(); throwOnError(codes_set_string(raw(), key.c_str(), value.c_str(), &size), Here(), "CodesHandle::set(string, string)", key); } void ConcreteCodesHandle::set(const std::string& key, double value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API: " + key << " = " << std::to_string(value) << std::endl; throwOnError(codes_set_double(raw(), key.c_str(), value), Here(), "CodesHandle::set(string, double)", key); } void ConcreteCodesHandle::set(const std::string& key, long value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API: " + key << " = " << std::to_string(value) << std::endl; throwOnError(codes_set_long(raw(), key.c_str(), value), Here(), "CodesHandle::set(string, long)", key); } /// Set arrays void ConcreteCodesHandle::set(const std::string& key, Span value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API>: " + key << std::endl; std::vector out; out.reserve(value.size()); for (auto it = value.data(); it != value.data() + value.size(); ++it) { out.push_back(it->c_str()); } set(key, out); } void ConcreteCodesHandle::set(const std::string& key, Span value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API>: " + key << std::endl; throwOnError(codes_set_string_array(raw(), key.c_str(), const_cast(value.data()), value.size()), Here(), "CodesHandle::set(string, span)", key); } /// set string array void ConcreteCodesHandle::set(const std::string& key, Span value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API>: " + key << std::endl; throwOnError(codes_set_double_array(raw(), key.c_str(), value.data(), value.size()), Here(), "CodesHandle::set(string, span)", key); } void ConcreteCodesHandle::set(const std::string& key, Span value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API>: " + key << std::endl; throwOnError(codes_set_float_array(raw(), key.c_str(), value.data(), value.size()), Here(), "CodesHandle::set(string, span)", key); } void ConcreteCodesHandle::set(const std::string& key, Span value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API>: " + key << std::endl; throwOnError(codes_set_long_array(raw(), key.c_str(), value.data(), value.size()), Here(), "CodesHandle::set(string, span)", key); } void ConcreteCodesHandle::set(const std::string& key, Span value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API>: " + key << std::endl; size_t size = value.size(); throwOnError(codes_set_bytes(raw(), key.c_str(), value.data(), &size), Here(), "CodesHandle::set(string, span)", key); } void ConcreteCodesHandle::forceSet(const std::string& key, Span value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API>: " + key << std::endl; throwOnError(codes_set_force_double_array(raw(), key.c_str(), value.data(), value.size()), Here(), "CodesHandle::forceSet(string, span)", key); } void ConcreteCodesHandle::forceSet(const std::string& key, Span value) { LOG_DEBUG_LIB(LibMetkit) << std::endl; LOG_DEBUG_LIB(LibMetkit) << "Codes API>: " + key << std::endl; throwOnError(codes_set_force_float_array(raw(), key.c_str(), value.data(), value.size()), Here(), "CodesHandle::forceSet(string, span)", key); } size_t ConcreteCodesHandle::size(const std::string& key) const { size_t size; throwOnError(codes_get_size(raw(), key.c_str(), &size), Here(), "CodesHandle::size(string)", key); return size; } size_t ConcreteCodesHandle::length(const std::string& key) const { size_t length; throwOnError(codes_get_length(raw(), key.c_str(), &length), Here(), "CodesHandle::length(string)", key); return length; } /// Get the value of the key CodesValue ConcreteCodesHandle::get(const std::string& key) const { NativeType ktype = type(key); bool isArray = size(key) > 1; switch (ktype) { case NativeType::Long: if (isArray) { return getLongArray(key); } else { return getLong(key); } case NativeType::Double: if (isArray) { return getDoubleArray(key); } else { return getDouble(key); } case NativeType::String: if (isArray) { return getStringArray(key); } else { return getString(key); } case NativeType::Bytes: return getBytes(key); case NativeType::Undefined: throw CodesException(std::string("CodesHandle::get(") + key + std::string("): Native type is UNDEFINED"), Here()); case NativeType::Section: case NativeType::Label: case NativeType::Missing: default: return getString(key); } } /// Get the type of the key NativeType ConcreteCodesHandle::type(const std::string& key) const { int type; throwOnError(codes_get_native_type(raw(), key.c_str(), &type), Here(), "CodesHandle::type(string)", key); switch (type) { case CODES_TYPE_LONG: return NativeType::Long; case CODES_TYPE_DOUBLE: return NativeType::Double; case CODES_TYPE_STRING: return NativeType::String; case CODES_TYPE_BYTES: return NativeType::Bytes; case CODES_TYPE_SECTION: return NativeType::Section; case CODES_TYPE_LABEL: return NativeType::Label; case CODES_TYPE_MISSING: return NativeType::Missing; default: return NativeType::Undefined; } } /// Explicit getters long ConcreteCodesHandle::getLong(const std::string& key) const { long value; throwOnError(codes_get_long(raw(), key.c_str(), &value), Here(), "CodesHandle::getLong(string)", key); return value; } double ConcreteCodesHandle::getDouble(const std::string& key) const { double value; throwOnError(codes_get_double(raw(), key.c_str(), &value), Here(), "CodesHandle::getDouble(string)", key); return value; } std::string ConcreteCodesHandle::getString(const std::string& key) const { std::string ret; std::size_t keylen = 1024; ret.resize(keylen); throwOnError(codes_get_string(raw(), key.c_str(), ret.data(), &keylen), Here(), "CodesHandle::getString(string)", key); ret.resize(strlen(ret.c_str())); return ret; } std::vector ConcreteCodesHandle::getLongArray(const std::string& key) const { std::vector ret; std::size_t ksize = size(key); ret.resize(ksize); throwOnError(codes_get_long_array(raw(), key.c_str(), ret.data(), &ksize), Here(), "CodesHandle::getLongArray(string)", key); ret.resize(ksize); return ret; } std::vector ConcreteCodesHandle::getDoubleArray(const std::string& key) const { std::vector ret; std::size_t ksize = size(key); ret.resize(ksize); throwOnError(codes_get_double_array(raw(), key.c_str(), ret.data(), &ksize), Here(), "CodesHandle::getDoubleArray(string)", key); ret.resize(ksize); return ret; } std::vector ConcreteCodesHandle::getFloatArray(const std::string& key) const { std::vector ret; std::size_t ksize = size(key); ret.resize(ksize); throwOnError(codes_get_float_array(raw(), key.c_str(), ret.data(), &ksize), Here(), "CodesHandle::getFloatArray(string)", key); ret.resize(ksize); return ret; } std::vector ConcreteCodesHandle::getStringArray(const std::string& key) const { std::vector cstrings; std::size_t ksize = size(key); cstrings.resize(ksize); throwOnError(codes_get_string_array(raw(), key.c_str(), cstrings.data(), &ksize), Here(), "CodesHandle::getStringArray(string)", key); cstrings.resize(ksize); std::vector ret; ret.reserve(cstrings.size()); for (char* cstr : cstrings) { ret.push_back(cstr); } return ret; } std::vector ConcreteCodesHandle::getBytes(const std::string& key) const { std::vector ret; /// Note: The returned length for bytes often is much higher than needed. /// For UIDs (i.e. "uuidOfHGrid"), the native type is BYTES, but length returns the number of characters in its /// string representation - which is twice the number of bytes. However, in contrast for fields like "bitmap" it /// directly returns the number of bytes. It has been discussed with Shahram several times without a change taking /// in. std::size_t ksize = length(key); ret.resize(ksize); throwOnError(codes_get_bytes(raw(), key.c_str(), ret.data(), &ksize), Here(), "CodesHandle::getBytes(string)", key); ret.resize(ksize); return ret; } /// Cloning the whole handle. Expected to be wrapped by the user explicitly std::unique_ptr ConcreteCodesHandle::clone() const { std::unique_ptr ret{codes_handle_clone(raw())}; if (!ret) { throw CodesException("CodesHandle::clone() failed", Here()); } return std::make_unique(std::move(ret)); } /// Copy the message into a new allocated buffer void ConcreteCodesHandle::copyInto(uint8_t* data, size_t size) const { std::size_t s = size; throwOnError(codes_get_message_copy(raw(), data, &s), Here(), "CodesHandle::copy(uint8_t*, size_t*)"); } /// Copy the message into a new allocated buffer Span ConcreteCodesHandle::messageData() const { size_t s; const uint8_t* data; throwOnError(codes_get_message(raw(), reinterpret_cast(&data), &s), Here(), "CodesHandle::messageData()"); return {data, s}; } class ConcreteKeyIterator : public KeyIterator { public: // methods ConcreteKeyIterator(const ConcreteCodesHandle& handle, std::unique_ptr it) : refHandle_{std::cref(handle)}, it_{std::move(it)}, isValid_{false} { next(); } virtual ~ConcreteKeyIterator() {} std::string name() const override { return codes_keys_iterator_get_name(it_.get()); }; /// Get the value of the key CodesValue get() const override { std::string key = name(); NativeType type = refHandle_.get().type(key); bool isArray = refHandle_.get().size(key) > 1; switch (type) { case NativeType::Long: if (isArray) { return getLongArray(); } else { return getLong(); } case NativeType::Double: if (isArray) { return getDoubleArray(); } else { return getDouble(); } case NativeType::String: if (isArray) { return getStringArray(); } else { return getString(); } case NativeType::Bytes: return getBytes(); case NativeType::Undefined: throw CodesException(std::string("IteratedKey::get(") + key + std::string("): Native type is UNDEFINED")); case NativeType::Section: case NativeType::Label: case NativeType::Missing: default: return getString(); } }; /// Get the type of the key NativeType type() const override { return refHandle_.get().type(name()); }; /// Explicit getters long getLong() const override { long value; size_t size = 1; throwOnError(codes_keys_iterator_get_long(it_.get(), &value, &size), Here(), "KeyIterator::getLong()"); return value; } double getDouble() const override { double value; size_t size = 1; throwOnError(codes_keys_iterator_get_double(it_.get(), &value, &size), Here(), "KeyIterator::getDouble()"); return value; } float getFloat() const override { float value; size_t size = 1; throwOnError(codes_keys_iterator_get_float(it_.get(), &value, &size), Here(), "KeyIterator::getFloat()"); return value; } std::string getString() const override { std::string ret; std::size_t keylen = 1024; ret.resize(keylen); throwOnError(codes_keys_iterator_get_string(it_.get(), ret.data(), &keylen), Here(), "KeyIterator::getString()"); ret.resize(strlen(ret.c_str())); return ret; } std::vector getLongArray() const override { std::vector ret; std::string key = name(); std::size_t size = refHandle_.get().size(key); ret.resize(size); throwOnError(codes_keys_iterator_get_long(it_.get(), ret.data(), &size), Here(), "KeyIterator::getLongArray()"); ret.resize(size); return ret; } std::vector getDoubleArray() const override { std::vector ret; std::string key = name(); std::size_t size = refHandle_.get().size(key); ret.resize(size); throwOnError(codes_keys_iterator_get_double(it_.get(), ret.data(), &size), Here(), "KeyIterator::getDoubleArray"); ret.resize(size); return ret; } std::vector getFloatArray() const override { std::vector ret; std::string key = name(); std::size_t size = refHandle_.get().size(key); ret.resize(size); throwOnError(codes_keys_iterator_get_float(it_.get(), ret.data(), &size), Here(), "KeyIterator::getFloatArray"); ret.resize(size); return ret; } std::vector getStringArray() const override { // There is no other option to get string array return refHandle_.get().getStringArray(name()); } std::vector getBytes() const override { std::vector ret; std::string key = name(); std::size_t size = refHandle_.get().length(key) / 2; ret.resize(size); throwOnError(codes_keys_iterator_get_bytes(it_.get(), ret.data(), &size), Here(), "KeyIterator::getBytes()"); ret.resize(size); return ret; } protected: void next() override { isValid_ = codes_keys_iterator_next(it_.get()) > 0; } bool isValid() const override { return isValid_; } private: std::reference_wrapper refHandle_; std::unique_ptr it_; bool isValid_; }; unsigned long mapFlags(KeyIteratorFlags flags) { unsigned long res = 0; if (hasFlag(flags, KeyIteratorFlags::AllKeys)) { res |= CODES_KEYS_ITERATOR_ALL_KEYS; } if (hasFlag(flags, KeyIteratorFlags::SkipReadOnly)) { res |= CODES_KEYS_ITERATOR_SKIP_READ_ONLY; } if (hasFlag(flags, KeyIteratorFlags::SkipOptional)) { res |= CODES_KEYS_ITERATOR_SKIP_OPTIONAL; } if (hasFlag(flags, KeyIteratorFlags::SkipEditionSpecific)) { res |= CODES_KEYS_ITERATOR_SKIP_EDITION_SPECIFIC; } if (hasFlag(flags, KeyIteratorFlags::SkipCoded)) { res |= CODES_KEYS_ITERATOR_SKIP_CODED; } if (hasFlag(flags, KeyIteratorFlags::SkipComputed)) { res |= CODES_KEYS_ITERATOR_SKIP_COMPUTED; } if (hasFlag(flags, KeyIteratorFlags::SkipDuplicates)) { res |= CODES_KEYS_ITERATOR_SKIP_DUPLICATES; } if (hasFlag(flags, KeyIteratorFlags::SkipFunction)) { res |= CODES_KEYS_ITERATOR_SKIP_FUNCTION; } return res; } /// Iterate keys on an iterator with a range based for loop KeyRange ConcreteCodesHandle::keys(KeyIteratorFlags flags, std::optional ns) const { return KeyRange{ std::make_unique(*this, std::unique_ptr(codes_keys_iterator_new( raw(), mapFlags(flags), ns ? ns->c_str() : NULL)))}; }; KeyRange ConcreteCodesHandle::keys(Namespace ns) const { return keys(KeyIteratorFlags::AllKeys, ns); }; class ConcreteIteratedGeoData : public GeoIterator { public: // methods ConcreteIteratedGeoData(const ConcreteCodesHandle& handle, std::unique_ptr it) : refHandle_{std::ref(handle)}, it_{std::move(it)}, data_{0.0, 0.0, 0.0}, isValid_{false} { if (hasNext()) { next(); } } virtual ~ConcreteIteratedGeoData() {} /// Returns the name of the key const GeoData& data() const override { return data_; }; bool hasNext() const override { return codes_grib_iterator_has_next(it_.get()) > 0; }; protected: void next() override { isValid_ = codes_grib_iterator_next(it_.get(), &data_.latitude, &data_.longitude, &data_.value) > 0; } bool isValid() const override { return isValid_; } private: std::reference_wrapper refHandle_; std::unique_ptr it_; GeoData data_; bool isValid_; }; /// Iterate ConcreteCodesHandle::values with longitude and latituted GeoRange ConcreteCodesHandle::values() const { int err; GeoRange res{std::make_unique( *this, std::unique_ptr(codes_grib_iterator_new(raw(), 0, &err)))}; throwOnError(err, Here(), "CodesHandle::values()"); return res; }; } // namespace std::string samplesPath() { return codes_samples_path(NULL); } std::string definitionPath() { return codes_definition_path(NULL); } long apiVersion() { return codes_get_api_version(); } std::string gitSha1() { return codes_get_git_sha1(); } std::string gitBranch() { return codes_get_git_branch(); } std::string buildDate() { return codes_get_build_date(); } std::string packageName() { return codes_get_package_name(); } std::unique_ptr codesHandleFromMessage(Span data) { return std::make_unique(std::unique_ptr( codes_handle_new_from_message(NULL, static_cast(data.data()), data.size()))); } std::unique_ptr codesHandleFromMessageCopy(Span data) { return std::make_unique(std::unique_ptr( codes_handle_new_from_message_copy(NULL, static_cast(data.data()), data.size()))); } std::unique_ptr codesHandleFromSample(const std::string& sampleName, std::optional product) { if (product) { switch (*product) { case Product::GRIB: return std::make_unique( std::unique_ptr(codes_grib_handle_new_from_samples(NULL, sampleName.c_str()))); case Product::BUFR: return std::make_unique( std::unique_ptr(codes_bufr_handle_new_from_samples(NULL, sampleName.c_str()))); default: return std::make_unique( std::unique_ptr(codes_handle_new_from_samples(NULL, sampleName.c_str()))); } } return std::make_unique( std::unique_ptr(codes_handle_new_from_samples(NULL, sampleName.c_str()))); } std::unique_ptr codesHandleFromFile(const std::string& fpath, Product product, std::optional offset) { int err = 0; std::unique_ptr ret; FILE* file = fopen(fpath.c_str(), "rb"); // "w" means write mode if (file == nullptr) { throw CodesException(std::string("Error opening file ") + fpath, Here()); } if (offset) { fseek(file, *offset, SEEK_SET); } switch (product) { case Product::GRIB: ret = std::unique_ptr(codes_grib_handle_new_from_file(NULL, file, &err)); break; case Product::BUFR: ret = std::unique_ptr(codes_bufr_handle_new_from_file(NULL, file, &err)); break; }; fclose(file); throwOnError(err, Here(), "codesHandleFromFile(FILE*, Product): "); if (!ret) { throw CodesException("codes_handle_new_from_file returned NULL without an additional error"); } return std::make_unique(std::move(ret)); } using ReadCBCtx = std::pair>, std::exception_ptr>; long readCallBack(void* ctx, void* buffer, long len) { auto& [func, eptr] = *static_cast(ctx); try { auto r = func.get()(static_cast(buffer), len); // API indicates EOF with 0. Codes expects -1 for EOF. return (r == 0) ? -1 : r; } catch (...) { // Capture exception for propagation and indicate error eptr = std::current_exception(); return -2; } } std::unique_ptr codesHandleFromStream(std::function readFunc) { int err = 0; ReadCBCtx ctx{readFunc, {}}; std::unique_ptr ret = std::unique_ptr(codes_handle_new_from_stream(NULL, &ctx, &readCallBack, &err)); if (ctx.second) { std::rethrow_exception(ctx.second); } throwOnError(err, Here(), "codesHandleFromStream(readFunc)"); if (ret) { return std::make_unique(std::move(ret)); } return {}; } std::unique_ptr codesHandleFromGRIBHandle(codes_handle* gribHandle) { if (gribHandle) { return std::make_unique(gribHandle); } return {}; } } // namespace metkit::codes metkit-1.19.2/src/metkit/codes/api/GeoIterator.h0000664000175000017500000000530115232567717021632 0ustar alastairalastair/* * (C) Copyright 2025- 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. */ #pragma once #include "metkit/codes/api/CodesTypes.h" #include #include #include #include #include namespace metkit::codes { //---------------------------------------------------------------------------------------------------------------------- class GeoRange; /// Aggregate that stores a value with lon/lat. struct GeoData { double value; double longitude; double latitude; }; /// Abstract interface wrapping C API calls on on key_iterator. class GeoIterator { friend class GeoRange; public: virtual ~GeoIterator() = default; /// Access to the iterated data. /// @return Reference to the iterated geo data object. virtual const GeoData& data() const = 0; /// Check if there are follow up values. /// @return True if there are more values to iterate on. virtual bool hasNext() const = 0; protected: /// Iterates the next element. virtual void next() = 0; /// Check if the iterator is valid. /// @return True if the iterator is still valid and holds a value. False indicates end of iteration. virtual bool isValid() const = 0; }; //---------------------------------------------------------------------------------------------------------------------- /// Implements the geo iterator (codes_iterator) based on a C++ range. /// Enables use of range-based for-loop. class GeoRange { public: struct EndIterator; struct Iterator { friend struct EndIterator; Iterator& operator++() { state->next(); return *this; } const GeoData& operator*() const { return state->data(); } const GeoData* operator->() const { return &state->data(); } inline bool operator!=(const EndIterator&) { return state->isValid(); } GeoIterator* state; }; struct EndIterator { inline bool operator!=(const Iterator& it) { return it.state->isValid(); } }; Iterator begin() const { return Iterator{impl_.get()}; }; EndIterator end() const { return {}; } GeoRange(std::unique_ptr impl) : impl_{std::move(impl)} {}; private: std::unique_ptr impl_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::codes metkit-1.19.2/src/metkit/codes/api/KeyIterator.h0000664000175000017500000001353415232567717021657 0ustar alastairalastair/* * (C) Copyright 2025- 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. */ #pragma once #include "metkit/codes/api/CodesTypes.h" #include #include #include namespace metkit::codes { /// A namepspace is defined through the definition files and represented as a string. using Namespace = std::string; /// Commonly used eccodes namespacess. namespace namespaces { /// LS namespace contains a collection of descriptive keys as used by `grib_ls`. const Namespace ls{"ls"}; const Namespace parameter{"parameter"}; const Namespace statistics{"statistics"}; const Namespace time{"time"}; const Namespace geography{"geography"}; const Namespace vertical{"vertical"}; /// MARS namespace contains keys used for indexation const Namespace mars{"mars"}; }; // namespace namespaces /// Flag to control/filter key iterator. /// /// Use `AllKeys` to disable filtering. /// Use combination of `Skip*` to filter. enum class KeyIteratorFlags : unsigned int { /// Default - Will iterate all keys in a namespace. AllKeys = 0, SkipReadOnly = 1 << 0, SkipOptional = 1 << 1, SkipEditionSpecific = 1 << 2, SkipCoded = 1 << 3, SkipComputed = 1 << 4, SkipDuplicates = 1 << 5, SkipFunction = 1 << 6, }; KeyIteratorFlags operator|(KeyIteratorFlags a, KeyIteratorFlags b); KeyIteratorFlags operator&(KeyIteratorFlags a, KeyIteratorFlags b); KeyIteratorFlags operator^(KeyIteratorFlags a, KeyIteratorFlags b); KeyIteratorFlags operator~(KeyIteratorFlags a); /// Optional helper to check if a flag is set. bool hasFlag(KeyIteratorFlags value, KeyIteratorFlags flag); //---------------------------------------------------------------------------------------------------------------------- class KeyRange; /// Abstract interface wrapping C API calls on on key_iterator. class KeyIterator { friend class KeyRange; public: virtual ~KeyIterator() = default; /// Get the name of the iterated key. /// @return Name of the iterated key. virtual std::string name() const = 0; /// Get the value of the key. /// /// High-level functionality: /// Inspection on the contained valued is performed with `type` and `size`. /// Then the more specific `getXXX` call is performed. /// @return Variant of all possible value types. virtual CodesValue get() const = 0; /// Get the type of the iterated key. /// @return Type of the iterated key. virtual NativeType type() const = 0; /// Get the contained value for a key as long. /// @return Retrieved value contained for the iterated key. virtual long getLong() const = 0; /// Get the contained value for a key as double. /// @return Retrieved value contained for the iterated key. virtual double getDouble() const = 0; /// Get the contained value for a key as float. /// @return Retrieved value contained for the iterated key. virtual float getFloat() const = 0; /// Get the contained value for a key as string. /// /// This should be possible for all key types. /// @return Retrieved value contained for the iterated key. virtual std::string getString() const = 0; /// Get the contained values for a key as array of long. /// @return Retrieved values contained for the iterated key. virtual std::vector getLongArray() const = 0; /// Get the contained values for a key as array of double. /// @return Retrieved values contained for the iterated key. virtual std::vector getDoubleArray() const = 0; /// Get the contained values for a key as array of float. /// @return Retrieved values contained for the iterated key. virtual std::vector getFloatArray() const = 0; /// Get the contained values for a key as array of string. /// @return Retrieved values contained for the iterated key. virtual std::vector getStringArray() const = 0; /// Get the contained values for a key as array of uint8_t (bytes). /// @return Retrieved values contained for the iterated key. virtual std::vector getBytes() const = 0; protected: /// Iterates the next element. virtual void next() = 0; /// Check if the iterator is valid. /// @return True if the iterator is still valid and holds a value. False indicates end of iteration. virtual bool isValid() const = 0; }; //---------------------------------------------------------------------------------------------------------------------- /// Implements the key iterator (codes_key_iterator) based on a C++ range. /// Enables use of range-based for-loop. class KeyRange { public: struct EndIterator; struct Iterator { friend struct EndIterator; Iterator& operator++() { state->next(); return *this; }; const KeyIterator& operator*() const { return *state; }; const KeyIterator* operator->() const { return state; }; inline bool operator!=(const KeyRange::EndIterator&) { return state->isValid(); } KeyIterator* state; }; struct EndIterator { inline bool operator!=(const KeyRange::Iterator& it) { return it.state->isValid(); } }; Iterator begin() const { return Iterator{impl_.get()}; }; EndIterator end() const { return {}; }; KeyRange(std::unique_ptr impl) : impl_{std::move(impl)} {}; private: std::unique_ptr impl_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::codes metkit-1.19.2/src/metkit/codes/api/CodesAPI.h0000664000175000017500000004577315232567717021016 0ustar alastairalastair/* * (C) Copyright 2025- 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. */ #pragma once #include "metkit/codes/api/CodesTypes.h" #include "metkit/codes/api/GeoIterator.h" #include "metkit/codes/api/KeyIterator.h" #include "eccodes.h" #include #include #include #include #include #include /// This file contains the important parts for an eccodes c api wrapper. /// It does not wrap the whole `eccodes.h`. /// The most important interface is the `CodesHandle` - it use to work on specific GRIB or BUFR messages. /// An addition to that, a few factory functions (`codesHandleFromMessage`, `codesHandleFromSample`, /// `codesHandleFromFile`, `codesHandleFromStream`) and iterators are wrapped. /// /// Missing components: /// * codes_context /// * codes_index /// * codes_fieldset /// * codes_values /// * multio_handle /// * codes_nearest /// * GTS handling /// * GRIBEX namespace metkit::codes { //---------------------------------------------------------------------------------------------------------------------- /// Retrieve information about the configured samples path. /// @return The configured path to sample files. std::string samplesPath(); /// Retrieve information about the configured definition path. /// @return The configured path to the definition files. std::string definitionPath(); /// Retrieve eccodes version /// @return The eccodes version. long apiVersion(); /// Retrieve repository information of the built. /// @return The SHA1 of the latest commit the eccodes library has been built on. std::string gitSha1(); /// Retrieve repository information of the built. /// @return The branch the eccodes library has been built on. std::string gitBranch(); /// Retrieve repository information of the built. /// @return The built date of the eccodes library. std::string buildDate(); /// Retrieve repository information of the built. /// @return The package name the library has been distributed with. std::string packageName(); //---------------------------------------------------------------------------------------------------------------------- /// Abstract interface wrapping eccodes handles. /// /// Eccodes exposed `codes_handle*` is used to handle GRIB1, GRIB2 and BUFR data. class CodesHandle { friend class KeyRange; friend class GeoRange; public: virtual ~CodesHandle() = default; /// Check if a key is defined. /// @param key Name of the field that is checked to be defined. /// @return True if the field is defined. virtual bool isDefined(const std::string& key) const = 0; /// Check if a key is missing. /// @param key Name of the field that is checked to be missing. /// @return True if the field is missing. virtual bool isMissing(const std::string& key) const = 0; /// Check if a key is defined and not missing. /// @param key Name of the field that is checked to exist and have a value. /// @return True if the field exists and has a value. virtual bool has(const std::string& key) const = 0; /// Set a key to its missing value. /// @param key Name of the field that is supposed to be set to its missing value. virtual void setMissing(const std::string& key) = 0; /// Set scalar string value for a specific field. /// @param key Name of the field that is supposed to be set. /// @param value Value the field is supposed to be set to. virtual void set(const std::string& key, const std::string& value) = 0; /// Set scalar double value for a specific field. /// @param key Name of the field that is supposed to be set. /// @param value Value the field is supposed to be set to. virtual void set(const std::string& key, double value) = 0; /// Set scalar long value for a specific field. /// @param key Name of the field that is supposed to be set. /// @param value Value the field is supposed to be set to. virtual void set(const std::string& key, long value) = 0; /// Set scalar floating point value for a specific field. /// /// Overload for other floating point types. /// @param key Name of the field that is supposed to be set. /// @param value Value the field is supposed to be set to. template && std::is_floating_point_v, bool> = true> void set(const std::string& key, const T& v) { set(key, static_cast(v)); } /// Set scalar integral value for a specific field. /// /// Overload for other integral types. /// @param key Name of the field that is supposed to be set. /// @param value Value the field is supposed to be set on. template && std::is_integral_v, bool> = true> void set(const std::string& key, const T& v) { set(key, static_cast(v)); } /// Set array of string through `span` as general contiguous memory container. /// @param key Name of the field that is supposed to be set. /// @param value Contiguous array the field is supposed to be set to. virtual void set(const std::string& key, Span value) = 0; /// Set array of c-string through `span` as general contiguous memory container. /// @param key Name of the field that is supposed to be set. /// @param value Contiguous array the field is supposed to be set to. virtual void set(const std::string& key, Span value) = 0; /// Set array of double through `span` as general contiguous memory container. /// @param key Name of the field that is supposed to be set. /// @param value Contiguous array the field is supposed to be set to. virtual void set(const std::string& key, Span value) = 0; /// Set array of float through `span` as general contiguous memory container. /// @param key Name of the field that is supposed to be set. /// @param value Contiguous array the field is supposed to be set to. virtual void set(const std::string& key, Span value) = 0; /// Set array of long through `span` as general contiguous memory container. /// @param key Name of the field that is supposed to be set. /// @param value Contiguous array the field is supposed to be set to. virtual void set(const std::string& key, Span value) = 0; /// Set array of uint8_t (bytes) through `span` as general contiguous memory container. /// @param key Name of the field that is supposed to be set. /// @param value Contiguous array the field is supposed to be set to. virtual void set(const std::string& key, Span value) = 0; /// Set any type of value as sum type for a specific field. /// Visits the value and calls the corresponding `set` overload. /// A template and SFINAE is used to match exactly the type `CodesValue`, /// this avoids abiguous overloads through implict convertion. /// @param key Name of the field that is supposed to be set. /// @param value Value the field is supposed to be set to. template , bool> = true> void set(const std::string& key, const T& value) { std::visit([&](const auto& v) { this->set(key, v); }, value); } /// Force setting arrays of double through `span` as general contiguous memory container. /// /// Allows setting arrays that usually get computed through other keys /// @param key Name of the field that is supposed to be set to a specific value. /// @param value Value the field is supposed to be set on. virtual void forceSet(const std::string& key, Span value) = 0; /// Force setting arrays of float through `span` as general contiguous memory container /// /// Allows setting arrays that usually get computed through other keys /// @param key Name of the field that is supposed to be set to a specific value. /// @param value Value the field is supposed to be set on. virtual void forceSet(const std::string& key, Span value) = 0; /// Returns the number of elements contained for a given key. /// /// Can be used to determine if a field is storing a scalar or an array. /// @param key Name of the field that is supposed to be inspected. /// @return For given scalars 1 is returned. For given arrays the size of the array is returned. virtual size_t size(const std::string& key) const = 0; /// Calls `string_length` on the accessor of a field and returns its value. /// /// @param key Name of the field that is supposed to be inspected. /// @return Value of the `string_length` method of the underlying accessor of the key. /// @see ecCodes documentation for more information. virtual size_t length(const std::string& key) const = 0; /// Get the value of the key. /// /// High-level functionality: /// Inspection on the contained valued is performed with `type` and `size`. /// Then the more specific `getXXX` call is performed. /// @param key Name of the field that is supposed to be retrieved. /// @return Variant of all possible value types. virtual CodesValue get(const std::string& key) const = 0; /// Get the type of the key /// @param key Name of the field that is supposed to be inspected. /// @return Enum of the native types ECCODES exposes. virtual NativeType type(const std::string& key) const = 0; /// Get the contained value for a key as long. /// @param key Name of the field that is supposed to be retrieved. /// @return Retrieved value contained for the passed key. virtual long getLong(const std::string& key) const = 0; /// Get the contained value for a key as double. /// @param key Name of the field that is supposed to be retrieved. /// @return Retrieved value contained for the passed key. virtual double getDouble(const std::string& key) const = 0; /// Get the contained value for a key as string. /// /// This should be possible for all key types. /// @param key Name of the field that is supposed to be retrieved. /// @return Retrieved value contained for the passed key. virtual std::string getString(const std::string& key) const = 0; /// Get the contained values for a key as array of long. /// @param key Name of the field that is supposed to be retrieved. /// @return Retrieved values contained for the passed key. virtual std::vector getLongArray(const std::string& key) const = 0; /// Get the contained values for a key as array of double. /// @param key Name of the field that is supposed to be retrieved. /// @return Retrieved values contained for the passed key. virtual std::vector getDoubleArray(const std::string& key) const = 0; /// Get the contained values for a key as array of float. /// @param key Name of the field that is supposed to be retrieved. /// @return Retrieved values contained for the passed key. virtual std::vector getFloatArray(const std::string& key) const = 0; /// Get the contained values for a key as array of string. /// /// This should be possible for all key types. /// @param key Name of the field that is supposed to be retrieved. /// @return Retrieved values contained for the passed key. virtual std::vector getStringArray(const std::string& key) const = 0; /// Get the contained values for a key as array of uint8_t (bytes). /// @param key Name of the field that is supposed to be retrieved. /// @return Retrieved values contained for the passed key. virtual std::vector getBytes(const std::string& key) const = 0; /// Clones the underyling handle. /// Uses `codes_handle_clone` internally. /// @return Unique pointer to a cloned `CodesHandle` instance. virtual std::unique_ptr clone() const = 0; /// Copy the message into a new allocated buffer /// @param data Pointer to an allocated array /// @param size Size of the allocated array. /// Should be containing at least the size returned by `messageSize`. virtual void copyInto(uint8_t* data, size_t size) const = 0; /// Retrieve binary size of the handled message. /// @return Size in bytes of the message. virtual size_t messageSize() const = 0; /// Return the pointer to the underlying buffer. /// @return Contiguous array to the underlying buffer as a span. /// No data is copied or owned, lifetime is bound to this handle. /// @see messageSize() /// @throws CodesException on any error returned from eccodes virtual Span messageData() const = 0; /// Iterate keys in a GRIB2 or BUFR handle. /// /// @param flags Iterator flags to filter keys /// @param ns A specific namespace that is iterated (e.g. "mars" or "ls"). /// Namespaces are defined within definition files, /// that means custom namespaces can be provided by users. /// Common namespaces that are available by the default definition files /// are listed in the namespace `codes::namespaces` /// @return A Range that can be iterated on with a range-based for-loop. /// @see KeyIterator virtual KeyRange keys(KeyIteratorFlags flags = KeyIteratorFlags::AllKeys, std::optional ns = std::optional{}) const = 0; /// Iterate keys in a GRIB2 or BUFR handle. /// /// @param ns A specific namespace that is iterated (e.g. "mars" or "ls"). /// Namespaces are defined within definition files, /// that means custom namespaces can be provided by users. /// Common namespaces that are available by the default definition files /// are listed in the namespace `codes::namespaces` /// @return A Range that can be iterated on with a range-based for-loop. /// @see KeyIterator virtual KeyRange keys(Namespace ns) const = 0; /// Iterate values with longitude and latituted. /// /// @return A Range that can be iterated on with a range-based for-loop. /// The iterated value contains a type `GeoData` /// with members `longitude`, `latitude` and `value`. /// @see GeoIterator virtual GeoRange values() const = 0; /// Release the underlying `codes_handle*`. /// /// After calling `release` the instance of this type is in an inoperable state. /// Used to pass ownership out of C++ (e.g. python). /// /// Example use: /// ```cpp /// auto rawHandle = reinterpret_cast(handl->release()); /// ``` /// /// @return Pointer to `codes_handle*` [[nodiscard]] virtual void* release() = 0; }; /// Create a new `CodesHandle` from a byte array. /// /// ECCODES does not copy the array until a modification is made. /// The user needs to maintain the lifetime of the passed array. /// @param data Array to an contiguous array of bytes. /// Lifetime of the array need to be maintained till the first change is made on the handle. /// @return Instance of a `CodesHandle` wrapped in a `unique_ptr`. /// @see codesHandleFromMessageCopy std::unique_ptr codesHandleFromMessage(Span data); /// Create a new `CodesHandle` from a byte array. /// /// ECCODES copies the array. No lifetime requirements exist on `data`. /// @param data Array to an contiguous array of bytes the message is created from. /// @return Instance of a `CodesHandle` wrapped in a `unique_ptr`. /// @see codesHandleFromMessage std::unique_ptr codesHandleFromMessageCopy(Span data); /// Create a new `CodesHandle` from a sample existing in the configured samples path. /// /// ECCODES does not copy the array until a modification is made. /// The user needs to maintain the lifetime of the passed array. /// @param sample Name of the sample in the sample path. Usually without the ".tmpl" suffix, e.g. "GRIB2". /// @param product The intented type of handle that is supposed to be loaded (BUFR or GRIB). /// Does not need to be specified. /// @return Instance of a `CodesHandle` wrapped in a `unique_ptr`. /// @see samplesPath std::unique_ptr codesHandleFromSample(const std::string& sampleName, std::optional product = std::optional{}); /// Create a new `CodesHandle` from a file. /// /// ECCODES does not copy the array until a modification is made. /// The user needs to maintain the lifetime of the passed array. /// @param file Pointer to a implementation dependent file handle containing a BUFR or GRIB message. /// @param product The intented type of handle that is supposed to be loaded (BUFR or GRIB). /// @param offset Optional: An offset in bytes within the file where the actual message starts. /// @return Instance of a `CodesHandle` wrapped in a `unique_ptr`. std::unique_ptr codesHandleFromFile(const std::string& fpath, Product, std::optional offset = std::optional{}); /// Create a new `CodesHandle` from a stream /// /// The user needs to maintain the lifetime of the passed array. /// @param readFunc Function that accepts a buffer as uint8_t* and its length as int64_t. /// The function can fill the buffer and return the number of bytes filled/consumed. /// Exceptions thrown in the function are propagated. /// A return value of 0 indicates an stream end (EOF) and will result in a nullptr without an error. /// A negative return value indicates an error - in that case it is smarter to throw an exception. /// @return Instance of a `CodesHandle` wrapped in a `unique_ptr`. Nullptr on stream end. /// @throws CodesWrongLength if the message is corrupted, CodesException on any other error /// returned from eccodes, any expected propagated from readFunc. std::unique_ptr codesHandleFromStream(std::function readFunc); /// Temporary workaround. /// /// This function is a transitional API introduced to unblock the current /// implementation. It is intentionally not part of the intended long-term /// design and should be removed as soon as the underlying workflow is /// refactored. /// /// The returned wrapper borrows `gribHandle` without taking ownership. /// The caller keeps ownership of `gribHandle` and needs to maintain its /// lifetime for the whole lifetime of the returned `CodesHandle`. /// @param gribHandle Pointer to a `codes_handle` that is a GRIB handle. /// @return Instance of a `CodesHandle` wrapped in a `std::unique_ptr`. std::unique_ptr codesHandleFromGRIBHandle(codes_handle* gribHandle); //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::codes metkit-1.19.2/src/metkit/codes/CodesDataContent.h0000664000175000017500000000436515232567717022030 0ustar alastairalastair/* * (C) Copyright 2017- 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. */ #pragma once #include "eckit/io/Offset.h" #include "metkit/codes/api/CodesAPI.h" #include "eckit/message/MessageContent.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- class CodesDataContent : public eckit::message::MessageContent { public: CodesDataContent(std::unique_ptr handle, eckit::Offset offset); CodesDataContent(std::unique_ptr handle); virtual ~CodesDataContent() = default; const CodesHandle& codesHandle() const; CodesHandle& codesHandle(); protected: std::unique_ptr handle_; eckit::Offset offset_; using eckit::message::MessageContent::transform; void transform(const eckit::OrderedStringDict&) override; private: size_t length() const override; void write(eckit::DataHandle& handle) const override; eckit::DataHandle* readHandle() const override; void print(std::ostream& s) const override; std::string getString(const std::string& key) const override; long getLong(const std::string& key) const override; double getDouble(const std::string& key) const override; void getDoubleArray(const std::string& key, std::vector& values) const override; void getFloatArray(const std::string& key, std::vector& values) const override; size_t getSize(const std::string& key) const override; void getDoubleArray(const std::string& key, double* data, size_t lenExpected) const override; void getFloatArray(const std::string& key, float* data, size_t lenExpected) const override; eckit::Offset offset() const override; const void* data() const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/codes/LibEccodes.h0000664000175000017500000000177615232567717020645 0ustar alastairalastair/* * (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. */ /// @author Olivier Iffrig /// @date October 2019 #pragma once #include "eckit/system/Library.h" namespace metkit { //---------------------------------------------------------------------------------------------------------------------- class LibEccodes : public eckit::system::Library { public: LibEccodes(); static const LibEccodes& instance(); protected: std::string version() const override; std::string gitsha1(unsigned int count) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit metkit-1.19.2/src/metkit/api/0000775000175000017500000000000015232567717016141 5ustar alastairalastairmetkit-1.19.2/src/metkit/api/metkit_c.cc0000664000175000017500000002621315232567717020253 0ustar alastairalastair#include "metkit_c.h" #include #include "eckit/runtime/Main.h" #include "metkit/mars/MarsExpansion.h" #include "metkit/mars/MarsRequest.h" #include "metkit/metkit_version.h" // --------------------------------------------------------------------------------------------------------------------- struct metkit_marsrequest_t : public metkit::mars::MarsRequest { using metkit::mars::MarsRequest::MarsRequest; metkit_marsrequest_t(metkit::mars::MarsRequest&& req) : metkit::mars::MarsRequest(std::move(req)) {} }; struct metkit_requestiterator_t { explicit metkit_requestiterator_t(std::vector&& vec) : vector_(std::move(vec)), current_(vector_.begin()) {} metkit_iterator_status_t next() { if (first_) { first_ = false; if (current_ != vector_.end()) { return METKIT_ITERATOR_SUCCESS; } } if (current_ == vector_.end()) { return METKIT_ITERATOR_COMPLETE; } ++current_; return current_ == vector_.end() ? METKIT_ITERATOR_COMPLETE : METKIT_ITERATOR_SUCCESS; } // Note: expected to call next() before calling current() metkit_iterator_status_t current(metkit_marsrequest_t* out) { if (first_ || current_ == vector_.end()) { return METKIT_ITERATOR_ERROR; } *out = std::move(*current_); return METKIT_ITERATOR_SUCCESS; } private: bool first_ = true; std::vector vector_; std::vector::iterator current_; }; // --------------------------------------------------------------------------------------------------------------------- /// @note: Unlike metkit_requestiterator_t, lifetime of the char* returned from this iterator are have the same as the /// iterator's lifetime. struct metkit_paramiterator_t { metkit_paramiterator_t(std::vector vec) : vector_(vec), current_(vector_.begin()) {} metkit_iterator_status_t next() { if (first_) { first_ = false; if (current_ != vector_.end()) { return METKIT_ITERATOR_SUCCESS; } } if (current_ == vector_.end()) { return METKIT_ITERATOR_COMPLETE; } ++current_; return current_ == vector_.end() ? METKIT_ITERATOR_COMPLETE : METKIT_ITERATOR_SUCCESS; } // Note: expected to call next() before calling current() metkit_iterator_status_t current(const char** out) const { if (first_ || current_ == vector_.end()) { return METKIT_ITERATOR_ERROR; } *out = current_->c_str(); return METKIT_ITERATOR_SUCCESS; } bool first_ = true; std::vector vector_; std::vector::const_iterator current_; }; // --------------------------------------------------------------------------------------------------------------------- // ERROR HANDLING static thread_local std::string g_current_error_string; const char* metkit_get_error_string(metkit_error_t err) { switch (err) { case METKIT_SUCCESS: return "Success"; case METKIT_ERROR: case METKIT_ERROR_USER: case METKIT_ERROR_ASSERT: return g_current_error_string.c_str(); default: return ""; } } metkit_error_t innerWrapFn(std::function f) { return f(); } metkit_error_t innerWrapFn(std::function f) { f(); return METKIT_SUCCESS; } template [[nodiscard]] metkit_error_t tryCatch(FN&& fn) { try { return innerWrapFn(std::forward(fn)); } catch (const eckit::UserError& e) { g_current_error_string = e.what(); return METKIT_ERROR_USER; } catch (const eckit::AssertionFailed& e) { g_current_error_string = e.what(); return METKIT_ERROR_ASSERT; } catch (const eckit::Exception& e) { g_current_error_string = e.what(); return METKIT_ERROR; } catch (const std::exception& e) { g_current_error_string = e.what(); return METKIT_ERROR_UNKNOWN; } catch (...) { return METKIT_ERROR_UNKNOWN; } } // ----------------------------------------------------------------------------- // HELPERS // ----------------------------------------------------------------------------- metkit_error_t metkit_initialise() { return tryCatch([] { static bool initialised = false; if (initialised) { eckit::Log::warning() << "Initialising Metkit library twice" << std::endl; } if (!initialised) { const char* argv[2] = {"metkit-api", nullptr}; eckit::Main::initialise(1, const_cast(argv)); initialised = true; } }); } // ----------------------------------------------------------------------------- // PARSING // ----------------------------------------------------------------------------- metkit_error_t metkit_parse_marsrequests(const char* str, metkit_requestiterator_t** requests, bool strict) { return tryCatch([requests, str, strict] { ASSERT(requests); ASSERT(str); std::istringstream in(str); *requests = new metkit_requestiterator_t(metkit::mars::MarsRequest::parse(in, strict)); }); } metkit_error_t metkit_parse_marsrequest(const char* str, metkit_marsrequest_t* request, bool strict) { return tryCatch([request, str, strict] { ASSERT(request); ASSERT(str); *request = metkit::mars::MarsRequest::parse(str, strict); }); } // ----------------------------------------------------------------------------- // REQUEST // ----------------------------------------------------------------------------- metkit_error_t metkit_marsrequest_new(metkit_marsrequest_t** request) { return tryCatch([request] { ASSERT(request); *request = new metkit_marsrequest_t(); }); } metkit_error_t metkit_marsrequest_delete(const metkit_marsrequest_t* request) { return tryCatch([request] { delete request; }); } metkit_error_t metkit_marsrequest_set(metkit_marsrequest_t* request, const char* param, const char* values[], int numValues) { return tryCatch([request, param, values, numValues] { ASSERT(request); ASSERT(param); ASSERT(values); std::string param_str(param); std::vector values_vec; values_vec.reserve(numValues); std::copy(values, values + numValues, std::back_inserter(values_vec)); request->values(param_str, values_vec); }); } metkit_error_t metkit_marsrequest_set_one(metkit_marsrequest_t* request, const char* param, const char* value) { return metkit_marsrequest_set(request, param, &value, 1); } metkit_error_t metkit_marsrequest_set_verb(metkit_marsrequest_t* request, const char* verb) { return tryCatch([request, verb] { ASSERT(request); ASSERT(verb); request->verb(verb); }); } metkit_error_t metkit_marsrequest_verb(const metkit_marsrequest_t* request, const char** verb) { return tryCatch([request, verb] { ASSERT(request); ASSERT(verb); *verb = request->verb().c_str(); }); } metkit_error_t metkit_marsrequest_has_param(const metkit_marsrequest_t* request, const char* param, bool* has) { return tryCatch([request, param, has] { ASSERT(request); ASSERT(param); ASSERT(has); *has = request->has(param); }); } metkit_error_t metkit_marsrequest_params(const metkit_marsrequest_t* request, metkit_paramiterator_t** params) { return tryCatch([request, params] { ASSERT(request); ASSERT(params); *params = new metkit_paramiterator_t(request->params()); }); } metkit_error_t metkit_marsrequest_count_values(const metkit_marsrequest_t* request, const char* param, size_t* count) { return tryCatch([request, param, count] { ASSERT(request); ASSERT(param); ASSERT(count); *count = request->countValues(param); }); } metkit_error_t metkit_marsrequest_value(const metkit_marsrequest_t* request, const char* param, int index, const char** value) { return tryCatch([request, param, index, value] { ASSERT(request); ASSERT(param); ASSERT(value); *value = request->values(param, false)[index].c_str(); }); } metkit_error_t metkit_marsrequest_expand(const metkit_marsrequest_t* request, bool inherit, bool strict, metkit_marsrequest_t* expandedRequest) { return tryCatch([request, expandedRequest, inherit, strict] { ASSERT(request); ASSERT(expandedRequest); ASSERT(expandedRequest->empty()); metkit::mars::MarsExpansion expand(inherit, strict); *expandedRequest = expand.expand(*request); }); } metkit_error_t metkit_marsrequest_merge(metkit_marsrequest_t* request, const metkit_marsrequest_t* otherRequest) { return tryCatch([request, otherRequest] { ASSERT(request); ASSERT(otherRequest); request->merge(*otherRequest); }); } void metkit_string_delete(const char* str) { delete[] str; } // ----------------------------------------------------------------------------- // REQUEST ITERATOR // ----------------------------------------------------------------------------- metkit_error_t metkit_requestiterator_delete(const metkit_requestiterator_t* it) { return tryCatch([it] { delete it; }); } metkit_iterator_status_t metkit_requestiterator_next(metkit_requestiterator_t* it) { if (!it) { return METKIT_ITERATOR_ERROR; } return it->next(); } metkit_iterator_status_t metkit_requestiterator_current(metkit_requestiterator_t* it, metkit_marsrequest_t* request) { if (!it || !request || !request->empty()) { return METKIT_ITERATOR_ERROR; } return it->current(request); } // ----------------------------------------------------------------------------- // PARAM ITERATOR // ----------------------------------------------------------------------------- metkit_error_t metkit_paramiterator_delete(const metkit_paramiterator_t* it) { return tryCatch([it] { delete it; }); } metkit_iterator_status_t metkit_paramiterator_next(metkit_paramiterator_t* it) { if (!it) { return METKIT_ITERATOR_ERROR; } return it->next(); } metkit_iterator_status_t metkit_paramiterator_current(const metkit_paramiterator_t* it, const char** param) { if (!it || !param) { return METKIT_ITERATOR_ERROR; } return it->current(param); } // --------------------------------------------------------------------------------------------------------------------- // Bridge between C and C++ const metkit::mars::MarsRequest& metkit::mars::MarsRequest::fromOpaque(const metkit_marsrequest_t* request) { return *static_cast(request); } // --------------------------------------------------------------------------------------------------------------------- metkit-1.19.2/src/metkit/api/metkit_c.h0000664000175000017500000002261715232567717020121 0ustar alastairalastair#pragma once #include #include #ifdef __cplusplus extern "C" { #endif /* --------------------------------------------------------------------------------------------------------------------- * TYPES * -----*/ struct metkit_marsrequest_t; typedef struct metkit_marsrequest_t metkit_marsrequest_t; struct metkit_requestiterator_t; /** RequestIterator for iterating over vector of Request instances */ typedef struct metkit_requestiterator_t metkit_requestiterator_t; struct metkit_paramiterator_t; /** Iterator for iterating over parameters in Request */ typedef struct metkit_paramiterator_t metkit_paramiterator_t; /* --------------------------------------------------------------------------------------------------------------------- * ERROR HANDLING * -------------- */ typedef enum metkit_error_values_t { METKIT_SUCCESS = 0, /* Operation succeded. */ METKIT_ERROR = 1, /* Operation failed. */ METKIT_ERROR_UNKNOWN = 2, /* Failed with an unknown error. */ METKIT_ERROR_USER = 3, /* Failed with an user error. */ METKIT_ERROR_ASSERT = 4 /* Failed with an assert() */ } metkit_error_t; typedef enum metkit_iterator_status_t { METKIT_ITERATOR_SUCCESS = 0, /* Operation succeded. */ METKIT_ITERATOR_COMPLETE = 1, /* All elements have been returned */ METKIT_ITERATOR_ERROR = 2 /* Operation failed. */ } metkit_iterator_status_t; const char* metkit_get_error_string(enum metkit_error_values_t err); /* ----------------------------------------------------------------------------- * HELPERS * ------- */ /** * @brief Get metkit version. * * @return const char* version string */ const char* metkit_version(); /** * @brief Get metkit git sha1 version. * * @return const char* git sha1 version string */ const char* metkit_git_sha1(); /** * @brief Initialise Main() context. * * @note This is ONLY required when Main() is NOT initialised, such as loading * the MetKit as shared library in Python. * @return metkit_error_t Error code */ metkit_error_t metkit_initialise(); /* --------------------------------------------------------------------------------------------------------------------- * PARSING * --- */ /** * Parse MARS requests into RequestIterator of Request instances. Resulting RequestIterator * must be deallocated with metkit_delete_requestiterator * @param str MARS requests * @param[out] requests Allocates RequestIterator object * @return metkit_error_t Error code */ metkit_error_t metkit_parse_marsrequests(const char* str, metkit_requestiterator_t** requests, bool strict); /** * Parse MARS request into Request instance * @note will error if request expands to multiple requests: use metkit_parse_marsrequests instead * @param str MARS request * @param[out] request Request instance * @param strict if true, raise error rather than warning on invalid values * @return metkit_error_t Error code */ metkit_error_t metkit_parse_marsrequest(const char* str, metkit_marsrequest_t* request, bool strict); /* --------------------------------------------------------------------------------------------------------------------- * REQUEST * --- */ /** Allocates new Request object. Must be deallocated with mekit_delete_request * @param[out] request new Request instance * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_new(metkit_marsrequest_t** request); /** Deallocates Request object and associated resources. * @param request Request instance * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_delete(const metkit_marsrequest_t* request); /** Add parameter and values to request * @param request Request instance * @param param parameter name * @param values array of values for parameter * @param numValues number of values * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_set(metkit_marsrequest_t* request, const char* param, const char* values[], int numValues); /** Add parameter and values to request * @param request Request instance * @param param parameter name * @param values value to add * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_set_one(metkit_marsrequest_t* request, const char* param, const char* value); /** Set verb in Request object * @param request Request instance * @param verb verb to set * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_set_verb(metkit_marsrequest_t* request, const char* verb); /** Returns the verb in Request object * @param request Request instance * @param[out] verb verb in request * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_verb(const metkit_marsrequest_t* request, const char** verb); /** Returns whether parameter is in Request object * @param request Request instance * @param param parameter name * @param[out] has whether parameter exists in request * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_has_param(const metkit_marsrequest_t* request, const char* param, bool* has); /** Returns parameter iterator for Request object * Must be deallocated with metkit_paramiterator_delete * @note: The strings obtained from next() are owned by the iterator and should be copied if they need to outlive it. * @param request Request instance * @param[out] params parameter iterator * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_params(const metkit_marsrequest_t* request, metkit_paramiterator_t** params); /** Returns number of values for specific parameter in Request object * @param request Request instance * @param param parameter name in request * @param[out] count number of values for param in request * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_count_values(const metkit_marsrequest_t* request, const char* param, size_t* count); /** Returns value for specific parameter and index in Request object * @param request Request instance * @param param parameter name in request * @param index index of value to retrieve for param in request * @param[out] value retrieved value * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_value(const metkit_marsrequest_t* request, const char* param, int index, const char** value); /** Populates empty Request object by expanding existing request * @param request Request instance to be expanded * @param inherit if true, populate expanded request with default values * @param strict it true, raise error rather than warning on invalid values * @param[out] expandedRequest empty Request instance to be populated * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_expand(const metkit_marsrequest_t* request, bool inherit, bool strict, metkit_marsrequest_t* expandedRequest); /** Merges other Request object into existing request * @param request Request instance to contain result of merge * @param otherRequest other Request instance to merge * @return metkit_error_t Error code */ metkit_error_t metkit_marsrequest_merge(metkit_marsrequest_t* request, const metkit_marsrequest_t* otherRequest); /* --------------------------------------------------------------------------------------------------------------------- * REQUEST ITERATOR * --- */ /** Deallocates RequestIterator object and associated resources. * @param it RequestIterator instance * @return metkit_error_t Error code */ metkit_error_t metkit_requestiterator_delete(const metkit_requestiterator_t* it); /** Moves to the next Request element in RequestIterator * @param it RequestIterator instance * @return metkit_iterator_status_t Status of iterator */ // metkit_error_t metkit_requestiterator_next(metkit_requestiterator_t* it); metkit_iterator_status_t metkit_requestiterator_next(metkit_requestiterator_t* it); /** Populates empty Request object with data from current element in RequestIterator * @param it RequestIterator instance * @param request empty Request instance to populate with data * @return metkit_iterator_status_t Status of iterator * @note must call metkit_requestiterator_next before calling metkit_requestiterator_current. * * Example usage: * while (metkit_requestiterator_next(it) == METKIT_ITERATOR_SUCCESS) { * metkit_marsrequest_t* req{}; * metkit_marsrequest_new(&req); * metkit_requestiterator_current(it, req); * // use req ... * } */ metkit_iterator_status_t metkit_requestiterator_current(metkit_requestiterator_t* it, metkit_marsrequest_t* request); /* --------------------------------------------------------------------------------------------------------------------- * PARAMETER ITERATOR * --- */ /** Deallocates ParamIterator object and the char* strings it owns. * @param it ParamIterator instance * @return metkit_error_t Error code */ metkit_error_t metkit_paramiterator_delete(const metkit_paramiterator_t* it); /** Moves to the next parameter in ParamIterator * @param it ParamIterator instance * @return metkit_iterator_status_t Status of iterator */ metkit_iterator_status_t metkit_paramiterator_next(metkit_paramiterator_t* it); /** Returns current parameter in ParamIterator * @param it ParamIterator instance * @param[out] param current parameter * @return metkit_iterator_status_t Status of iterator */ metkit_iterator_status_t metkit_paramiterator_current(const metkit_paramiterator_t* it, const char** param); #ifdef __cplusplus } #endif metkit-1.19.2/src/metkit/mars2mars/0000775000175000017500000000000015232567717017277 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2mars/utils/0000775000175000017500000000000015232567717020437 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2mars/utils/paramMatcher.h0000664000175000017500000000257615232567717023226 0ustar alastairalastair/* * (C) Copyright 2026- 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 paramMatcher.h /// @brief Helpers for compact parameter matching in rule tables. #pragma once namespace metkit::mars2mars::util::param_matcher { /// @brief Inclusive integer range used by parameter predicates. struct Range { int first; int last; /// @brief Check whether a value lies inside the range. bool contains(int x) const { return x >= first && x <= last; } }; /// @brief Construct an inclusive integer range. inline Range range(int first, int last) { return {first, last}; } /// @brief Match a value against a range argument. inline bool matchSingle(int x, const Range& arg) { return arg.contains(x); } /// @brief Match a value against a scalar argument. inline bool matchSingle(int x, int y) { return x == y; } /// @brief Match a value against any supplied argument. template bool matchAny(int value, T... arg) { return (matchSingle(value, arg) || ...); } } // namespace metkit::mars2mars::util::param_matcher metkit-1.19.2/src/metkit/mars2mars/utils/logUtils.h0000664000175000017500000000534015232567717022414 0ustar alastairalastair/// @file logUtils.h /// @brief Logging macros used by mars2mars. #pragma once #include "eckit/log/Log.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2mars/utils/generalUtils.h" /// @brief Log a validation or guard check. #define MARS2MARS_LOG_CHECK(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2MARS-CHECK: " << msg << std::endl; \ } while (0) /// @brief Log a successful match. #define MARS2MARS_LOG_MATCH(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2MARS-MATCH: " << msg << std::endl; \ } while (0) /// @brief Log a resolved value. #define MARS2MARS_LOG_RESOLVE(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2MARS-RESOLVE: " << msg << std::endl; \ } while (0) /// @brief Log an overridden value. #define MARS2MARS_LOG_OVERRIDE(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2MARS-OVERRIDE: " << msg << std::endl; \ } while (0) /// @brief Log a defaulted value. #define MARS2MARS_LOG_DEFAULT(msg) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2MARS-DEFAULT: " << msg << std::endl; \ } while (0) /// @brief Log a concept invocation. #define MARS2MARS_LOG_CONCEPT(CONCEPTNAME) \ do { \ LOG_DEBUG_LIB(LibMetkit) << " MARS2MARS-CONCEPT:" \ << "[Concept " << std::string(CONCEPTNAME##Name) << "] " \ << "Op called: " \ << "Stage=" << Stage << ", " \ << "Section=" << Section << ", " \ << "Variant=" << std::string(CONCEPTNAME##TypeName()) << std::endl; \ } while (0) metkit-1.19.2/src/metkit/mars2mars/utils/mars2marsExceptions.h0000664000175000017500000001303515232567717024563 0ustar alastairalastair/* * (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 mars2marsExceptions.h /// @brief Unified exception hierarchy for the mars2mars framework. /// /// This header defines the complete exception model used across /// mars2mars, covering: /// /// - Generic infrastructure errors /// - Layer-specific failures (matcher, rules, validation, tables, deduction) /// - Concept execution failures (with contextual metadata) /// - Encoder failures (with serialized dictionary state) /// /// The hierarchy is designed with the following goals: /// /// - Strong contextual diagnostics /// - Support for nested exception propagation /// - Structured debug frame printing /// - Clear separation between backend and frontend layers /// /// All exceptions ultimately derive from `eckit::Exception`, /// ensuring compatibility with the broader ECMWF ecosystem. /// /// Nested exception support allows propagation chains to be /// printed in a structured stack-like format. /// /// @ingroup mars2mars_utils #pragma once // System includes #include #include #include #include // Project includes #include "eckit/exception/Exceptions.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars2mars/utils/generalUtils.h" namespace metkit::mars2mars::utils::exceptions { /// @brief Base exception for mars2mars. /// /// This is the root exception type for most mars2mars failures. /// It: /// /// - Inherits from `eckit::Exception` /// - Supports nested exceptions via `std::nested_exception` /// - Provides structured frame printing /// /// Derived exceptions typically extend this class with /// additional contextual metadata. /// /// The `printFrame()` method is designed to be used by /// extended stack printers. class Mars2marsGenericException : public eckit::Exception, public std::nested_exception { public: Mars2marsGenericException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : eckit::Exception(reason, loc) {} virtual ~Mars2marsGenericException() = default; virtual void printFrame(const std::string& pad, std::ostream& os) const { const auto& loc = location(); os << pad << "+ file: " << loc.file() << "\n" << pad << "+ function: " << loc.func() << "\n" << pad << "+ line: " << loc.line() << "\n" << pad << "+ link: " << loc.file() << ":" << loc.line() << "\n" << pad << "+ message: " << what() << "\n"; } }; /// @brief Exception raised in the rules layer. /// /// Used when evaluating rule-based logic fails. /// /// This class derives directly from `eckit::Exception` and /// supports nested exceptions. class Mars2marsRulesException : public eckit::Exception, public std::nested_exception { public: Mars2marsRulesException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : eckit::Exception(reason, loc) {} }; /// @brief Exception raised in the dictionary access layer. /// /// Used when dictionary validation or access fails. /// Inherits structured printing from the generic exception. class Mars2marsDictException : public Mars2marsGenericException { public: Mars2marsDictException(std::string reason, const eckit::CodeLocation& loc = eckit::CodeLocation()) : Mars2marsGenericException(reason, loc) {} }; inline constexpr int tabSize = 4; inline constexpr int lineSize = 120; inline std::string indent(std::size_t level) { return std::string(level * tabSize, ' '); } /// @brief Print structured exception stack with detailed frames. /// /// For each nested exception frame: /// /// - Prints file, function, line, and message (if available) /// - Prints additional metadata for specialized exceptions /// /// This function detects `Mars2marsGenericException` /// via `dynamic_cast` and calls `printFrame()` /// to extract structured information. /// /// Nested exceptions are recursively printed. /// /// @param e Root exception /// @param level Indentation level /// @param frame Frame counter inline void printExtendedStack(const std::exception& e, std::ostream& os, std::size_t level = 0, std::size_t frame = 1) { const std::string pad = indent(level); os << pad << "+ " << std::string(lineSize, '=') << std::endl << pad << "+ frame " << frame << std::endl << pad << "+ " << std::string(lineSize, '-') << std::endl; if (const auto* me = dynamic_cast(&e)) { me->printFrame(pad, os); } else { os << pad << "+ message: " << e.what() << std::endl; } os << pad << "+ " << std::string(lineSize, '+') << std::endl; const auto* nested = dynamic_cast(&e); if (nested == nullptr) { return; } const std::exception_ptr nestedPtr = nested->nested_ptr(); if (!nestedPtr) { return; } try { std::rethrow_exception(nestedPtr); } catch (const std::exception& nestedException) { printExtendedStack(nestedException, os, level + 1, frame + 1); } catch (...) { os << std::string(level + 1, ' ') << "[nested non-std exception]" << std::endl; } } } // namespace metkit::mars2mars::utils::exceptions metkit-1.19.2/src/metkit/mars2mars/utils/generalUtils.h0000664000175000017500000000027715232567717023254 0ustar alastairalastair/// @file generalUtils.h /// @brief Small utility macros shared by mars2mars. #pragma once /// @brief Mark a code path as unreachable. #define mars2marsUnreachable() __builtin_unreachable() metkit-1.19.2/src/metkit/mars2mars/utils/type_traits_name.h0000664000175000017500000001057515232567717024167 0ustar alastairalastair/* * (C) Copyright 2026- 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 type_traits_name.h /// @brief Human-readable type names used in diagnostics. #pragma once #include #include #include #include #include #include #include "metkit/codes/api/CodesTypes.h" #include "metkit/mars2mars/utils/generalUtils.h" namespace metkit::mars2mars::utils { /// @brief Return a readable name for `T`. // ============================================================ // Primary template (deferred to fallback demangling) // ============================================================ template constexpr std::string_view type_name(); // ============================================================ // Base type specializations // ============================================================ template <> constexpr std::string_view type_name() { return "string"; } template <> constexpr std::string_view type_name() { return "bool"; } template <> constexpr std::string_view type_name() { return "char"; } template <> constexpr std::string_view type_name() { return "signed char"; } template <> constexpr std::string_view type_name() { return "unsigned char"; } template <> constexpr std::string_view type_name() { return "int"; } template <> constexpr std::string_view type_name() { return "long"; } template <> constexpr std::string_view type_name() { return "long long"; } template <> constexpr std::string_view type_name() { return "unsigned int"; } template <> constexpr std::string_view type_name() { return "unsigned long"; } template <> constexpr std::string_view type_name() { return "unsigned long long"; } template <> constexpr std::string_view type_name() { return "float"; } template <> constexpr std::string_view type_name() { return "double"; } template <> constexpr std::string_view type_name() { return "long double"; } // ============================================================ // std::vector specializations // ============================================================ template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector"; } template <> constexpr std::string_view type_name>() { return "vector_view"; } template <> constexpr std::string_view type_name>() { return "vector_view"; } } // namespace metkit::mars2mars::utils metkit-1.19.2/src/metkit/mars2mars/utils/dictionary_traits/0000775000175000017500000000000015232567717024172 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2mars/utils/dictionary_traits/dictaccess_eckit_configuration.h0000664000175000017500000005074415232567717032570 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include #include #include #include #include "eckit/config/LocalConfiguration.h" #include "metkit/mars2mars/utils/generalUtils.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/type_traits_name.h" // ============================================================================ // Helper macros to reduce boilerplate (similar to LocalConfiguration version) // ============================================================================ // ============================================================== // GET_OR_THROW // ============================================================== #define M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(CTYPE, ISFUNC, GETFUNC) \ template <> \ struct DictGetOrThrow { \ \ static CTYPE get_or_throw(const eckit::LocalConfiguration& cfg, std::string_view key) noexcept(false) { \ const std::string k{key}; \ \ try { \ /* Check key exists */ \ if (!cfg.has(k)) { \ throw exceptions::Mars2marsDictException("Missing key `"s + k + "` in dictionary type `"s + \ std::string(type_name()) + \ "`", \ Here()); \ } \ \ /* Check type */ \ if (hacks::ISFUNC(cfg, k)) { \ return cfg.GETFUNC(k); \ } \ else { \ throw exceptions::Mars2marsDictException( \ "Key `"s + k + "` is not of expected type `"s + std::string(type_name()) + \ "` for dictionary type `"s + std::string(type_name()) + "`", \ Here()); \ } \ } \ catch (const exceptions::Mars2marsGenericException&) { \ throw; \ } \ catch (...) { \ std::throw_with_nested(exceptions::Mars2marsDictException( \ "Internal error while reading key `"s + k + "` as `" + std::string(type_name()) + \ "` from dictionary type `"s + std::string(type_name()) + "`", \ Here())); \ } \ mars2marsUnreachable(); \ } \ }; // ============================================================== // GET_OPT // ============================================================== #define M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(CTYPE, ISFUNC, GETFUNC) \ template <> \ struct DictGetOpt { \ static std::optional get_opt(const eckit::LocalConfiguration& cfg, \ std::string_view key) noexcept(false) { \ const std::string k{key}; \ \ try { \ if (!cfg.has(k)) { \ return std::nullopt; \ } \ \ if (hacks::ISFUNC(cfg, k)) { \ return cfg.GETFUNC(k); \ } \ else { \ return std::nullopt; \ } \ } \ catch (...) { \ return std::nullopt; \ } \ mars2marsUnreachable(); \ } \ }; // ============================================================== // SET_OR_THROW // ============================================================== #define M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(CTYPE, SETFUNC) \ template <> \ struct DictSetOrIgnore { \ static void set_or_ignore(eckit::LocalConfiguration& cfg, std::string_view key, \ const CTYPE& value) noexcept(false) { \ const std::string k{key}; \ \ try { \ cfg.SETFUNC(k, value); \ return; \ } \ catch (...) { \ } \ mars2marsUnreachable(); \ } \ }; // ============================================================== // SET_OR_IGNORE // ============================================================== #define M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(CTYPE, SETFUNC) \ template <> \ struct DictSetOrThrow { \ \ static void set_or_throw(eckit::LocalConfiguration& cfg, std::string_view key, \ const CTYPE& value) noexcept(false) { \ const std::string k{key}; \ \ try { \ cfg.SETFUNC(k, value); \ return; \ } \ catch (const exceptions::Mars2marsGenericException&) { \ throw; \ } \ catch (...) { \ std::throw_with_nested(exceptions::Mars2marsDictException( \ "Unable to set key `"s + k + "` with type `"s + std::string(type_name()) + \ "` in dictionary type `"s + std::string(type_name()) + "`", \ Here())); \ } \ mars2marsUnreachable(); \ } \ }; namespace metkit::mars2mars::utils { // ----------------------------------------------------------------------------- // type_name specialisation // ----------------------------------------------------------------------------- template <> constexpr std::string_view type_name() { return "eckit::LocalConfiguration"; } template <> constexpr std::string_view type_name>() { return "vector"; } } // namespace metkit::mars2mars::utils namespace metkit::mars2mars::utils::dict_traits { using std::operator""s; namespace hacks { inline bool isIntegral(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isIntegral(std::string{key}); } inline bool isFloatingPoint(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isFloatingPoint(std::string{key}) || conf.isIntegral(std::string{key}); } inline bool isBoolean(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isBoolean(std::string{key}); } inline bool isString(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isString(std::string{key}); } inline bool isSubConfiguration(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isSubConfiguration(std::string{key}); } inline bool isIntegralList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isIntegralList(std::string{key}); } inline bool isFloatingPointList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isFloatingPointList(std::string{key}) || conf.isIntegralList(std::string{key}); } inline bool isStringList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isStringList(std::string{key}); } inline bool isSubConfigurationList(const eckit::LocalConfiguration& conf, std::string_view key) { return conf.isSubConfigurationList(std::string{key}); } } // namespace hacks // ----------------------------------------------------------------------------- // to_json // ----------------------------------------------------------------------------- template <> struct DictToJsonTraits { static std::string to_json(const eckit::LocalConfiguration& cfg) noexcept(true) { try { std::ostringstream os; os << cfg; return os.str(); } catch (...) { return "[to_json failed for eckit::LocalConfiguration]"; } } }; // ----------------------------------------------------------------------------- // DictCreateFromSample / Clone / NeedsChecks // ----------------------------------------------------------------------------- template <> struct DictTraits { static constexpr bool support_checks = false; static std::unique_ptr make_from_sample_or_throw(std::string_view name) { auto cfg = std::make_unique(); cfg->set("SampleName", std::string(name)); return cfg; } static std::unique_ptr clone_or_throw(const eckit::LocalConfiguration& cfg) { return std::make_unique(cfg); } }; // ----------------------------------------------------------------------------- // DictHas // ----------------------------------------------------------------------------- template <> struct DictHas { static bool has(const eckit::LocalConfiguration& cfg, std::string_view key) noexcept(false) { const std::string k{key}; try { return cfg.has(k); } catch (const exceptions::Mars2marsGenericException&) { throw; } catch (...) { std::throw_with_nested(exceptions::Mars2marsDictException( "Internal error while checking presence of key `"s + k + "` in dictionary type `"s + std::string(type_name()) + "`", Here())); } mars2marsUnreachable(); } }; // ============================================================ // eckit::LocalConfiguration specializations via macros // ============================================================ //------------------------------------------------------------------------------ // Scalar types //------------------------------------------------------------------------------ // bool M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(bool, isBoolean, getBool) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(bool, isBoolean, getBool) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(bool, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(bool, set) // int M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(int, isIntegral, getInt) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(int, isIntegral, getInt) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(int, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(int, set) // long M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(long, isIntegral, getLong) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(long, isIntegral, getLong) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(long, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(long, set) // float M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(float, isFloatingPoint, getFloat) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(float, isFloatingPoint, getFloat) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(float, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(float, set) // double M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(double, isFloatingPoint, getDouble) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(double, isFloatingPoint, getDouble) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(double, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(double, set) // std::string M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::string, isString, getString) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::string, isString, getString) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::string, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::string, set) // eckit::LocalConfiguration (sub-configuration) M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(eckit::LocalConfiguration, isSubConfiguration, getSubConfiguration) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(eckit::LocalConfiguration, isSubConfiguration, getSubConfiguration) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(eckit::LocalConfiguration, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(eckit::LocalConfiguration, set) //------------------------------------------------------------------------------ // Vector types //------------------------------------------------------------------------------ // std::vector M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isIntegralList, getIntVector) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isIntegralList, getIntVector) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isIntegralList, getLongVector) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isIntegralList, getLongVector) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isFloatingPointList, getFloatVector) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isFloatingPointList, getFloatVector) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isFloatingPointList, getDoubleVector) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isFloatingPointList, getDoubleVector) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isStringList, getStringVector) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isStringList, getStringVector) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) // std::vector M2M_DEFINE_LOCALCONFIG_DICT_GET_OR_THROW(std::vector, isSubConfigurationList, getSubConfigurations) M2M_DEFINE_LOCALCONFIG_DICT_GET_OPT(std::vector, isSubConfigurationList, getSubConfigurations) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_THROW(std::vector, set) M2M_DEFINE_LOCALCONFIG_DICT_SET_OR_IGNORE(std::vector, set) } // namespace metkit::mars2mars::utils::dict_traits metkit-1.19.2/src/metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h0000664000175000017500000002231615232567717031423 0ustar alastairalastair/* * (C) Copyright 2026- 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 dictionary_access_traits.h /// @brief Generic dictionary access traits for mars2mars. #pragma once #include #include #include #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "metkit/mars2mars/utils/generalUtils.h" // Exceptions #include "metkit/config/LibMetkit.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/type_traits_name.h" namespace metkit::mars2mars::utils::dict_traits { using std::operator""s; template struct dependent_false : std::false_type {}; /// @brief Trait hook used to render a dictionary as JSON. template struct DictToJsonTraits { static std::string to_json(const Dict&) { return std::string{"[to_json not supported for this dictionary type]"}; } static void dump_or_ignore(const Dict&, const std::string&) { LOG_DEBUG_LIB(LibMetkit) << to_json(std::declval()); } }; /// @brief Trait hook for cloning and sample creation. template struct DictTraits { static constexpr bool support_checks = false; static std::unique_ptr make_from_sample_or_throw(std::string_view) { static_assert(dependent_false::value, "DictTraits::make_from_sample_or_throw not specialized"); } static std::unique_ptr clone_or_throw(const Dict&) { static_assert(dependent_false::value, "DictTraits::clone_or_throw not specialized"); } }; /// @brief Trait hook for key presence checks. template struct DictHas { static bool has(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictHas not specialized for this Dict"); mars2marsUnreachable(); } }; /// @brief Trait hook for missing-value checks. template struct DictMissing { static bool isMissing(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictMissing not specialized for this Dict"); mars2marsUnreachable(); } static void setMissing(Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictMissing not specialized for this Dict"); mars2marsUnreachable(); } }; /// @brief Trait hook for optional value retrieval. template struct DictGetOpt { static std::optional get_opt(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictGetOpt not specialized for this Dict and type"); mars2marsUnreachable(); } }; /// @brief Trait hook for mandatory value retrieval. template struct DictGetOrThrow { static T get_or_throw(const Dict&, std::string_view) noexcept(false) { static_assert(dependent_false::value, "DictGetOrThrow not specialized for this Dict and type"); mars2marsUnreachable(); } }; /// @brief Trait hook for assignment that may be ignored. template struct DictSetOrIgnore { static void set_or_ignore(Dict&, std::string_view, const T&) noexcept(false) { static_assert(dependent_false::value, "DictSetOrIgnore not specialized for this Dict and type"); mars2marsUnreachable(); } }; /// @brief Trait hook for assignment that must succeed. template struct DictSetOrThrow { static void set_or_throw(Dict&, std::string_view, const T&) noexcept(false) { static_assert(dependent_false::value, "DictSetOrThrow not specialized for this Dict and type"); mars2marsUnreachable(); } }; // ============================================================ // dict_to_json // ============================================================ /// @brief Convert a dictionary to JSON using the active traits. template std::string dict_to_json(const Dict& d) { return DictToJsonTraits::to_json(d); } // ============================================================ // clone / make_from_sample / needs_checks // ============================================================ /// @brief Report whether a dictionary supports consistency checks. template inline constexpr bool dict_supports_checks_v = DictTraits::support_checks; /// @brief Create a dictionary from a sample name. template std::unique_ptr make_from_sample_or_throw(std::string_view name) { return DictTraits::make_from_sample_or_throw(name); } /// @brief Clone a supported dictionary. template std::unique_ptr clone_or_throw(const Dict& d) { return DictTraits::clone_or_throw(d); } /// @brief Render a dictionary or ignore unsupported types. template void dump_or_ignore(const Dict& d, const std::string& f) { DictToJsonTraits::dump_or_ignore(d, f); } // ============================================================ // has / isMissing / setMissing // ============================================================ // has(dict,key) /// @brief Check whether a key exists in a dictionary. template inline bool has(const Dict& dict, std::string_view key) { return DictHas::has(dict, key); } // has(dict,key) /// @brief Check whether a typed value exists in a dictionary. template inline bool has(const Dict& dict, std::string_view key) { return DictGetOpt::get_opt(dict, key).has_value(); } // isMissing(dict,key) /// @brief Check whether a key is semantically missing. template inline bool isMissing(const Dict& dict, std::string_view key) { return DictMissing::isMissing(dict, key); } // setMissing(dict,key) /// @brief Mark a key as missing. template inline void setMissing_or_throw(Dict& dict, std::string_view key) { DictMissing::setMissing(dict, key); return; } // check(dict,key,cond) -> bool /// @brief Evaluate a predicate on a typed optional value. template inline bool check(const Dict& dict, std::string_view key, Cond&& condition) { if (auto v = DictGetOpt::get_opt(dict, key); v.has_value()) { return std::forward(condition)(*v); } return false; } // ============================================================ // GET UTILITIES // ============================================================ // get_or_throw(dict,key) -> T /// @brief Retrieve a mandatory typed value from a dictionary. template inline T get_or_throw(const Dict& dict, std::string_view key) { try { return DictGetOrThrow::get_or_throw(dict, key); } catch (...) { std::throw_with_nested( exceptions::Mars2marsDictException("Forwarding errors while getting key `"s + std::string(key) + "` as `" + std::string(type_name()) + "` from dictionary`"s, Here())); mars2marsUnreachable(); } mars2marsUnreachable(); } // get(dict,key) -> std::optional /// @brief Retrieve an optional typed value from a dictionary. template inline std::optional get_opt(const Dict& dict, std::string_view key) { try { return DictGetOpt::get_opt(dict, key); } catch (...) { return std::nullopt; } mars2marsUnreachable(); } // ============================================================ // SET UTILITIES // ============================================================ // set(dict,key,value) /// @brief Set a typed value in a dictionary and throw on failure. template inline void set_or_throw(Dict& dict, std::string_view key, const T& value) { try { DictSetOrThrow::set_or_throw(dict, key, value); return; } catch (...) { std::throw_with_nested(exceptions::Mars2marsDictException("Forwarding errors while setting key `"s + std::string(key) + "` as `" + std::string(type_name()) + "` to dictionary`"s, Here())); mars2marsUnreachable(); } mars2marsUnreachable(); } /// @brief Set a typed value in a dictionary and ignore failures. template inline void set_or_ignore(Dict& dict, std::string_view key, const T& value) { try { DictSetOrIgnore::set_or_ignore(dict, key, value); return; } catch (...) { // ignore exceptions mars2marsUnreachable(); } mars2marsUnreachable(); } } // namespace metkit::mars2mars::utils::dict_traits metkit-1.19.2/src/metkit/mars2mars/utils/dictionary_traits/dictaccess_mars_request.h0000664000175000017500000005654315232567717031257 0ustar alastairalastair#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "eckit/log/JSON.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/generalUtils.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/type_traits_name.h" namespace metkit::mars2mars::utils { template <> constexpr std::string_view type_name() { return "metkit::mars::MarsRequest"; } } // namespace metkit::mars2mars::utils namespace metkit::mars2mars::utils::dict_traits { using std::operator""s; namespace detail { inline const std::string& scalarStringOrThrow(const metkit::mars::MarsRequest& mars, std::string_view key) { const std::string k{key}; if (!mars.has(k)) { throw exceptions::Mars2marsDictException("Missing key `"s + k + "` in metkit::mars::MarsRequest", Here()); } const auto& values = mars.values(k); if (values.size() != 1) { throw exceptions::Mars2marsDictException( "Invalid non-scalar key `"s + k + "` in metkit::mars::MarsRequest: found `"s + std::to_string(values.size()) + "` values. mars2mars expects a single MARS point, not a hypercube.", Here()); } return values.front(); } inline std::optional scalarStringOpt(const metkit::mars::MarsRequest& mars, std::string_view key) { const std::string k{key}; if (!mars.has(k)) { return std::nullopt; } const auto& values = mars.values(k); if (values.empty()) { return std::nullopt; } if (values.size() != 1) { throw exceptions::Mars2marsDictException( "Invalid non-scalar key `"s + k + "` in metkit::mars::MarsRequest: found `"s + std::to_string(values.size()) + "` values. mars2mars expects a single MARS point, not a hypercube.", Here()); } return values.front(); } inline long parsePlainLong(std::string_view value, std::string_view key) { const std::string s{value}; std::size_t pos = 0; long result = 0; try { result = std::stol(s, &pos); } catch (...) { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to long", Here()); } if (pos != s.size()) { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to long without loss", Here()); } return result; } inline double parsePlainDouble(std::string_view value, std::string_view key) { const std::string s{value}; std::size_t pos = 0; double result = 0.0; try { result = std::stod(s, &pos); } catch (...) { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to double", Here()); } if (pos != s.size()) { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to double without loss", Here()); } return result; } inline long parseHoursAsLong(std::string_view value, std::string_view key) { const std::string s{value}; if (s.empty()) { throw exceptions::Mars2marsDictException("Cannot convert empty key `"s + std::string{key} + "` value to hours", Here()); } if (s.back() == 'h') { return parsePlainLong(std::string_view{s.data(), s.size() - 1}, key); } if (s.back() == 'm' || s.back() == 's') { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to hours: sub-hourly extents are not supported by this mars2mars path", Here()); } // Existing workflows treat bare numeric step/timespan as hours. return parsePlainLong(s, key); } inline std::string longToHours(long value) { return std::to_string(value) + "h"; } inline std::string doubleToString(double value) { std::ostringstream os; os.precision(std::numeric_limits::max_digits10); os << value; return os.str(); } // ----------------------------------------------------------------------------- // long converters // ----------------------------------------------------------------------------- using ToLong = long (*)(std::string_view value, std::string_view key); using FromLong = std::string (*)(long value); inline long paramToLong(std::string_view value, std::string_view key) { // Conservative first implementation: // param=130 -> OK // param=228.128 -> throw // // If later you need "228.128" -> some canonical long encoding, // add that rule here, not in generic parsing. // std::cout << "Parsing param value `" << value << "` as long for key `" << key << "`" << std::endl; return parsePlainLong(value, key); } inline long plainLong(std::string_view value, std::string_view key) { return parsePlainLong(value, key); } inline long hoursLong(std::string_view value, std::string_view key) { return parseHoursAsLong(value, key); } inline std::string plainLongString(long value) { return std::to_string(value); } inline bool allDigits(std::string_view s) { return std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); }); } inline bool isLeapYear(long year) { return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); } inline long daysInMonth(long year, long month) { switch (month) { case 1: return 31; case 2: return isLeapYear(year) ? 29 : 28; case 3: return 31; case 4: return 30; case 5: return 31; case 6: return 30; case 7: return 31; case 8: return 31; case 9: return 30; case 10: return 31; case 11: return 30; case 12: return 31; default: return 0; } } inline void checkYYYYMMDD(long yyyymmdd, std::string_view key) { const long year = yyyymmdd / 10000; const long month = (yyyymmdd / 100) % 100; const long day = yyyymmdd % 100; if (year <= 0 || month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) { throw exceptions::Mars2marsDictException("Invalid MARS date `"s + std::to_string(yyyymmdd) + "` for key `"s + std::string{key} + "`. Expected a valid yyyymmdd date.", Here()); } } inline long marsDateToLong(std::string_view value, std::string_view key) { const std::string s{value}; std::string compact; if (s.size() == 8 && allDigits(s)) { compact = s; } else if (s.size() == 10 && s[4] == '-' && s[7] == '-' && allDigits(std::string_view{s.data(), 4}) && allDigits(std::string_view{s.data() + 5, 2}) && allDigits(std::string_view{s.data() + 8, 2})) { compact.reserve(8); compact.append(s, 0, 4); compact.append(s, 5, 2); compact.append(s, 8, 2); } else { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to MARS date integer yyyymmdd. Supported forms are yyyymmdd and yyyy-mm-dd.", Here()); } const long result = parsePlainLong(compact, key); checkYYYYMMDD(result, key); return result; } inline std::string longToMarsDate(long value) { const std::string s = std::to_string(value); if (s.size() != 8 || !allDigits(s)) { throw exceptions::Mars2marsDictException("Cannot convert integer `"s + s + "` to MARS date. Expected yyyymmdd.", Here()); } checkYYYYMMDD(value, "date"); return s; } inline long parseTimePart(std::string_view part, std::string_view full, std::string_view key) { if (part.empty() || !allDigits(part)) { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + std::string{full} + "` to MARS time.", Here()); } return parsePlainLong(part, key); } inline long makeHHMMSS(long hh, long mm, long ss, std::string_view value, std::string_view key) { if (hh < 0 || hh > 23 || mm < 0 || mm > 59 || ss < 0 || ss > 59) { throw exceptions::Mars2marsDictException("Invalid MARS time `"s + std::string{value} + "` for key `"s + std::string{key} + "`. Expected hh=[0,23], mm=[0,59], ss=[0,59].", Here()); } if (ss != 0) { throw exceptions::Mars2marsDictException("Invalid MARS time `"s + std::string{value} + "` for key `"s + std::string{key} + "`. Seconds are not supported by metkit MARS time normalisation.", Here()); } return hh * 10000 + mm * 100 + ss; } inline long parseSeparatedMarsTime(std::string_view value, std::string_view key, char sep) { const auto p0 = value.find(sep); if (p0 == std::string_view::npos) { throw exceptions::Mars2marsDictException( "Internal error while parsing separated MARS time `"s + std::string{value} + "`", Here()); } const auto p1 = value.find(sep, p0 + 1); const auto hhPart = value.substr(0, p0); if (p1 == std::string_view::npos) { const auto mmPart = value.substr(p0 + 1); const long hh = parseTimePart(hhPart, value, key); const long mm = parseTimePart(mmPart, value, key); return makeHHMMSS(hh, mm, 0, value, key); } if (value.find(sep, p1 + 1) != std::string_view::npos) { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + std::string{value} + "` to MARS time.", Here()); } const auto mmPart = value.substr(p0 + 1, p1 - p0 - 1); const auto ssPart = value.substr(p1 + 1); const long hh = parseTimePart(hhPart, value, key); const long mm = parseTimePart(mmPart, value, key); const long ss = parseTimePart(ssPart, value, key); return makeHHMMSS(hh, mm, ss, value, key); } inline long marsTimeToLong(std::string_view value, std::string_view key) { const std::string s{value}; if (s.empty()) { throw exceptions::Mars2marsDictException( "Cannot convert empty key `"s + std::string{key} + "` value to MARS time.", Here()); } if (s.find(':') != std::string::npos) { return parseSeparatedMarsTime(s, key, ':'); } if (s.find('-') != std::string::npos) { return parseSeparatedMarsTime(s, key, '-'); } if (!allDigits(s)) { throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to MARS time integer hhmmss.", Here()); } if (s.size() <= 2) { // H or HH -> HH:00:00 const long hh = parsePlainLong(s, key); return makeHHMMSS(hh, 0, 0, value, key); } if (s.size() <= 4) { // HMM or HHMM -> HH:MM:00 std::string hhmm = s; hhmm.insert(hhmm.begin(), 4 - hhmm.size(), '0'); const long hh = parsePlainLong(std::string_view{hhmm.data(), 2}, key); const long mm = parsePlainLong(std::string_view{hhmm.data() + 2, 2}, key); return makeHHMMSS(hh, mm, 0, value, key); } if (s.size() <= 6) { // HMMSS or HHMMSS -> HH:MM:SS std::string hhmmss = s; hhmmss.insert(hhmmss.begin(), 6 - hhmmss.size(), '0'); const long hh = parsePlainLong(std::string_view{hhmmss.data(), 2}, key); const long mm = parsePlainLong(std::string_view{hhmmss.data() + 2, 2}, key); const long ss = parsePlainLong(std::string_view{hhmmss.data() + 4, 2}, key); return makeHHMMSS(hh, mm, ss, value, key); } throw exceptions::Mars2marsDictException( "Cannot convert key `"s + std::string{key} + "` value `"s + s + "` to MARS time integer hhmmss.", Here()); } inline std::string longToMarsTime(long value) { if (value < 0 || value > 235959) { throw exceptions::Mars2marsDictException( "Cannot convert integer `"s + std::to_string(value) + "` to MARS time. Expected hhmmss.", Here()); } std::ostringstream os; os << std::setw(6) << std::setfill('0') << value; const std::string hhmmss = os.str(); const long hh = parsePlainLong(std::string_view{hhmmss.data(), 2}, "time"); const long mm = parsePlainLong(std::string_view{hhmmss.data() + 2, 2}, "time"); const long ss = parsePlainLong(std::string_view{hhmmss.data() + 4, 2}, "time"); makeHHMMSS(hh, mm, ss, hhmmss, "time"); // metkit canonical MARS time is HHMM, not HHMMSS. return hhmmss.substr(0, 4); } inline const std::unordered_map& toLongConverters() { static const std::unordered_map converters = { {"param", paramToLong}, {"levelist", plainLong}, {"chem", plainLong}, {"step", hoursLong}, {"timespan", hoursLong}, {"date", marsDateToLong}, {"hdate", marsDateToLong}, {"time", marsTimeToLong}, {"htime", marsTimeToLong}, }; return converters; } inline const std::unordered_map& fromLongConverters() { static const std::unordered_map converters = { {"param", plainLongString}, {"levelist", plainLongString}, {"chem", plainLongString}, {"step", longToHours}, {"timespan", longToHours}, {"date", longToMarsDate}, {"hdate", longToMarsDate}, {"time", longToMarsTime}, {"htime", longToMarsTime}, }; return converters; } inline long convertToLongOrThrow(std::string_view key, std::string_view value) { const std::string k{key}; const auto& converters = toLongConverters(); const auto it = converters.find(k); if (it == converters.end()) { throw exceptions::Mars2marsDictException( "Key `"s + k + "` does not support conversion to long for metkit::mars::MarsRequest", Here()); } return it->second(value, key); } inline std::string convertFromLongOrThrow(std::string_view key, long value) { const std::string k{key}; const auto& converters = fromLongConverters(); const auto it = converters.find(k); if (it == converters.end()) { throw exceptions::Mars2marsDictException( "Key `"s + k + "` does not support assignment from long for metkit::mars::MarsRequest", Here()); } return it->second(value); } // ----------------------------------------------------------------------------- // double converters // ----------------------------------------------------------------------------- using ToDouble = double (*)(std::string_view value, std::string_view key); using FromDouble = std::string (*)(double value); inline const std::unordered_map& toDoubleConverters() { static const std::unordered_map converters = { {"wavelength", parsePlainDouble}, }; return converters; } inline const std::unordered_map& fromDoubleConverters() { static const std::unordered_map converters = { {"wavelength", doubleToString}, }; return converters; } inline double convertToDoubleOrThrow(std::string_view key, std::string_view value) { const std::string k{key}; const auto& converters = toDoubleConverters(); const auto it = converters.find(k); if (it == converters.end()) { throw exceptions::Mars2marsDictException( "Key `"s + k + "` does not support conversion to double for metkit::mars::MarsRequest", Here()); } return it->second(value, key); } inline std::string convertFromDoubleOrThrow(std::string_view key, double value) { const std::string k{key}; const auto& converters = fromDoubleConverters(); const auto it = converters.find(k); if (it == converters.end()) { throw exceptions::Mars2marsDictException( "Key `"s + k + "` does not support assignment from double for metkit::mars::MarsRequest", Here()); } return it->second(value); } } // namespace detail // ----------------------------------------------------------------------------- // DictToJsonTraits // ----------------------------------------------------------------------------- template <> struct DictToJsonTraits { static std::string to_json(const metkit::mars::MarsRequest& mars) noexcept(true) { try { std::ostringstream os; eckit::JSON json(os); mars.json(json); return os.str(); } catch (...) { return "[to_json failed for metkit::mars::MarsRequest]"; } } }; // ----------------------------------------------------------------------------- // DictTraits // ----------------------------------------------------------------------------- template <> struct DictTraits { static constexpr bool support_checks = false; // static std::unique_ptr // make_from_sample_or_throw(std::string_view name) { // return std::make_unique(std::string{name}); // } static std::unique_ptr clone_or_throw(const metkit::mars::MarsRequest& mars) { return std::make_unique(mars); } }; // ----------------------------------------------------------------------------- // DictHas // ----------------------------------------------------------------------------- template <> struct DictHas { static bool has(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { return mars.has(std::string{key}); } }; // ----------------------------------------------------------------------------- // DictMissing // ----------------------------------------------------------------------------- template <> struct DictMissing { static bool isMissing(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const std::string k{key}; if (!mars.has(k)) { return true; } const auto& values = mars.values(k, true); return values.empty(); } static void setMissing(metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { mars.unsetValues(std::string{key}); } }; // ----------------------------------------------------------------------------- // std::string access: all keys, raw scalar string // ----------------------------------------------------------------------------- template <> struct DictGetOrThrow { static std::string get_or_throw(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { return detail::scalarStringOrThrow(mars, key); } }; template <> struct DictGetOpt { static std::optional get_opt(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { return detail::scalarStringOpt(mars, key); } }; template <> struct DictSetOrThrow { static void set_or_throw(metkit::mars::MarsRequest& mars, std::string_view key, const std::string& value) noexcept(false) { mars.setValue(std::string{key}, value); } }; template <> struct DictSetOrIgnore { static void set_or_ignore(metkit::mars::MarsRequest& mars, std::string_view key, const std::string& value) noexcept(false) { try { mars.setValue(std::string{key}, value); } catch (...) { } } }; // ----------------------------------------------------------------------------- // long access: explicit supported keys only // ----------------------------------------------------------------------------- template <> struct DictGetOrThrow { static long get_or_throw(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const auto& raw = detail::scalarStringOrThrow(mars, key); return detail::convertToLongOrThrow(key, raw); } }; template <> struct DictGetOpt { static std::optional get_opt(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const auto raw = detail::scalarStringOpt(mars, key); if (!raw.has_value()) { return std::nullopt; } return detail::convertToLongOrThrow(key, *raw); } }; template <> struct DictSetOrThrow { static void set_or_throw(metkit::mars::MarsRequest& mars, std::string_view key, const long& value) noexcept(false) { mars.setValue(std::string{key}, detail::convertFromLongOrThrow(key, value)); } }; template <> struct DictSetOrIgnore { static void set_or_ignore(metkit::mars::MarsRequest& mars, std::string_view key, const long& value) noexcept(false) { try { mars.setValue(std::string{key}, detail::convertFromLongOrThrow(key, value)); } catch (...) { } } }; // ----------------------------------------------------------------------------- // double access: wavelength only // ----------------------------------------------------------------------------- template <> struct DictGetOrThrow { static double get_or_throw(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const auto& raw = detail::scalarStringOrThrow(mars, key); return detail::convertToDoubleOrThrow(key, raw); } }; template <> struct DictGetOpt { static std::optional get_opt(const metkit::mars::MarsRequest& mars, std::string_view key) noexcept(false) { const auto raw = detail::scalarStringOpt(mars, key); if (!raw.has_value()) { return std::nullopt; } return detail::convertToDoubleOrThrow(key, *raw); } }; template <> struct DictSetOrThrow { static void set_or_throw(metkit::mars::MarsRequest& mars, std::string_view key, const double& value) noexcept(false) { mars.setValue(std::string{key}, detail::convertFromDoubleOrThrow(key, value)); } }; template <> struct DictSetOrIgnore { static void set_or_ignore(metkit::mars::MarsRequest& mars, std::string_view key, const double& value) noexcept(false) { try { mars.setValue(std::string{key}, detail::convertFromDoubleOrThrow(key, value)); } catch (...) { } } }; } // namespace metkit::mars2mars::utils::dict_traitsmetkit-1.19.2/src/metkit/mars2mars/mappings/0000775000175000017500000000000015232567717021115 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2mars/mappings/Mars2MarsReturnValue.h0000664000175000017500000000174415232567717025300 0ustar alastairalastair/* * (C) Copyright 2026- 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 Mars2MarsReturnValue.h /// @brief Return type used by mars2mars conversion APIs. #pragma once #include "eckit/config/LocalConfiguration.h" namespace metkit::mars2mars { /// @brief Structured result returned by mars2mars conversions. /// /// @tparam MarsDict /// Dictionary type used for the converted request. template struct Mars2MarsResult { /// @brief Converted MARS request. MarsDict mars; /// @brief Auxiliary metadata produced during conversion. eckit::LocalConfiguration misc; }; } // namespace metkit::mars2mars metkit-1.19.2/src/metkit/mars2mars/mappings/mappings.h0000664000175000017500000000452515232567717023112 0ustar alastairalastair/* * (C) Copyright 2026- 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 all.h /// @brief Conversion rules used by the mars2mars mapper. #pragma once #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" #include "metkit/mars2mars/mappings/rules/wave2oper.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" // All the rules #include "metkit/mars2mars/mappings/rules/chemical.h" #include "metkit/mars2mars/mappings/rules/ecc-1806.h" #include "metkit/mars2mars/mappings/rules/local2wmo.h" #include "metkit/mars2mars/mappings/rules/sfc2sol.h" #include "metkit/mars2mars/mappings/rules/timespan.h" #include "metkit/mars2mars/mappings/rules/wave2oper.h" #include "metkit/mars2mars/mappings/rules/windspeed.h" namespace metkit::mars2mars::rules { /// @brief Apply all registered conversion rules and return a result object. template Mars2MarsResult convertAll(const InDict_t& in) { using metkit::mars2mars::utils::dict_traits::clone_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { std::unique_ptr out = clone_or_throw(in); std::unique_ptr misc = std::make_unique(); // Apply all conversions in sequence impl::convertWave2Oper(in, *out, *misc); impl::convertECC1806(in, *out, *misc); impl::convertSFC2SOL(in, *out, *misc); impl::convertLocal2WMO(in, *out, *misc); impl::fixTimespan(in, *out, *misc); impl::fixWindspeed(in, *out, *misc); impl::convertChemical(in, *out, *misc); return Mars2MarsResult{std::move(*out), std::move(*misc)}; } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2marsGenericException("Failed to convertAll input dictionaries", Here())); } } } // namespace metkit::mars2mars::rules metkit-1.19.2/src/metkit/mars2mars/mappings/rules/0000775000175000017500000000000015232567717022247 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2mars/mappings/rules/windspeed.h0000664000175000017500000000472315232567717024410 0ustar alastairalastair/* * (C) Copyright 2026- 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 windspeed.h /// @brief Conversion rules used by the mars2mars mapper. #pragma once #include #include "eckit/config/LocalConfiguration.h" #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" #include "metkit/mars2mars/mappings/rules/common.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/paramMatcher.h" namespace metkit::mars2mars::rules::impl { /// @brief Convert surface-like legacy requests into sol layer output. template inline void fixWindspeed(const InDict_t& in, OutDict_t& out, eckit::LocalConfiguration& misc) { using metkit::mars2mars::utils::dict_traits::get_opt; using metkit::mars2mars::utils::dict_traits::get_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { const auto levtype = get_opt(in, "levtype"); if (levtype && *levtype == "SFC") { const auto param = get_or_throw(in, "param"); switch (param) { case 228246: // 100u return detail::setParamLevel(out, 131, "hl", 100); case 228239: // 200u return detail::setParamLevel(out, 131, "hl", 200); case 228247: // 100v return detail::setParamLevel(out, 132, "hl", 100); case 228240: // 200v return detail::setParamLevel(out, 132, "hl", 200); case 228249: // 100si return detail::setParamLevel(out, 10, "hl", 100); case 228241: // 200si return detail::setParamLevel(out, 10, "hl", 200); default: break; } } } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2marsGenericException("Failed to convert input dictionary in fixWindspeed", Here())); } } } // namespace metkit::mars2mars::rules::impl metkit-1.19.2/src/metkit/mars2mars/mappings/rules/sfc2sol.h0000664000175000017500000000646015232567717024001 0ustar alastairalastair/* * (C) Copyright 2026- 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 sfc2sol.h /// @brief Conversion rules used by the mars2mars mapper. #pragma once #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" #include "metkit/mars2mars/mappings/rules/common.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/paramMatcher.h" namespace metkit::mars2mars::rules::impl { /// @brief Convert surface-like legacy requests into sol layer output. template inline void convertSFC2SOL(const InDict_t& in, OutDict_t& out, eckit::LocalConfiguration& misc) { using metkit::mars2mars::utils::dict_traits::get_opt; using metkit::mars2mars::utils::dict_traits::get_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { const auto param = get_or_throw(in, "param"); switch (param) { // (35, 36, 37, 39) -> 262024 case 35: return detail::setParamLevel(out, 262024, "sol", 1); case 36: return detail::setParamLevel(out, 262024, "sol", 2); case 37: return detail::setParamLevel(out, 262024, "sol", 3); case 38: return detail::setParamLevel(out, 262024, "sol", 4); // (39, 40, 41, 42) -> 260199 case 39: return detail::setParamLevel(out, 260199, "sol", 1); case 40: return detail::setParamLevel(out, 260199, "sol", 2); case 41: return detail::setParamLevel(out, 260199, "sol", 3); case 42: return detail::setParamLevel(out, 260199, "sol", 4); // (139, 170, 183, 236) -> 260360 case 139: return detail::setParamLevel(out, 260360, "sol", 1); case 170: return detail::setParamLevel(out, 260360, "sol", 2); case 183: return detail::setParamLevel(out, 260360, "sol", 3); case 236: return detail::setParamLevel(out, 260360, "sol", 4); // SFC with non-zero levelist to SOL case 33: case 238: case 228038: case 235080: case 237080: case 238080: case 239080: case 260199: case 260360: case 262000: case 262024: const auto levelist = get_opt(in, "levelist").value_or(0); if (levelist != 0) { return detail::setParamLevel(out, param, "sol", levelist); } break; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2marsGenericException("Failed to convertSFC2SOL input dictionaries", Here())); } } } // namespace metkit::mars2mars::rules::impl metkit-1.19.2/src/metkit/mars2mars/mappings/rules/common.h0000664000175000017500000000314715232567717023715 0ustar alastairalastair/* * (C) Copyright 2026- 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 common.h /// @brief Shared utilities to make conversion rules easier to implement. /// @todo: utils/paramMatcher.h needs to be merged into this file. #pragma once #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/paramMatcher.h" namespace metkit::mars2mars::rules::impl::detail { /// @brief Assign `param`, `levtype`, and `levelist` together. template inline void setParamLevel(OutDict_t& out, long param, const std::string& levtype, long levelist) { using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { set_or_throw(out, "param", param); set_or_throw(out, "levtype", levtype); set_or_throw(out, "levelist", levelist); } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2marsGenericException("Failed to setParamLevel to input dictionaries", Here())); } } } // namespace metkit::mars2mars::rules::impl::detail metkit-1.19.2/src/metkit/mars2mars/mappings/rules/ecc-1806.h0000664000175000017500000001211515232567717023546 0ustar alastairalastair/* * (C) Copyright 2026- 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 ecc-1806.h /// @brief Conversion rules used by the mars2mars mapper. #pragma once #include "eckit/config/LocalConfiguration.h" #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" #include "metkit/mars2mars/mappings/rules/common.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/paramMatcher.h" namespace metkit::mars2mars::rules::impl { /// @brief Convert surface-like legacy requests into sol layer output. template inline void convertECC1806(const InDict_t& in, OutDict_t& out, eckit::LocalConfiguration& misc) { using metkit::mars2mars::utils::dict_traits::get_or_throw; using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { const auto param = get_or_throw(in, "param"); // See: // https://github.com/ecmwf/eccodes/blob/develop/definitions/grib1/localConcepts/ecmf/paramIdForConversion.def switch (param) { case 55: set_or_throw(out, "param", 228004); set_or_throw(out, "timespan", "24h"); break; case 56: set_or_throw(out, "param", 235168); set_or_throw(out, "timespan", "24h"); break; case 130232: set_or_throw(out, "param", 235135); // TODO: Set timespan break; case 151163: set_or_throw(out, "param", 262104); break; case 151145: set_or_throw(out, "param", 262124); break; case 172146: set_or_throw(out, "param", 235033); // TODO: Set timespan break; case 172147: set_or_throw(out, "param", 235034); // TODO: Set timespan break; case 172169: set_or_throw(out, "param", 235035); // TODO: Set timespan break; case 172175: set_or_throw(out, "param", 235036); // TODO: Set timespan break; case 172176: set_or_throw(out, "param", 235037); // TODO: Set timespan break; case 172177: set_or_throw(out, "param", 235038); // TODO: Set timespan break; case 172178: set_or_throw(out, "param", 235039); // TODO: Set timespan break; case 172179: set_or_throw(out, "param", 235040); // TODO: Set timespan break; case 174098: set_or_throw(out, "param", 262000); break; case 151175: set_or_throw(out, "param", 262118); break; case 151132: set_or_throw(out, "param", 262139); break; case 151131: set_or_throw(out, "param", 262140); break; case 72: set_or_throw(out, "param", 260087); break; case 73: set_or_throw(out, "param", 260097); break; case 172050: set_or_throw(out, "param", 235026); // TODO: Set timespan break; case 172145: set_or_throw(out, "param", 235032); // TODO: Set timespan break; case 172189: set_or_throw(out, "param", 235189); // TODO: Set timespan break; case 172195: set_or_throw(out, "param", 235045); // TODO: Set timespan break; case 172196: set_or_throw(out, "param", 235046); // TODO: Set timespan break; case 172197: set_or_throw(out, "param", 235047); // TODO: Set timespan break; default: break; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2marsGenericException("Failed to convert input dictionary in convertECC1806", Here())); } } } // namespace metkit::mars2mars::rules::impl metkit-1.19.2/src/metkit/mars2mars/mappings/rules/timespan.h0000664000175000017500000001143315232567717024242 0ustar alastairalastair/* * (C) Copyright 2026- 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 timespan.h /// @brief Conversion rules used by the mars2mars mapper. #pragma once #include "eckit/config/LocalConfiguration.h" #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" #include "metkit/mars2mars/mappings/rules/common.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/paramMatcher.h" namespace metkit::mars2mars::rules::impl { /// @brief Assign `param`, and `timespan` together. template inline void setParamTimespan(OutDict_t& out, long param, const std::string& timespan) { using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { set_or_throw(out, "param", param); set_or_throw(out, "timespan", timespan); } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2marsGenericException("Failed to set param and/or timespan in output dictionary", Here())); } } /// @brief Convert surface-like legacy requests into sol layer output. template inline void fixTimespan(const InDict_t& in, OutDict_t& out, eckit::LocalConfiguration& misc) { using metkit::mars2mars::utils::dict_traits::get_or_throw; using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { const auto param = get_or_throw(in, "param"); switch (param) { // Max 2t case 121: return setParamTimespan(out, 237167, "6h"); case 228026: return setParamTimespan(out, 237167, "3h"); case 201: return set_or_throw(out, "param", 237167); // Max 6h CAPE case 228035: return setParamTimespan(out, 237117, "6h"); // Max 6h CAPES case 228036: return setParamTimespan(out, 237321, "6h"); // Max precipitation case 228222: return setParamTimespan(out, 237055, "3h"); case 228224: return setParamTimespan(out, 237055, "6h"); case 228226: return set_or_throw(out, "param", 237055); // Max wind gust case 123: return setParamTimespan(out, 237318, "6h"); case 228028: return setParamTimespan(out, 237318, "3h"); case 49: return set_or_throw(out, "param", 237318); // Mean flash density ? case 228051: return setParamTimespan(out, 235326, "1h"); case 228057: return setParamTimespan(out, 235326, "3h"); case 228058: return setParamTimespan(out, 235326, "6h"); // Min 2t case 122: return setParamTimespan(out, 238167, "6h"); case 228027: return setParamTimespan(out, 238167, "3h"); case 202: return set_or_throw(out, "param", 238167); // Min precipitation case 228223: return setParamTimespan(out, 238055, "3h"); case 228225: return setParamTimespan(out, 238055, "6h"); case 228227: return set_or_throw(out, "param", 238055); // Most frequent precipitation type case 260320: return setParamTimespan(out, 260683, "1h"); case 260321: return setParamTimespan(out, 260683, "3h"); case 260339: return setParamTimespan(out, 260683, "6h"); // Most severe precipitation type case 260318: return setParamTimespan(out, 260682, "1h"); case 260319: return setParamTimespan(out, 260682, "3h"); case 260338: return setParamTimespan(out, 260682, "6h"); default: break; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2marsGenericException("Failed to convert input dictionary in fixTimespan", Here())); } } } // namespace metkit::mars2mars::rules::impl metkit-1.19.2/src/metkit/mars2mars/mappings/rules/chemical.h0000664000175000017500000014325715232567717024201 0ustar alastairalastair/* * (C) Copyright 2026- 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.h /// @brief Conversion rules used by the mars2mars mapper. #pragma once #include "eckit/config/LocalConfiguration.h" #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" #include "metkit/mars2mars/mappings/rules/common.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/paramMatcher.h" namespace metkit::mars2mars::rules::impl { /// @brief Assign `param`, and `chem` together. template inline void setParamChem(OutDict_t& out, long param, long chem) { using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { set_or_throw(out, "param", param); set_or_throw(out, "chem", chem); } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2marsGenericException("Failed to set param, and/or chem and/or in output dictionaries", Here())); } } /// @brief Assign `param`, `chem`, and `wavelength` together. template inline void setParamChemWavelength(OutDict_t& out, long param, long chem, double wavelength) { using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { set_or_throw(out, "param", param); set_or_throw(out, "chem", chem); set_or_throw(out, "wavelength", wavelength); } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2marsGenericException("Failed to set param, chem and/or, wavelength in output dictionaries", Here())); } } /// @brief Convert surface-like legacy requests into sol layer output. template inline void convertChemical(const InDict_t& in, OutDict_t& out, eckit::LocalConfiguration& misc) { using metkit::mars2mars::utils::dict_traits::get_or_throw; using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { const auto param = get_or_throw(in, "param"); switch (param) { case 210001: return setParamChem(out, 402000, 901); case 210002: return setParamChem(out, 402000, 902); case 210003: return setParamChem(out, 402000, 903); case 210004: return setParamChem(out, 402000, 904); case 210005: return setParamChem(out, 402000, 905); case 210006: return setParamChem(out, 402000, 906); case 210007: return setParamChem(out, 402000, 907); case 210008: return setParamChem(out, 402000, 908); case 210009: return setParamChem(out, 402000, 909); case 210010: return setParamChem(out, 402000, 910); case 210011: return setParamChem(out, 402000, 911); case 210022: return setParamChem(out, 453000, 922); case 210025: return setParamChem(out, 453000, 922); case 210048: return setParamChem(out, 402000, 924); case 210071: return setParamChem(out, 479000, 404); case 210072: return setParamChem(out, 400000, 929); case 210073: return setParamChem(out, 400000, 930); case 210074: return setParamChem(out, 400000, 931); case 210081: return setParamChem(out, 469000, 2); case 210085: return setParamChem(out, 469000, 129); case 210090: return setParamChem(out, 469000, 934); case 210091: return setParamChem(out, 469000, 933); case 210102: return setParamChem(out, 469000, 233); case 210103: return setParamChem(out, 469000, 42); case 210104: return setParamChem(out, 469000, 46); case 210105: return setParamChem(out, 469000, 47); case 210106: return setParamChem(out, 469000, 10); case 210107: return setParamChem(out, 469000, 48); case 210108: return setParamChem(out, 469000, 16); case 210113: return setParamChem(out, 469000, 5); case 210115: return setParamChem(out, 469000, 52); case 210116: return setParamChem(out, 469000, 19); case 210117: return setParamChem(out, 469000, 18); case 210118: return setParamChem(out, 469000, 45); case 210121: return setParamChem(out, 402000, 17); case 210122: return setParamChem(out, 402000, 233); case 210123: return setParamChem(out, 402000, 2); case 210124: return setParamChem(out, 402000, 5); case 210125: return setParamChem(out, 401000, 17); case 210126: return setParamChem(out, 401000, 233); case 210127: return setParamChem(out, 401000, 2); case 210128: return setParamChem(out, 401000, 5); case 210170: return setParamChem(out, 402000, 8); case 210181: return setParamChem(out, 402000, 11); case 210183: return setParamChem(out, 401000, 11); case 210203: return setParamChem(out, 402000, 236); case 210206: return setParamChem(out, 401000, 231); case 210207: return setParamChemWavelength(out, 457000, 922, 550.0); case 210208: return setParamChemWavelength(out, 457000, 936, 550.0); case 210209: return setParamChemWavelength(out, 457000, 935, 550.0); case 210210: return setParamChemWavelength(out, 457000, 934, 550.0); case 210211: return setParamChemWavelength(out, 457000, 933, 550.0); case 210212: return setParamChemWavelength(out, 457000, 911, 550.0); case 210213: return setParamChemWavelength(out, 457000, 922, 469.0); case 210214: return setParamChemWavelength(out, 457000, 922, 670.0); case 210215: return setParamChemWavelength(out, 457000, 922, 865.0); case 210216: return setParamChemWavelength(out, 457000, 922, 1240.0); case 210217: return setParamChemWavelength(out, 457000, 922, 340.0); case 210218: return setParamChemWavelength(out, 457000, 922, 355.0); case 210219: return setParamChemWavelength(out, 457000, 922, 380.0); case 210220: return setParamChemWavelength(out, 457000, 922, 400.0); case 210221: return setParamChemWavelength(out, 457000, 922, 440.0); case 210222: return setParamChemWavelength(out, 457000, 922, 500.0); case 210223: return setParamChemWavelength(out, 457000, 922, 532.0); case 210224: return setParamChemWavelength(out, 457000, 922, 645.0); case 210225: return setParamChemWavelength(out, 457000, 922, 800.0); case 210226: return setParamChemWavelength(out, 457000, 922, 858.0); case 210227: return setParamChemWavelength(out, 457000, 922, 1020.0); case 210228: return setParamChemWavelength(out, 457000, 922, 1064.0); case 210229: return setParamChemWavelength(out, 457000, 922, 1640.0); case 210230: return setParamChemWavelength(out, 457000, 922, 2130.0); case 210247: return setParamChem(out, 402000, 912); case 210248: return setParamChem(out, 402000, 913); case 210249: return setParamChem(out, 402000, 21); case 210250: return setParamChemWavelength(out, 457000, 900, 550.0); case 210251: return setParamChemWavelength(out, 457000, 914, 550.0); case 210252: return setParamChem(out, 402000, 915); case 210253: return setParamChem(out, 402000, 916); case 211001: return setParamChem(out, 402000, 901); case 211002: return setParamChem(out, 402000, 902); case 211003: return setParamChem(out, 402000, 903); case 211004: return setParamChem(out, 402000, 904); case 211005: return setParamChem(out, 402000, 905); case 211006: return setParamChem(out, 402000, 906); case 211007: return setParamChem(out, 402000, 907); case 211008: return setParamChem(out, 402000, 908); case 211009: return setParamChem(out, 402000, 909); case 211010: return setParamChem(out, 402000, 910); case 211011: return setParamChem(out, 402000, 911); case 211048: return setParamChem(out, 402000, 924); case 211121: return setParamChem(out, 402000, 17); case 211122: return setParamChem(out, 402000, 233); case 211123: return setParamChem(out, 402000, 2); case 211124: return setParamChem(out, 402000, 5); case 211170: return setParamChem(out, 402000, 8); case 211203: return setParamChem(out, 402000, 236); case 211247: return setParamChem(out, 402000, 900); case 211248: return setParamChem(out, 402000, 903); case 211249: return setParamChem(out, 402000, 914); case 211252: return setParamChem(out, 402000, 915); case 211253: return setParamChem(out, 402000, 916); case 215001: return setParamChem(out, 453000, 901); case 215002: return setParamChem(out, 453000, 902); case 215003: return setParamChem(out, 453000, 903); case 215004: return setParamChem(out, 406000, 901); case 215005: return setParamChem(out, 406000, 902); case 215006: return setParamChem(out, 406000, 903); case 215007: return setParamChem(out, 407000, 901); case 215008: return setParamChem(out, 407000, 902); case 215009: return setParamChem(out, 407000, 903); case 215010: return setParamChem(out, 410000, 901); case 215011: return setParamChem(out, 410000, 902); case 215012: return setParamChem(out, 410000, 903); case 215013: return setParamChem(out, 411000, 901); case 215014: return setParamChem(out, 411000, 902); case 215015: return setParamChem(out, 411000, 903); case 215016: return setParamChem(out, 451000, 901); case 215017: return setParamChem(out, 451000, 902); case 215018: return setParamChem(out, 451000, 903); case 215019: return setParamChem(out, 401000, 901); case 215020: return setParamChem(out, 401000, 902); case 215021: return setParamChem(out, 401000, 903); case 215022: return setParamChemWavelength(out, 457000, 901, 550.0); case 215023: return setParamChemWavelength(out, 457000, 902, 550.0); case 215024: return setParamChemWavelength(out, 457000, 903, 550.0); case 215025: return setParamChem(out, 453000, 904); case 215026: return setParamChem(out, 453000, 905); case 215027: return setParamChem(out, 453000, 906); case 215028: return setParamChem(out, 406000, 904); case 215029: return setParamChem(out, 406000, 905); case 215030: return setParamChem(out, 406000, 906); case 215031: return setParamChem(out, 407000, 904); case 215032: return setParamChem(out, 407000, 905); case 215033: return setParamChem(out, 407000, 906); case 215034: return setParamChem(out, 410000, 904); case 215035: return setParamChem(out, 410000, 905); case 215036: return setParamChem(out, 410000, 906); case 215037: return setParamChem(out, 411000, 904); case 215038: return setParamChem(out, 411000, 905); case 215039: return setParamChem(out, 411000, 906); case 215040: return setParamChem(out, 451000, 904); case 215041: return setParamChem(out, 451000, 905); case 215042: return setParamChem(out, 451000, 906); case 215043: return setParamChem(out, 401000, 904); case 215044: return setParamChem(out, 401000, 905); case 215045: return setParamChem(out, 401000, 906); case 215046: return setParamChemWavelength(out, 457000, 904, 550.0); case 215047: return setParamChemWavelength(out, 457000, 905, 550.0); case 215048: return setParamChemWavelength(out, 457000, 906, 550.0); case 215049: return setParamChem(out, 453000, 908); case 215050: return setParamChem(out, 453000, 907); case 215051: return setParamChem(out, 406000, 908); case 215052: return setParamChem(out, 406000, 907); case 215053: return setParamChem(out, 407000, 908); case 215054: return setParamChem(out, 407000, 907); case 215055: return setParamChem(out, 410000, 908); case 215056: return setParamChem(out, 410000, 907); case 215057: return setParamChem(out, 411000, 908); case 215058: return setParamChem(out, 411000, 907); case 215059: return setParamChem(out, 451000, 908); case 215060: return setParamChem(out, 451000, 907); case 215061: return setParamChem(out, 401000, 908); case 215062: return setParamChem(out, 401000, 907); case 215063: return setParamChemWavelength(out, 457000, 908, 550.0); case 215064: return setParamChemWavelength(out, 457000, 907, 550.0); case 215065: return setParamChem(out, 453000, 910); case 215066: return setParamChem(out, 453000, 909); case 215067: return setParamChem(out, 406000, 910); case 215068: return setParamChem(out, 406000, 909); case 215069: return setParamChem(out, 407000, 910); case 215070: return setParamChem(out, 407000, 909); case 215071: return setParamChem(out, 410000, 910); case 215072: return setParamChem(out, 410000, 909); case 215073: return setParamChem(out, 411000, 910); case 215074: return setParamChem(out, 411000, 909); case 215075: return setParamChem(out, 451000, 910); case 215076: return setParamChem(out, 451000, 909); case 215077: return setParamChem(out, 401000, 910); case 215078: return setParamChem(out, 401000, 909); case 215079: return setParamChemWavelength(out, 457000, 910, 550.0); case 215080: return setParamChemWavelength(out, 457000, 909, 550.0); case 215081: return setParamChem(out, 453000, 911); case 215082: return setParamChem(out, 406000, 911); case 215083: return setParamChem(out, 407000, 911); case 215084: return setParamChem(out, 410000, 911); case 215085: return setParamChem(out, 411000, 911); case 215086: return setParamChem(out, 451000, 911); case 215087: return setParamChem(out, 401000, 911); case 215088: return setParamChemWavelength(out, 457000, 911, 550.0); case 215093: return setParamChemWavelength(out, 457000, 922, 532.0); case 215094: return setParamChemWavelength(out, 457000, 904, 532.0); case 215095: return setParamChemWavelength(out, 457000, 910, 532.0); case 215096: return setParamChemWavelength(out, 472000, 922, 340.0); case 215097: return setParamChemWavelength(out, 472000, 922, 355.0); case 215098: return setParamChemWavelength(out, 472000, 922, 380.0); case 215099: return setParamChemWavelength(out, 472000, 922, 400.0); case 215100: return setParamChemWavelength(out, 472000, 922, 440.0); case 215101: return setParamChemWavelength(out, 472000, 922, 469.0); case 215102: return setParamChemWavelength(out, 472000, 922, 500.0); case 215103: return setParamChemWavelength(out, 472000, 922, 532.0); case 215104: return setParamChemWavelength(out, 472000, 922, 550.0); case 215105: return setParamChemWavelength(out, 472000, 922, 645.0); case 215106: return setParamChemWavelength(out, 472000, 922, 670.0); case 215107: return setParamChemWavelength(out, 472000, 922, 800.0); case 215108: return setParamChemWavelength(out, 472000, 922, 858.0); case 215109: return setParamChemWavelength(out, 472000, 922, 865.0); case 215110: return setParamChemWavelength(out, 472000, 922, 1020.0); case 215111: return setParamChemWavelength(out, 472000, 922, 1064.0); case 215112: return setParamChemWavelength(out, 472000, 922, 1240.0); case 215113: return setParamChemWavelength(out, 472000, 922, 1640.0); case 215114: return setParamChemWavelength(out, 457000, 923, 340.0); case 215115: return setParamChemWavelength(out, 457000, 923, 355.0); case 215116: return setParamChemWavelength(out, 457000, 923, 380.0); case 215117: return setParamChemWavelength(out, 457000, 923, 400.0); case 215118: return setParamChemWavelength(out, 457000, 923, 440.0); case 215119: return setParamChemWavelength(out, 457000, 923, 469.0); case 215120: return setParamChemWavelength(out, 457000, 923, 500.0); case 215121: return setParamChemWavelength(out, 457000, 923, 532.0); case 215122: return setParamChemWavelength(out, 457000, 923, 550.0); case 215123: return setParamChemWavelength(out, 457000, 923, 645.0); case 215124: return setParamChemWavelength(out, 457000, 923, 670.0); case 215125: return setParamChemWavelength(out, 457000, 923, 800.0); case 215126: return setParamChemWavelength(out, 457000, 923, 858.0); case 215127: return setParamChemWavelength(out, 457000, 923, 865.0); case 215128: return setParamChemWavelength(out, 457000, 923, 1020.0); case 215129: return setParamChemWavelength(out, 457000, 923, 1064.0); case 215130: return setParamChemWavelength(out, 457000, 923, 1240.0); case 215131: return setParamChemWavelength(out, 457000, 923, 1640.0); case 215132: return setParamChemWavelength(out, 458000, 922, 340.0); case 215133: return setParamChemWavelength(out, 458000, 922, 355.0); case 215134: return setParamChemWavelength(out, 458000, 922, 380.0); case 215135: return setParamChemWavelength(out, 458000, 922, 400.0); case 215136: return setParamChemWavelength(out, 458000, 922, 440.0); case 215137: return setParamChemWavelength(out, 458000, 922, 469.0); case 215138: return setParamChemWavelength(out, 458000, 922, 500.0); case 215139: return setParamChemWavelength(out, 458000, 922, 532.0); case 215140: return setParamChemWavelength(out, 458000, 922, 550.0); case 215141: return setParamChemWavelength(out, 458000, 922, 645.0); case 215142: return setParamChemWavelength(out, 458000, 922, 670.0); case 215143: return setParamChemWavelength(out, 458000, 922, 800.0); case 215144: return setParamChemWavelength(out, 458000, 922, 858.0); case 215145: return setParamChemWavelength(out, 458000, 922, 865.0); case 215146: return setParamChemWavelength(out, 458000, 922, 1020.0); case 215147: return setParamChemWavelength(out, 458000, 922, 1064.0); case 215148: return setParamChemWavelength(out, 458000, 922, 1240.0); case 215149: return setParamChemWavelength(out, 458000, 922, 1640.0); case 215150: return setParamChemWavelength(out, 459000, 922, 340.0); case 215151: return setParamChemWavelength(out, 459000, 922, 355.0); case 215152: return setParamChemWavelength(out, 459000, 922, 380.0); case 215153: return setParamChemWavelength(out, 459000, 922, 400.0); case 215154: return setParamChemWavelength(out, 459000, 922, 440.0); case 215155: return setParamChemWavelength(out, 459000, 922, 469.0); case 215156: return setParamChemWavelength(out, 459000, 922, 500.0); case 215157: return setParamChemWavelength(out, 459000, 922, 532.0); case 215158: return setParamChemWavelength(out, 459000, 922, 550.0); case 215159: return setParamChemWavelength(out, 459000, 922, 645.0); case 215160: return setParamChemWavelength(out, 459000, 922, 670.0); case 215161: return setParamChemWavelength(out, 459000, 922, 800.0); case 215162: return setParamChemWavelength(out, 459000, 922, 858.0); case 215163: return setParamChemWavelength(out, 459000, 922, 865.0); case 215164: return setParamChemWavelength(out, 459000, 922, 1020.0); case 215165: return setParamChemWavelength(out, 459000, 922, 1064.0); case 215166: return setParamChemWavelength(out, 459000, 922, 1240.0); case 215167: return setParamChemWavelength(out, 459000, 922, 1640.0); case 215176: return setParamChemWavelength(out, 472000, 922, 2130.0); case 215177: return setParamChemWavelength(out, 457000, 923, 2130.0); case 215178: return setParamChemWavelength(out, 458000, 922, 2130.0); case 215179: return setParamChemWavelength(out, 459000, 922, 2130.0); case 215180: return setParamChemWavelength(out, 462000, 922, 355.0); case 215181: return setParamChemWavelength(out, 462000, 922, 532.0); case 215182: return setParamChemWavelength(out, 462000, 922, 1064.0); case 215183: return setParamChemWavelength(out, 460000, 922, 355.0); case 215184: return setParamChemWavelength(out, 460000, 922, 532.0); case 215185: return setParamChemWavelength(out, 460000, 922, 1064.0); case 215186: return setParamChemWavelength(out, 461000, 922, 355.0); case 215187: return setParamChemWavelength(out, 461000, 922, 532.0); case 215188: return setParamChemWavelength(out, 461000, 922, 1064.0); case 215189: return setParamChem(out, 453000, 912); case 215190: return setParamChem(out, 453000, 913); case 215191: return setParamChem(out, 406000, 912); case 215192: return setParamChem(out, 406000, 913); case 215193: return setParamChem(out, 407000, 912); case 215194: return setParamChem(out, 407000, 913); case 215195: return setParamChem(out, 410000, 912); case 215196: return setParamChem(out, 410000, 913); case 215197: return setParamChem(out, 411000, 912); case 215198: return setParamChem(out, 411000, 913); case 215199: return setParamChem(out, 451000, 912); case 215200: return setParamChem(out, 451000, 913); case 215201: return setParamChem(out, 401000, 912); case 215202: return setParamChem(out, 401000, 913); case 215203: return setParamChemWavelength(out, 457000, 912, 550.0); case 215204: return setParamChemWavelength(out, 457000, 913, 550.0); case 215205: return setParamChem(out, 453000, 914); case 215206: return setParamChem(out, 406000, 914); case 215207: return setParamChem(out, 407000, 914); case 215208: return setParamChem(out, 410000, 914); case 215209: return setParamChem(out, 411000, 914); case 215210: return setParamChem(out, 451000, 914); case 215211: return setParamChem(out, 401000, 914); case 215212: return setParamChem(out, 453000, 915); case 215213: return setParamChem(out, 406000, 915); case 215214: return setParamChem(out, 407000, 915); case 215215: return setParamChem(out, 410000, 915); case 215216: return setParamChem(out, 411000, 915); case 215217: return setParamChem(out, 451000, 915); case 215218: return setParamChem(out, 401000, 915); case 215219: return setParamChem(out, 453000, 916); case 215220: return setParamChem(out, 406000, 916); case 215221: return setParamChem(out, 407000, 916); case 215222: return setParamChem(out, 410000, 916); case 215223: return setParamChem(out, 411000, 916); case 215224: return setParamChem(out, 451000, 916); case 215225: return setParamChem(out, 401000, 916); case 215226: return setParamChemWavelength(out, 457000, 918, 550.0); case 217003: return setParamChem(out, 402000, 3); case 217004: return setParamChem(out, 402000, 4); case 217006: return setParamChem(out, 402000, 6); case 217007: return setParamChem(out, 402000, 7); case 217009: return setParamChem(out, 402000, 9); case 217010: return setParamChem(out, 402000, 10); case 217011: return setParamChem(out, 402000, 311); case 217012: return setParamChem(out, 402000, 12); case 217013: return setParamChem(out, 402000, 13); case 217014: return setParamChem(out, 402000, 14); case 217015: return setParamChem(out, 402000, 15); case 217016: return setParamChem(out, 402000, 16); case 217018: return setParamChem(out, 402000, 18); case 217019: return setParamChem(out, 402000, 19); case 217020: return setParamChem(out, 402000, 20); case 217021: return setParamChem(out, 402000, 21); case 217022: return setParamChem(out, 402000, 22); case 217023: return setParamChem(out, 402000, 23); case 217024: return setParamChem(out, 402000, 24); case 217026: return setParamChem(out, 402000, 26); case 217027: return setParamChem(out, 402000, 27); case 217028: return setParamChem(out, 402000, 28); case 217029: return setParamChem(out, 402000, 29); case 217030: return setParamChem(out, 402000, 30); case 217032: return setParamChem(out, 402000, 32); case 217033: return setParamChem(out, 402000, 33); case 217034: return setParamChem(out, 402000, 34); case 217035: return setParamChem(out, 402000, 35); case 217036: return setParamChem(out, 402000, 36); case 217037: return setParamChem(out, 402000, 37); case 217038: return setParamChem(out, 402000, 38); case 217039: return setParamChem(out, 402000, 39); case 217040: return setParamChem(out, 402000, 40); case 217041: return setParamChem(out, 402000, 41); case 217042: return setParamChem(out, 402000, 42); case 217043: return setParamChem(out, 402000, 43); case 217044: return setParamChem(out, 402000, 44); case 217045: return setParamChem(out, 402000, 45); case 217046: return setParamChem(out, 402000, 46); case 217047: return setParamChem(out, 402000, 47); case 217048: return setParamChem(out, 402000, 48); case 217049: return setParamChem(out, 402000, 49); case 217050: return setParamChem(out, 402000, 50); case 217051: return setParamChem(out, 402000, 900); case 217052: return setParamChem(out, 402000, 52); case 217053: return setParamChem(out, 402000, 53); case 217054: return setParamChem(out, 402000, 112); case 217055: return setParamChem(out, 402000, 55); case 217056: return setParamChem(out, 402000, 56); case 217057: return setParamChem(out, 402000, 57); case 217058: return setParamChem(out, 402000, 58); case 217059: return setParamChem(out, 402000, 59); case 217063: return setParamChem(out, 402000, 63); case 217064: return setParamChem(out, 402000, 64); case 217065: return setParamChem(out, 402000, 65); case 217066: return setParamChem(out, 402000, 66); case 217067: return setParamChem(out, 402000, 67); case 217068: return setParamChem(out, 402000, 68); case 217069: return setParamChem(out, 402000, 69); case 217070: return setParamChem(out, 402000, 70); case 217071: return setParamChem(out, 402000, 71); case 217072: return setParamChem(out, 402000, 72); case 217073: return setParamChem(out, 402000, 73); case 217074: return setParamChem(out, 402000, 74); case 217075: return setParamChem(out, 402000, 75); case 217076: return setParamChem(out, 402000, 76); case 217077: return setParamChem(out, 402000, 77); case 217078: return setParamChem(out, 402000, 78); case 217079: return setParamChem(out, 402000, 79); case 217080: return setParamChem(out, 402000, 80); case 217082: return setParamChem(out, 402000, 82); case 217083: return setParamChem(out, 402000, 83); case 217085: return setParamChem(out, 402000, 85); case 217086: return setParamChem(out, 402000, 86); case 217099: return setParamChem(out, 402000, 99); case 217100: return setParamChem(out, 402000, 100); case 217101: return setParamChem(out, 402000, 101); case 217107: return setParamChem(out, 402000, 107); case 217118: return setParamChem(out, 402000, 118); case 217159: return setParamChem(out, 402000, 159); case 217161: return setParamChem(out, 402000, 161); case 217169: return setParamChem(out, 402000, 169); case 217173: return setParamChem(out, 402000, 173); case 217174: return setParamChem(out, 402000, 174); case 217175: return setParamChem(out, 402000, 175); case 217176: return setParamChem(out, 402000, 176); case 217177: return setParamChem(out, 402000, 177); case 217178: return setParamChem(out, 402000, 178); case 217186: return setParamChem(out, 402000, 186); case 217187: return setParamChem(out, 402000, 187); case 217188: return setParamChem(out, 402000, 188); case 217189: return setParamChem(out, 402000, 189); case 217190: return setParamChem(out, 402000, 190); case 217191: return setParamChem(out, 402000, 191); case 217192: return setParamChem(out, 402000, 192); case 217193: return setParamChem(out, 402000, 193); case 217194: return setParamChem(out, 402000, 194); case 217195: return setParamChem(out, 402000, 195); case 217196: return setParamChem(out, 402000, 196); case 217197: return setParamChem(out, 402000, 197); case 217198: return setParamChem(out, 402000, 198); case 217199: return setParamChem(out, 402000, 199); case 217200: return setParamChem(out, 402000, 200); case 217201: return setParamChem(out, 402000, 201); case 217202: return setParamChem(out, 402000, 202); case 217203: return setParamChem(out, 402000, 203); case 217204: return setParamChem(out, 402000, 204); case 217206: return setParamChem(out, 402000, 917); case 217222: return setParamChem(out, 402000, 222); case 217224: return setParamChem(out, 402000, 224); case 217225: return setParamChem(out, 402000, 225); case 217226: return setParamChem(out, 402000, 226); case 217227: return setParamChem(out, 402000, 227); case 217228: return setParamChem(out, 402000, 228); case 217229: return setParamChem(out, 402000, 229); case 217230: return setParamChem(out, 402000, 230); case 218003: return setParamChem(out, 401000, 3); case 218004: return setParamChem(out, 401000, 404); case 218006: return setParamChem(out, 401000, 6); case 218007: return setParamChem(out, 401000, 7); case 218009: return setParamChem(out, 401000, 9); case 218010: return setParamChem(out, 401000, 10); case 218011: return setParamChem(out, 401000, 311); case 218012: return setParamChem(out, 401000, 12); case 218013: return setParamChem(out, 401000, 13); case 218014: return setParamChem(out, 401000, 14); case 218015: return setParamChem(out, 401000, 15); case 218016: return setParamChem(out, 401000, 16); case 218018: return setParamChem(out, 401000, 18); case 218019: return setParamChem(out, 401000, 19); case 218020: return setParamChem(out, 401000, 20); case 218021: return setParamChem(out, 401000, 21); case 218022: return setParamChem(out, 401000, 22); case 218023: return setParamChem(out, 401000, 23); case 218024: return setParamChem(out, 401000, 24); case 218026: return setParamChem(out, 401000, 26); case 218027: return setParamChem(out, 401000, 27); case 218028: return setParamChem(out, 401000, 28); case 218029: return setParamChem(out, 401000, 29); case 218030: return setParamChem(out, 401000, 30); case 218032: return setParamChem(out, 401000, 32); case 218033: return setParamChem(out, 401000, 33); case 218034: return setParamChem(out, 401000, 34); case 218035: return setParamChem(out, 401000, 35); case 218036: return setParamChem(out, 401000, 36); case 218037: return setParamChem(out, 401000, 37); case 218038: return setParamChem(out, 401000, 38); case 218039: return setParamChem(out, 401000, 39); case 218040: return setParamChem(out, 401000, 40); case 218041: return setParamChem(out, 401000, 41); case 218042: return setParamChem(out, 401000, 42); case 218043: return setParamChem(out, 401000, 43); case 218044: return setParamChem(out, 401000, 44); case 218045: return setParamChem(out, 401000, 45); case 218046: return setParamChem(out, 401000, 46); case 218047: return setParamChem(out, 401000, 47); case 218048: return setParamChem(out, 401000, 48); case 218049: return setParamChem(out, 401000, 49); case 218050: return setParamChem(out, 401000, 50); case 218051: return setParamChem(out, 401000, 900); case 218052: return setParamChem(out, 401000, 52); case 218053: return setParamChem(out, 401000, 53); case 218054: return setParamChem(out, 401000, 112); case 218055: return setParamChem(out, 401000, 55); case 218056: return setParamChem(out, 401000, 56); case 218057: return setParamChem(out, 401000, 57); case 218058: return setParamChem(out, 401000, 58); case 218059: return setParamChem(out, 401000, 359); case 218063: return setParamChem(out, 401000, 63); case 218064: return setParamChem(out, 401000, 64); case 218065: return setParamChem(out, 401000, 65); case 218066: return setParamChem(out, 401000, 66); case 218067: return setParamChem(out, 401000, 67); case 218068: return setParamChem(out, 401000, 68); case 218069: return setParamChem(out, 401000, 69); case 218070: return setParamChem(out, 401000, 70); case 218071: return setParamChem(out, 401000, 71); case 218072: return setParamChem(out, 401000, 72); case 218073: return setParamChem(out, 401000, 73); case 218074: return setParamChem(out, 401000, 74); case 218075: return setParamChem(out, 401000, 75); case 218076: return setParamChem(out, 401000, 76); case 218077: return setParamChem(out, 401000, 77); case 218078: return setParamChem(out, 401000, 78); case 218079: return setParamChem(out, 401000, 79); case 218080: return setParamChem(out, 401000, 80); case 218082: return setParamChem(out, 401000, 82); case 218083: return setParamChem(out, 401000, 83); case 218085: return setParamChem(out, 401000, 85); case 218086: return setParamChem(out, 401000, 86); case 218099: return setParamChem(out, 401000, 99); case 218100: return setParamChem(out, 401000, 100); case 218101: return setParamChem(out, 401000, 101); case 218107: return setParamChem(out, 401000, 107); case 218117: return setParamChem(out, 401000, 14); case 218159: return setParamChem(out, 401000, 159); case 218161: return setParamChem(out, 401000, 161); case 218169: return setParamChem(out, 401000, 169); case 218173: return setParamChem(out, 401000, 173); case 218174: return setParamChem(out, 401000, 174); case 218175: return setParamChem(out, 401000, 175); case 218176: return setParamChem(out, 401000, 176); case 218177: return setParamChem(out, 401000, 177); case 218178: return setParamChem(out, 401000, 178); case 218186: return setParamChem(out, 401000, 186); case 218187: return setParamChem(out, 401000, 187); case 218188: return setParamChem(out, 401000, 188); case 218189: return setParamChem(out, 401000, 189); case 218190: return setParamChem(out, 401000, 190); case 218191: return setParamChem(out, 401000, 191); case 218192: return setParamChem(out, 401000, 192); case 218193: return setParamChem(out, 401000, 193); case 218194: return setParamChem(out, 401000, 194); case 218195: return setParamChem(out, 401000, 195); case 218196: return setParamChem(out, 401000, 196); case 218197: return setParamChem(out, 401000, 197); case 218198: return setParamChem(out, 401000, 198); case 218199: return setParamChem(out, 401000, 199); case 218200: return setParamChem(out, 401000, 200); case 218201: return setParamChem(out, 401000, 201); case 218202: return setParamChem(out, 401000, 202); case 218203: return setParamChem(out, 401000, 203); case 218204: return setParamChem(out, 401000, 204); case 218206: return setParamChem(out, 401000, 917); case 218221: return setParamChem(out, 401000, 8); case 218222: return setParamChem(out, 401000, 222); case 218224: return setParamChem(out, 401000, 224); case 218225: return setParamChem(out, 401000, 225); case 218226: return setParamChem(out, 401000, 226); case 218227: return setParamChem(out, 401000, 227); case 218228: return setParamChem(out, 401000, 228); case 218229: return setParamChem(out, 401000, 229); case 218230: return setParamChem(out, 401000, 230); case 219207: return setParamChem(out, 469000, 9); case 219208: return setParamChem(out, 469000, 311); case 219209: return setParamChem(out, 469000, 12); case 219212: return setParamChem(out, 469000, 99); case 219213: return setParamChem(out, 469000, 100); case 219219: return setParamChem(out, 469000, 226); case 219220: return setParamChem(out, 469000, 224); case 222001: return setParamChem(out, 445000, 236); case 222006: return setParamChem(out, 445000, 6); case 222013: return setParamChem(out, 445000, 13); case 222015: return setParamChem(out, 445000, 15); case 222017: return setParamChem(out, 445000, 8); case 222019: return setParamChem(out, 445000, 19); case 222027: return setParamChem(out, 445000, 27); case 222031: return setParamChem(out, 445000, 17); case 222033: return setParamChem(out, 445000, 33); case 223006: return setParamChem(out, 444000, 6); case 223013: return setParamChem(out, 444000, 13); case 223015: return setParamChem(out, 444000, 15); case 223017: return setParamChem(out, 444000, 8); case 223019: return setParamChem(out, 444000, 19); case 223026: return setParamChem(out, 444000, 26); case 223027: return setParamChem(out, 444000, 27); case 223031: return setParamChem(out, 444000, 17); case 223033: return setParamChem(out, 444000, 33); default: break; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2marsGenericException("Failed to convert input dictionary in convertChemical", Here())); } } } // namespace metkit::mars2mars::rules::impl metkit-1.19.2/src/metkit/mars2mars/mappings/rules/wave2oper.h0000664000175000017500000000303215232567717024330 0ustar alastairalastair/* * (C) Copyright 2026- 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 wave2oper.h /// @brief Conversion rules used by the mars2mars mapper. #pragma once #include #include "eckit/config/LocalConfiguration.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" namespace metkit::mars2mars::rules::impl { /// @brief Convert surface-like legacy requests into sol layer output. template inline void convertWave2Oper(const InDict_t& in, OutDict_t& out, eckit::LocalConfiguration& misc) { using metkit::mars2mars::utils::dict_traits::get_or_throw; using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { if (get_or_throw(in, "stream") == "wave") { set_or_throw(out, "stream", "oper"); } } catch (...) { // Rethrow nested exceptions std::throw_with_nested(Mars2marsGenericException("Failed to convert input dictionary in wave2oper", Here())); } } } // namespace metkit::mars2mars::rules::impl metkit-1.19.2/src/metkit/mars2mars/mappings/rules/local2wmo.h0000664000175000017500000001624315232567717024325 0ustar alastairalastair/* * (C) Copyright 2026- 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 local2wmo.h /// @brief Conversion rules used by the mars2mars mapper. #pragma once #include "eckit/config/LocalConfiguration.h" #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" #include "metkit/mars2mars/mappings/rules/common.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" #include "metkit/mars2mars/utils/paramMatcher.h" namespace metkit::mars2mars::rules::impl { /// @brief Assign `param`, and `scaleFactor` together. template inline void setParamScale(OutDict_t& out, eckit::LocalConfiguration& misc, long param, double scaleFactor) { using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { set_or_throw(out, "param", param); set_or_throw(misc, "scaleFactor", scaleFactor); } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2marsGenericException("Failed to set param and/or scaleFactor in output dictionaries", Here())); } } /// @brief Convert surface-like legacy requests into sol layer output. template inline void convertLocal2WMO(const InDict_t& in, OutDict_t& out, eckit::LocalConfiguration& misc) { using metkit::mars2mars::utils::dict_traits::get_or_throw; using metkit::mars2mars::utils::dict_traits::set_or_throw; using metkit::mars2mars::utils::exceptions::Mars2marsGenericException; try { const auto param = get_or_throw(in, "param"); switch (param) { // Cloud cover (0-1 -> %) case 164: return setParamScale(out, misc, 228164, 100.0); case 186: return setParamScale(out, misc, 3073, 100.0); case 187: return setParamScale(out, misc, 3074, 100.0); case 188: return setParamScale(out, misc, 3075, 100.0); // Precipitation (m -> kg m-2) case 143: return setParamScale(out, misc, 228143, 1000.0); case 228: return setParamScale(out, misc, 228228, 1000.0); // Runoff water (m -> kg m-2) case 205: return setParamScale(out, misc, 231002, 1000.0); // Snowfall water equivalent (m -> kg m-2) case 144: return setParamScale(out, misc, 228144, 10000.0); // Runoff and water cycle (m -> kg m-2) case 8: return setParamScale(out, misc, 231010, 1000.0); case 9: return setParamScale(out, misc, 231012, 1000.0); case 141: return setParamScale(out, misc, 231141, 1000.0); case 142: return setParamScale(out, misc, 3062, 1000.0); case 182: return setParamScale(out, misc, 260259, 1000.0); case 198: return setParamScale(out, misc, 160198, 1000.0); case 239: return setParamScale(out, misc, 231057, 1000.0); case 240: return setParamScale(out, misc, 231058, 1000.0); case 228216: return setParamScale(out, misc, 231001, 1000.0); // Albedo (0-1 -> %) (for ERA) case 15: return setParamScale(out, misc, 210199, 100.0); case 16: return setParamScale(out, misc, 210198, 100.0); case 17: return setParamScale(out, misc, 210261, 100.0); case 18: return setParamScale(out, misc, 210260, 100.0); case 32: return setParamScale(out, misc, 228032, 100.0); case 174: setParamScale(out, misc, 260509, 100.0); set_or_throw(misc, "typeOfGeneratingProcess", 9); // Climatological return; case 243: return setParamScale(out, misc, 260509, 100.0); case 210186: return setParamScale(out, misc, 210201, 100.0); case 210187: return setParamScale(out, misc, 210202, 100.0); case 210188: return setParamScale(out, misc, 210200, 100.0); case 210189: return setParamScale(out, misc, 210263, 100.0); case 210190: return setParamScale(out, misc, 210264, 100.0); case 210191: return setParamScale(out, misc, 210262, 100.0); // Water cycle (m -> kg m-2) (for ERA) case 44: return setParamScale(out, misc, 231003, 1000.0); case 45: return setParamScale(out, misc, 3099, 1000.0); case 228251: return setParamScale(out, misc, 231005, 1000.0); // Total column ozone: tco3 -> tcioz (kg m-2 -> DU, 1 DU = 2.1415e-5 kg m-2) case 206: return setParamScale(out, misc, 260132, 46698.05); // Time-mean rate (m s-1 -> kg m-2 s-1) case 172008: return setParamScale(out, misc, 235020, 1000.0); case 172009: return setParamScale(out, misc, 235021, 1000.0); case 172044: return setParamScale(out, misc, 235023, 1000.0); case 172045: return setParamScale(out, misc, 235024, 1000.0); case 172142: return setParamScale(out, misc, 235029, 1000.0); case 172143: return setParamScale(out, misc, 235030, 1000.0); case 172144: return setParamScale(out, misc, 235031, 1000.0); case 172182: return setParamScale(out, misc, 235043, 1000.0); case 172205: return setParamScale(out, misc, 235048, 1000.0); case 172228: return setParamScale(out, misc, 235055, 1000.0); case 235141: return setParamScale(out, misc, 235078, 1000.0); // Time-mean cloud cover and albedo (0-1 -> %) case 235186: return setParamScale(out, misc, 235108, 100.0); case 235187: return setParamScale(out, misc, 235109, 100.0); case 235188: return setParamScale(out, misc, 235110, 100.0); case 235243: return setParamScale(out, misc, 235263, 100.0); default: break; } } catch (...) { // Rethrow nested exceptions std::throw_with_nested( Mars2marsGenericException("Failed to convert input dictionary in convertLocal2WMO", Here())); } } } // namespace metkit::mars2mars::rules::impl metkit-1.19.2/src/metkit/mars2mars/api/0000775000175000017500000000000015232567717020050 5ustar alastairalastairmetkit-1.19.2/src/metkit/mars2mars/api/Mars2Mars.cc0000664000175000017500000000511215232567717022165 0ustar alastairalastair/* * (C) Copyright 2026- 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 Mars2Mars.cc /// @brief Implementation of the public Mars2Mars conversion API. /// /// The implementation forwards each supported conversion through the rule /// layer and wraps the result in the common API error-handling policy. /// #include "Mars2Mars.h" #include "eckit/config/LocalConfiguration.h" #include "eckit/exception/Exceptions.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars2mars/mappings/mappings.h" // dictionary access traits #include "metkit/mars2mars/utils/dictionary_traits/dictaccess_eckit_configuration.h" #include "metkit/mars2mars/utils/dictionary_traits/dictaccess_mars_request.h" #include "metkit/mars2mars/utils/dictionary_traits/dictionary_access_traits.h" // Error handling utilities #include "Mars2MarsApiErrorHandling.h" namespace metkit::mars2mars { // ----------------------------------------------------------------------------- // Mars2Mars construction // ----------------------------------------------------------------------------- /// @brief Default construct a Mars2Mars converter. Mars2Mars::Mars2Mars() {} /// @brief Convert an `eckit::LocalConfiguration` request. template <> Mars2MarsResult Mars2Mars::convert( const eckit::LocalConfiguration& mars) { using metkit::mars2mars::utils::exceptions::withMars2MarsApiErrorHandling; return withMars2MarsApiErrorHandling>( "Mars2Mars::convert", [&]() { return rules::convertAll(mars); }, Here()); } /// @brief Convert a `metkit::mars::MarsRequest` request. template <> Mars2MarsResult Mars2Mars::convert( const metkit::mars::MarsRequest& mars) { using metkit::mars2mars::utils::exceptions::withMars2MarsApiErrorHandling; return withMars2MarsApiErrorHandling>( "Mars2Mars::convert", [&]() { return rules::convertAll(mars); }, Here()); } } // namespace metkit::mars2mars metkit-1.19.2/src/metkit/mars2mars/api/Mars2MarsApiErrorHandling.h0000664000175000017500000001275315232567717025151 0ustar alastairalastair/* * (C) Copyright 2026- 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 Mars2MarsApiErrorHandling.h /// @brief Common error handling for mars2mars public API calls. #pragma once #include #include #include #include #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "metkit/mars2mars/utils/mars2marsExceptions.h" namespace metkit::mars2mars::utils::exceptions { namespace detail { /// @brief Environment variable controlling error-stack output. inline constexpr const char* MARS2MARS_ERROR_STACK_DIR_ENV = "MARS2MARS_ERROR_STACK_DIR"; /// @brief Sanitize a string for use in a generated filename. inline std::string sanitizeForFileName(const std::string& value) { std::string out; out.reserve(value.size()); for (const unsigned char c : value) { if (std::isalnum(c) || c == '_' || c == '-' || c == '.') { out.push_back(static_cast(c)); } else { out.push_back('_'); } } return out; } /// @brief Convert the current thread identifier into a filename-safe string. inline std::string currentThreadIdAsString() { std::ostringstream os; os << std::this_thread::get_id(); return sanitizeForFileName(os.str()); } /// @brief Resolve the directory where stack files are stored. inline eckit::PathName errorStackDirectory() { if (const char* value = std::getenv(MARS2MARS_ERROR_STACK_DIR_ENV)) { if (value[0] != '\0') { return eckit::PathName(value).fullName(); } } return eckit::PathName(".").fullName(); } /// @brief Build a unique error-stack file path for an API entry point. inline eckit::PathName makeErrorStackFilePath(const std::string& apiName) { static std::atomic counter{0}; const eckit::PathName dir = errorStackDirectory(); try { dir.mkdir(); } catch (const std::exception& e) { throw eckit::UserError("Unable to create Mars2Mars error stack directory `" + dir.asString() + "`: " + e.what(), Here()); } std::ostringstream fileName; fileName << "mars2mars-error-stack-" << sanitizeForFileName(apiName) << "-pid" << static_cast(::getpid()) << "-tid" << currentThreadIdAsString() << "-" << counter.fetch_add(1, std::memory_order_relaxed) << ".log"; return dir / fileName.str(); } /// @brief Serialize a mars2mars exception stack to disk. inline void writeMars2MarsExceptionStackToFile(const Mars2marsGenericException& exception, const std::string& apiName, const eckit::PathName& errorStackFilePath, const eckit::CodeLocation& loc = eckit::CodeLocation()) { std::ofstream out(errorStackFilePath.asString()); if (!out) { throw eckit::UserError( "Mars2Mars conversion failed, but the error stack file could not " "be opened: `" + errorStackFilePath.asString() + "`", Here()); } out << "Mars2Mars API failure\n"; out << "API entry point: " << apiName << "\n"; out << "Error stack file: " << errorStackFilePath.asString() << "\n"; out << "\n"; out << "Nested exception stack:\n"; out << "-----------------------\n"; out << "+ " << std::string(lineSize, '=') << std::endl << "+ frame " << 0 << std::endl << "+ " << std::string(lineSize, '-') << std::endl; out << "+ file: " << loc.file() << "\n" << "+ function: " << loc.func() << "\n" << "+ line: " << loc.line() << "\n" << "+ link: " << loc.file() << ":" << loc.line() << "\n" << "+ message: " << "Error encountered at: " << apiName << "\n"; out << "+ " << std::string(lineSize, '+') << std::endl; printExtendedStack(exception, out, 1, 1); } } // namespace detail /// @brief Execute a callable under the mars2mars API error boundary. /// /// @tparam Result /// Callable return type. /// /// @tparam Callable /// Callable type. /// /// @param[in] apiName /// API entry-point name used in diagnostics. /// /// @param[in] callable /// Operation to execute. /// /// @param[in] loc /// Source location attached to user-facing failures. template Result withMars2MarsApiErrorHandling(const std::string& apiName, Callable&& callable, const eckit::CodeLocation& loc = eckit::CodeLocation()) { try { return std::forward(callable)(); } catch (const Mars2marsGenericException& e) { const eckit::PathName errorStackFilePath = detail::makeErrorStackFilePath(apiName); std::cout << "Mars2Mars API error" << std::endl; detail::writeMars2MarsExceptionStackToFile(e, apiName, errorStackFilePath, loc); throw eckit::UserError( "Mars2Mars conversion failed. Full error stack written to `" + errorStackFilePath.asString() + "`", loc); } catch (...) { throw; } } } // namespace metkit::mars2mars::utils::exceptions metkit-1.19.2/src/metkit/mars2mars/api/Mars2Mars.h0000664000175000017500000000547215232567717022040 0ustar alastairalastair/* * (C) Copyright 2026- 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 Mars2Mars.h /// @brief Public conversion API for transforming MARS requests. /// /// This header exposes the primary mars2mars entry point. Callers provide a /// supported dictionary type and receive a structured result containing the /// converted MARS request plus auxiliary conversion metadata. /// /// The API is intentionally narrow and explicit: /// - only supported dictionary types may be converted /// - conversion is delegated to the rule engine /// - all failures are routed through the shared API error boundary /// /// @section References /// Implementation: /// - @ref Mars2Mars.cc /// /// Result type: /// - @ref Mars2MarsReturnValue.h /// /// @ingroup mars2mars_api /// #pragma once #include "eckit/config/LocalConfiguration.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars2mars/mappings/Mars2MarsReturnValue.h" namespace metkit::mars2mars { /// /// @brief Main API for converting MARS requests. /// /// The converter owns no runtime configuration state. It is a thin façade /// around the conversion rules and the shared API error handling layer. /// class Mars2Mars { public: /// /// @brief Construct a Mars2Mars converter. /// /// No configuration is stored in the object itself. /// Mars2Mars(); ~Mars2Mars() = default; /// @brief Convert a supported dictionary type. /// /// The primary template is deleted intentionally. Only explicit /// specializations are available through the public API. /// /// @tparam Dict_t /// Input dictionary type. /// /// @param[in] mars /// Input MARS dictionary. /// /// @return /// A converted result for the supported dictionary type. template Mars2MarsResult convert(const Dict_t& mars) = delete; }; // ----------------------------------------------------------------------------- // Supported API specializations // ----------------------------------------------------------------------------- /// @brief Convert an `eckit::LocalConfiguration` request. template <> Mars2MarsResult Mars2Mars::convert( const eckit::LocalConfiguration& mars); /// @brief Convert a `metkit::mars::MarsRequest` request. template <> Mars2MarsResult Mars2Mars::convert( const metkit::mars::MarsRequest& mars); } // namespace metkit::mars2mars metkit-1.19.2/src/metkit/CMakeLists.txt0000664000175000017500000001252115232567717020131 0ustar alastairalastair### config headers ecbuild_generate_config_headers( DESTINATION ${INSTALL_INCLUDE_DIR}/metkit ) configure_file( metkit_config.h.in metkit_config.h ) configure_file( metkit_version.h.in metkit_version.h ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/metkit_config.h ${CMAKE_CURRENT_BINARY_DIR}/metkit_version.h DESTINATION ${INSTALL_INCLUDE_DIR}/metkit ) ### metkit sources list( APPEND metkit_srcs metkit_version.c config/LibMetkit.cc config/LibMetkit.h mars/BaseProtocol.cc mars/BaseProtocol.h mars/ClientTask.cc mars/ClientTask.h mars/DHSProtocol.cc mars/DHSProtocol.h mars/Matcher.cc mars/Matcher.h mars/MarsExpansion.cc mars/MarsExpansion.h mars/MarsHandle.cc mars/MarsHandle.h mars/MarsLanguage.cc mars/MarsLanguage.h mars/MarsLocation.cc mars/MarsLocation.h mars/MarsParsedRequest.cc mars/MarsParsedRequest.h mars/MarsParser.cc mars/MarsParser.h mars/MarsRequest.cc mars/MarsRequest.h mars/MarsRequestHandle.cc mars/MarsRequestHandle.h mars/Parameter.cc mars/Parameter.h mars/ParamID.cc mars/ParamID.h mars/Quantile.cc mars/Quantile.h mars/RequestEnvironment.cc mars/RequestEnvironment.h mars/StepRangeNormalise.h mars/Type.cc mars/Type.h mars/TypeAny.cc mars/TypeAny.h mars/TypeDate.cc mars/TypeDate.h mars/TypeEnum.cc mars/TypeEnum.h mars/TypeExpver.cc mars/TypeExpver.h mars/TypeFloat.cc mars/TypeFloat.h mars/TypeInteger.cc mars/TypeInteger.h mars/TypeLowercase.cc mars/TypeLowercase.h mars/TypeMixed.cc mars/TypeMixed.h mars/TypeParam.cc mars/TypeParam.h mars/TypeRange.cc mars/TypeRange.h mars/TypeRegex.cc mars/TypeRegex.h mars/TypesFactory.cc mars/TypesFactory.h mars/TypeTime.cc mars/TypeTime.h mars/TypeToByList.h mars/TypeToByListQuantile.cc mars/TypeToByListQuantile.h tool/MetkitTool.cc tool/MetkitTool.h fields/FieldIndex.cc fields/FieldIndex.h fields/FieldIndexList.cc fields/FieldIndexList.h fields/SimpleFieldIndex.cc fields/SimpleFieldIndex.h hypercube/HyperCube.cc hypercube/HyperCube.h hypercube/HyperCubePayloaded.h api/metkit_c.cc api/metkit_c.h ) list( APPEND metkit_persistent_srcs mars/Param.cc mars/Param.h mars/Param mars/StepRange.cc mars/StepRange.h mars/StepRange ) list( APPEND metkit_srcs ${metkit_persistent_srcs} ) if ( HAVE_GRIB ) list( APPEND metkit_srcs pointdb/bits.h pointdb/DataSource.cc pointdb/DataSource.h pointdb/FieldIndexer.cc pointdb/FieldIndexer.h pointdb/GribDataSource.cc pointdb/GribDataSource.h pointdb/GribFieldInfo.cc pointdb/GribFieldInfo.h pointdb/GribHandleDataSource.cc pointdb/GribHandleDataSource.h pointdb/masks.h pointdb/PointIndex.cc pointdb/PointIndex.h codes/BUFRDecoder.cc codes/BUFRDecoder.h codes/GRIBDecoder.cc codes/GRIBDecoder.h codes/CodesDataContent.cc codes/CodesDataContent.h codes/BufrContent.cc codes/BufrContent.h codes/CodesSplitter.cc codes/CodesSplitter.h codes/CodesHandleDeleter.h codes/api/CodesAPI.h codes/api/CodesAPI.cc codes/api/CodesTypes.h codes/api/CodesTypes.cc codes/api/KeyIterator.h codes/api/KeyIterator.cc codes/api/GeoIterator.h ) if( NOT eccodes_HAVE_GEOGRAPHY OR NOT eccodes_HAVE_ECKIT_GEO ) list( APPEND metkit_srcs codes/LibEccodes.cc codes/LibEccodes.h ) endif() set( grib_libs eccodes ) endif () if ( HAVE_ODB ) list( APPEND metkit_srcs odb/IdMapper.cc odb/IdMapper.h odb/OdbToRequest.cc odb/OdbToRequest.h odb/OdbMetadataDecoder.cc odb/OdbMetadataDecoder.h odb/OdbDecoder.cc odb/OdbContent.cc odb/OdbContent.h odb/OdbDecoder.h odb/OdbSplitter.cc odb/OdbSplitter.h ) set( odc_libs odccore ) endif() if ( HAVE_GRIB2MARS ) list( APPEND metkit_srcs grib2mars/api/Grib2Mars.cc grib2mars/api/Grib2Mars.h grib2mars/mappings/Grib2MarsReturnValue.h ) endif() if ( HAVE_MARS2MARS ) list( APPEND metkit_srcs mars2mars/api/Mars2Mars.cc mars2mars/api/Mars2Mars.h ) endif() if ( HAVE_MARS2GRIB ) list( APPEND metkit_srcs mars2grib/api/Mars2Grib.cc mars2grib/api/Mars2Grib.h mars2grib/api/Options.h ) set( mars2grib_libs eckit_geo ) endif() ecbuild_add_library( TARGET metkit INSTALL_HEADERS LISTED HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/metkit PERSISTENT ${metkit_persistent_srcs} SOURCES ${metkit_srcs} PUBLIC_INCLUDES $ $ $ # for metkit_version.h PRIVATE_LIBS "${odc_libs}" PUBLIC_LIBS eckit eckit_option ${grib_libs} ${mars2grib_libs} ) if( HAVE_MARS2GRIB_PYTHON ) add_subdirectory(mars2grib/api/pymars2grib) endif() metkit-1.19.2/src/metkit/odb/0000775000175000017500000000000015232567717016134 5ustar alastairalastairmetkit-1.19.2/src/metkit/odb/IdMapper.h0000664000175000017500000000350315232567717020007 0ustar alastairalastair/* * (C) Copyright 1996-2012 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. */ /// @author Piotr Kuchta /// @date April 2011 /// @author Simon Smart /// @date April 2019 #ifndef metkit_odb_IdMapper_H #define metkit_odb_IdMapper_H #include #include #include #include namespace eckit { class PathName; } namespace metkit { namespace odb { //---------------------------------------------------------------------------------------------------------------------- class IdMap { public: // methods IdMap(const std::string& configFile, const std::string& fieldDelimiter = " \t", size_t numericIndex = 0, size_t alphanumericIndex = 1); ~IdMap(); std::string alphanumeric(long numeric); private: // methods std::map numeric2alpha_; }; //---------------------------------------------------------------------------------------------------------------------- class IdMapper { public: // methods static IdMapper& instance(); /// Returns true if modified bool alphanumeric(const std::string& keyword, long numeric, std::string& output); bool alphanumeric(const std::string& keyword, const std::set& numeric, std::set& output); private: // methods IdMapper(); ~IdMapper(); private: // members std::map maps_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace odb } // namespace metkit #endif metkit-1.19.2/src/metkit/odb/OdbToRequest.cc0000664000175000017500000000520115232567717021021 0ustar alastairalastair/* * (C) Copyright 1996-2012 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 "metkit/odb/OdbToRequest.h" #include #include "eckit/io/DataHandle.h" #include "eckit/message/Message.h" #include "odc/api/Odb.h" #include "metkit/config/LibMetkit.h" #include "metkit/odb/OdbMetadataDecoder.h" using namespace eckit; using namespace odc::api; namespace metkit { using namespace mars; using namespace codes; namespace odb { //---------------------------------------------------------------------------------------------------------------------- class MarsRequestSetter : public eckit::message::MetadataGatherer { public: // methods MarsRequestSetter(MarsRequest& request) : request_(request) {} void setValue(const std::string& key, const std::string& value) override { request_.setValue(key, value); } void setValue(const std::string& key, long value) override { request_.setValue(key, value); } void setValue(const std::string& key, double value) override { request_.setValue(key, value); } private: // members MarsRequest& request_; }; //---------------------------------------------------------------------------------------------------------------------- OdbToRequest::OdbToRequest(const std::string& verb, bool one, bool constant) : verb_(verb), one_(one), onlyConstantColumns_(constant) { LOG_DEBUG_LIB(LibMetkit) << "OdbToRequest one: " << one << " constant: " << constant << std::endl; } OdbToRequest::~OdbToRequest() {} std::vector OdbToRequest::odbToRequest(DataHandle& dh) const { LOG_DEBUG_LIB(LibMetkit) << "OdbToRequest::odbToRequest() dh: " << dh << std::endl; Reader reader(dh, false); Frame frame; std::vector requests; while ((frame = reader.next())) { Span span = frame.span(OdbMetadataDecoder::columnNames(), onlyConstantColumns_); MarsRequest r(verb_); MarsRequestSetter setter(r); OdbMetadataDecoder decoder(setter, {}, verb_); span.visit(decoder); if (one_ and requests.size()) { requests.back().merge(r); } else { requests.push_back(r); } } return requests; } //---------------------------------------------------------------------------------------------------------------------- } // namespace odb } // namespace metkit metkit-1.19.2/src/metkit/odb/OdbMetadataDecoder.cc0000664000175000017500000001047015232567717022100 0ustar alastairalastair/* * (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 "metkit/odb/OdbMetadataDecoder.h" #include #include #include #include "eckit/config/Resource.h" #include "eckit/config/YAMLConfiguration.h" #include "eckit/utils/StringTools.h" #include "metkit/config/LibMetkit.h" #include "metkit/mars/MarsRequest.h" #include "metkit/mars/Type.h" #include "metkit/odb/IdMapper.h" namespace { class OdbColumnNameMapping { public: static OdbColumnNameMapping& instance() { static OdbColumnNameMapping mapping; return mapping; } const std::vector& columnNames() { return columnNames_; } const std::map& table() { return mapping_; } private: // methods OdbColumnNameMapping() { static eckit::PathName configPath( eckit::Resource("odbMarsRequestMapping", "~metkit/share/metkit/odb/marsrequest.yaml")); eckit::YAMLConfiguration config(configPath); for (const auto& key : config.keys()) { mapping_[config.getString(key)] = eckit::StringTools::lower(key); } columnNames_.reserve(mapping_.size()); for (const auto& kv : mapping_) { columnNames_.push_back(kv.first); } } private: // members std::map mapping_; std::vector columnNames_; }; } // namespace //---------------------------------------------------------------------------------------------------------------------- namespace metkit::codes { //---------------------------------------------------------------------------------------------------------------------- const std::vector& OdbMetadataDecoder::columnNames() { return OdbColumnNameMapping::instance().columnNames(); } template void OdbMetadataDecoder::visit(const std::string& columnName, const std::set& vals, const metkit::mars::MarsLanguage& language) { auto mapitr = OdbColumnNameMapping::instance().table().find(columnName); ASSERT(mapitr != OdbColumnNameMapping::instance().table().end()); std::string keyword = eckit::StringTools::lower(mapitr->second); metkit::mars::Type* t = language.type(keyword); ASSERT(options_.valueRepresentation == eckit::message::ValueRepresentation::String); for (auto val : vals) { std::string stringVal = eckit::Translator()(val); std::string tidyVal = t->tidy(stringVal); if (stringVal == tidyVal) // if t->tidy had no effect, set the original value gather_.setValue(keyword, val); else gather_.setValue(keyword, tidyVal); } } OdbMetadataDecoder::OdbMetadataDecoder(eckit::message::MetadataGatherer& gather, const eckit::message::GetMetadataOptions& options, const std::string& verb) : language_(verb), gather_(gather), options_(options) {} void OdbMetadataDecoder::operator()(const std::string& columnName, const std::set& vals) { LOG_DEBUG_LIB(LibMetkit) << "OdbMetadataDecoder::operator() columnName: " << columnName << " vals: " << vals << std::endl; auto mapitr = OdbColumnNameMapping::instance().table().find(columnName); ASSERT(mapitr != OdbColumnNameMapping::instance().table().end()); std::set mapped; if (metkit::odb::IdMapper::instance().alphanumeric(mapitr->second, vals, mapped)) { visit(columnName, mapped, language_); } else { visit(columnName, vals, language_); } } void OdbMetadataDecoder::operator()(const std::string& columnName, const std::set& vals) { visit(columnName, vals, language_); } void OdbMetadataDecoder::operator()(const std::string& columnName, const std::set& vals) { visit(columnName, vals, language_); } //---------------------------------------------------------------------------------------------------------------------- } // namespace metkit::codes metkit-1.19.2/src/metkit/odb/OdbContent.cc0000664000175000017500000000253515232567717020507 0ustar alastairalastair/* * (C) Copyright 2017- 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. */ /// @author Baudouin Raoult /// @date Jun 2020 #include "metkit/odb/OdbContent.h" #include "eckit/exception/Exceptions.h" #include "eckit/io/DataHandle.h" #include "eckit/io/MemoryHandle.h" namespace metkit { namespace codes { OdbContent::OdbContent(eckit::Buffer&& buffer) : frame_(std::move(buffer)) {} OdbContent::OdbContent(eckit::DataHandle& handle, size_t size) : frame_(size) { ASSERT(handle.read(frame_, frame_.size()) == frame_.size()); } OdbContent::~OdbContent() {} const void* OdbContent::data() const { return frame_; } size_t OdbContent::length() const { return frame_.size(); } eckit::DataHandle* OdbContent::readHandle() const { return new eckit::MemoryHandle(frame_, frame_.size()); } void OdbContent::write(eckit::DataHandle& handle) const { ASSERT(handle.write(frame_, frame_.size()) == frame_.size()); } void OdbContent::print(std::ostream& s) const { s << "OdbContent[]"; } } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/odb/OdbSplitter.cc0000664000175000017500000000633015232567717020700 0ustar alastairalastair/* * (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 "metkit/odb/OdbSplitter.h" #include "eckit/io/BufferList.h" #include "eckit/io/PeekHandle.h" #include "eckit/message/Message.h" #include "metkit/config/LibMetkit.h" #include "metkit/odb/OdbContent.h" #include "metkit/odb/OdbMetadataDecoder.h" namespace metkit { namespace codes { OdbSplitter::OdbSplitter(eckit::PeekHandle& handle) : Splitter(handle), handleWrapper_(handle), reader_(handleWrapper_, false) {} OdbSplitter::~OdbSplitter() {} eckit::message::Message OdbSplitter::next() { if (!lastFrame_) { lastFrame_ = reader_.next(); if (!lastFrame_) { return eckit::message::Message(); } } eckit::BufferList buffers; odc::api::Span reference = lastFrame_.span(OdbMetadataDecoder::columnNames(), true); buffers.append(lastFrame_.encodedData()); handleWrapper_.clear(); LOG_DEBUG_LIB(LibMetkit) << "ODB frame: " << buffers.count() << ", size: " << lastFrame_.length() << ", total:" << buffers.size() << std::endl; lastFrame_ = odc::api::Frame(); //< we have consumed lastFrame_ odc::api::Frame frame; // aggregate all frames with the same metadata Span as reference Span while ((frame = reader_.next())) { odc::api::Span span = frame.span(OdbMetadataDecoder::columnNames(), true); if (span == reference) { buffers.append(frame.encodedData()); handleWrapper_.clear(); LOG_DEBUG_LIB(LibMetkit) << "ODB frame: " << buffers.count() << ", size: " << frame.length() << ", total:" << buffers.size() << std::endl; } else { // remember last frame to reuse as reference on the following next() lastFrame_ = frame; break; } } LOG_DEBUG_LIB(LibMetkit) << "Consolidating buffers of " << buffers.count() << " frames" << ", total size: " << buffers.size() << std::endl; return eckit::message::Message{new OdbContent(buffers.consolidate())}; } void OdbSplitter::print(std::ostream& s) const { s << "OdbSplitter[]"; } } // namespace codes } // namespace metkit //---------------------------------------------------------------------------------------------------------------------- namespace eckit { namespace message { template <> bool SplitterBuilder::match(eckit::PeekHandle& handle) const { unsigned char c0 = handle.peek(0); unsigned char c1 = handle.peek(1); unsigned char c2 = handle.peek(2); unsigned char c3 = handle.peek(3); unsigned char c4 = handle.peek(4); if (c0 == 0xff and c1 == 0xff and c2 == 'O' and c3 == 'D' and c4 == 'A') { return true; } return false; } } // namespace message } // namespace eckit static eckit::message::SplitterBuilder splitter; metkit-1.19.2/src/metkit/odb/OdbToRequest.h0000664000175000017500000000257315232567717020674 0ustar alastairalastair/* * (C) Copyright 1996-2012 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. */ #ifndef metkit_OdbToRequest_H #define metkit_OdbToRequest_H #include #include #include "eckit/filesystem/PathName.h" #include "eckit/io/Length.h" #include "eckit/io/Offset.h" #include "metkit/mars/MarsRequest.h" namespace eckit { class DataHandle; } namespace metkit { using namespace mars; namespace odb { //---------------------------------------------------------------------------------------------------------------------- class OdbToRequest { public: // methods OdbToRequest(const std::string& verb, bool one, bool constant); ~OdbToRequest(); std::vector odbToRequest(eckit::DataHandle& dh) const; private: // members std::string verb_ = "retrieve"; bool one_ = false; // bool mergeSimilarBlocks_ = false; // unused bool onlyConstantColumns_ = false; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace odb } // namespace metkit #endif metkit-1.19.2/src/metkit/odb/OdbDecoder.cc0000664000175000017500000000436615232567717020446 0ustar alastairalastair/* * (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 "metkit/odb/OdbDecoder.h" #include #include "eckit/config/Resource.h" #include "eckit/config/YAMLConfiguration.h" #include "eckit/io/DataHandle.h" #include "eckit/message/Message.h" #include "eckit/serialisation/MemoryStream.h" #include "eckit/types/Types.h" #include "eckit/utils/StringTools.h" #include "metkit/mars/MarsRequest.h" #include "metkit/odb/OdbMetadataDecoder.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- bool OdbDecoder::match(const eckit::message::Message& msg) const { size_t len = msg.length(); const unsigned char* p = static_cast(msg.data()); return len >= 5 and ((p[0] == 0xff and p[1] == 0xff and p[2] == 'O' and p[3] == 'D' and p[4] == 'A')); } void OdbDecoder::getMetadata(const eckit::message::Message& msg, eckit::message::MetadataGatherer& gather, const eckit::message::GetMetadataOptions& options) const { std::unique_ptr handle(msg.readHandle()); handle->openForRead(); eckit::AutoClose close(*handle); odc::api::Reader reader(*handle, false); odc::api::Frame frame; OdbMetadataDecoder setter(gather, options); while ((frame = reader.next())) { odc::api::Span span = frame.span(OdbMetadataDecoder::columnNames(), true); span.visit(setter); } } eckit::Buffer OdbDecoder::decode(const eckit::message::Message& msg) const { NOTIMP; // Not relevant for MultIO hackathon. Implement as needed in future. } void OdbDecoder::print(std::ostream& s) const { s << "OdbDecoder[]"; } static OdbDecoder decoder; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/odb/OdbMetadataDecoder.h0000664000175000017500000000370415232567717021744 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @date Jun 2020 #ifndef metkit_OdbMetadataDecoder_h #define metkit_OdbMetadataDecoder_h #include "eckit/message/Message.h" #include "metkit/mars/MarsLanguage.h" #include "odc/api/Odb.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------- class OdbMetadataDecoder : public odc::api::SpanVisitor { public: OdbMetadataDecoder(eckit::message::MetadataGatherer& gatherer, const eckit::message::GetMetadataOptions& options, const std::string& verb = "retrieve"); virtual void operator()(const std::string& columnName, const std::set& vals); virtual void operator()(const std::string& columnName, const std::set& vals); virtual void operator()(const std::string& columnName, const std::set& vals); static const std::vector& columnNames(); private: // methods template void visit(const std::string& columnName, const std::set& vals, const metkit::mars::MarsLanguage& language); private: // members metkit::mars::MarsLanguage language_; eckit::message::MetadataGatherer& gather_; eckit::message::GetMetadataOptions options_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit #endif metkit-1.19.2/src/metkit/odb/OdbContent.h0000664000175000017500000000220415232567717020342 0ustar alastairalastair/* * (C) Copyright 2017- 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. */ /// @author Baudouin Raoult /// @date Jun 2020 #ifndef metkit_codes_OdbContent_H #define metkit_codes_OdbContent_H #include "eckit/io/Buffer.h" #include "eckit/message/MessageContent.h" namespace metkit { namespace codes { class OdbContent : public eckit::message::MessageContent { public: OdbContent(eckit::Buffer&&); OdbContent(eckit::DataHandle&, size_t size); ~OdbContent(); private: eckit::Buffer frame_; virtual size_t length() const override; const void* data() const override; virtual eckit::DataHandle* readHandle() const override; virtual void write(eckit::DataHandle& handle) const override; virtual void print(std::ostream& s) const override; }; } // namespace codes } // namespace metkit #endif metkit-1.19.2/src/metkit/odb/OdbSplitter.h0000664000175000017500000000254115232567717020542 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @date Jun 2020 #ifndef metkit_OdbSplitter_h #define metkit_OdbSplitter_h #include "eckit/io/SeekableHandle.h" #include "eckit/message/Splitter.h" #include "odc/api/Odb.h" namespace eckit { class PeekHandle; } namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- class OdbSplitter : public eckit::message::Splitter { public: explicit OdbSplitter(eckit::PeekHandle&); ~OdbSplitter() override; private: // members eckit::SeekableHandle handleWrapper_; odc::api::Reader reader_; odc::api::Frame lastFrame_; private: // methods virtual eckit::message::Message next() override; virtual void print(std::ostream&) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit #endif metkit-1.19.2/src/metkit/odb/IdMapper.cc0000664000175000017500000000705115232567717020147 0ustar alastairalastair/* * (C) Copyright 1996-2012 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 "metkit/odb/IdMapper.h" #include #include #include #include "eckit/config/Resource.h" #include "eckit/filesystem/PathName.h" #include "eckit/log/Log.h" #include "eckit/utils/StringTools.h" #include "metkit/config/LibMetkit.h" using namespace eckit; namespace metkit { namespace odb { //---------------------------------------------------------------------------------------------------------------------- IdMapper::IdMapper() : maps_{{"CLASS", IdMap{"class.table"}}, {"TYPE", IdMap{"type.table"}}, {"STREAM", IdMap{"stream.table"}}, {"OBSGROUP", IdMap{"group.txt", ";", 0, 3}}} {} IdMapper::~IdMapper() {} IdMapper& IdMapper::instance() { static IdMapper themapper; return themapper; } //---------------------------------------------------------------------------------------------------------------------- bool IdMapper::alphanumeric(const std::string& keyword, long numeric, std::string& output) { auto idmap = maps_.find(StringTools::upper(keyword)); if (idmap == maps_.end()) { return false; } output = idmap->second.alphanumeric(numeric); return true; } bool IdMapper::alphanumeric(const std::string& keyword, const std::set& numeric, std::set& output) { auto idmap = maps_.find(StringTools::upper(keyword)); if (idmap == maps_.end()) { return false; } output.clear(); std::transform(numeric.begin(), numeric.end(), std::inserter(output, output.begin()), [&idmap](long l) { return idmap->second.alphanumeric(l); }); return true; } static PathName& codes_path() { static PathName p = Resource("$ODB_CODES", "~metkit/share/metkit/odb"); return p; } IdMap::IdMap(const std::string& configFile, const std::string& fieldDelimiter, size_t numericIndex, size_t alphanumericIndex) { PathName configPath = codes_path() / configFile; LOG_DEBUG_LIB(LibMetkit) << "GribCodesBase::GribCodesBase: config file:" << configPath << std::endl; numeric2alpha_.clear(); std::ifstream fin(configPath.asString()); std::string line; while (std::getline(fin, line)) { std::vector words = StringTools::split(fieldDelimiter, line); if (words.size() >= 2) { long num = eckit::Translator()(StringTools::trim(words[numericIndex])); std::string alpha(StringTools::trim(words[alphanumericIndex])); numeric2alpha_[num] = StringTools::lower(alpha); LOG_DEBUG_LIB(LibMetkit) << "GribCodesBase::readConfig: num='" << num << "' alpha='" << alpha << "'" << std::endl; } } } IdMap::~IdMap() {} std::string IdMap::alphanumeric(long numeric) { auto it = numeric2alpha_.find(numeric); if (it == numeric2alpha_.end()) { std::stringstream ss; ss << "Numeric code " << numeric << " not found"; throw UserError(ss.str(), Here()); } return it->second; } //---------------------------------------------------------------------------------------------------------------------- } // namespace odb } // namespace metkit metkit-1.19.2/src/metkit/odb/OdbDecoder.h0000664000175000017500000000236315232567717020303 0ustar alastairalastair/* * (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. */ /// @author Baudouin Raoult /// @date Jun 2020 #pragma once #include "eckit/message/Decoder.h" namespace metkit { namespace codes { //---------------------------------------------------------------------------------------------------------------------- class OdbDecoder : public eckit::message::MessageDecoder { private: // methods bool match(const eckit::message::Message&) const override; void print(std::ostream&) const override; void getMetadata(const eckit::message::Message& msg, eckit::message::MetadataGatherer&, const eckit::message::GetMetadataOptions&) const override; eckit::Buffer decode(const eckit::message::Message& msg) const override; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace codes } // namespace metkit metkit-1.19.2/src/metkit/metkit_version.c0000664000175000017500000000065215232567717020601 0ustar alastairalastair#include "metkit_version.h" #ifdef __cplusplus extern "C" { #endif const char* metkit_version() { return metkit_VERSION; } unsigned int metkit_version_int() { return 10000 * metkit_VERSION_MAJOR + 100 * metkit_VERSION_MINOR + 1 * metkit_VERSION_PATCH; } const char* metkit_version_str() { return metkit_VERSION_STR; } const char* metkit_git_sha1() { return metkit_GIT_SHA1; } #ifdef __cplusplus } #endif metkit-1.19.2/src/CMakeLists.txt0000664000175000017500000000017315232567717016634 0ustar alastairalastairadd_subdirectory( metkit ) add_subdirectory( tools ) if( HAVE_EXPERIMENTAL ) add_subdirectory( experimental ) endif() metkit-1.19.2/bamboo/0000775000175000017500000000000015232567717014543 5ustar alastairalastairmetkit-1.19.2/bamboo/env.sh0000664000175000017500000000007615232567717015672 0ustar alastairalastair#!/usr/bin/env bash # export VAR=VALUE # ctest_parallel="no" metkit-1.19.2/bamboo/MACOSX-env.sh0000664000175000017500000000007515232567717016661 0ustar alastairalastairexport PATH=$HOME/Applications/CMake.app/Contents/bin:$PATH metkit-1.19.2/bamboo/INTEL-env.sh0000664000175000017500000000066215232567717016544 0ustar alastairalastair#!/bin/bash [[ $(uname) == "Darwin" ]] && return # no module environment on the Mac # initialise module environment if it is not if [[ ! $(command -v module > /dev/null 2>&1) ]]; then . /usr/local/apps/module/init/bash fi module unload grib_api module unload eccodes module unload emos module unload fftw module unload libemos module unload metview module unload netcdf4 module load cmake/3.16.5 module switch gnu intel/17.0.3 metkit-1.19.2/bamboo/CLANG-env.sh0000664000175000017500000000065315232567717016515 0ustar alastairalastair#!/bin/bash [[ $(uname) == "Darwin" ]] && return # no module environment on the Mac # initialise module environment if it is not if [[ ! $(command -v module > /dev/null 2>&1) ]]; then . /usr/local/apps/module/init/bash fi module unload grib_api module unload eccodes module unload emos module unload fftw module unload libemos module unload metview module unload netcdf4 module load cmake/3.16.5 module switch gnu clang metkit-1.19.2/bamboo/GCC-env.sh0000664000175000017500000000062215232567717016261 0ustar alastairalastair#!/bin/bash [[ $(uname) == "Darwin" ]] && return # no module environment on the Mac # initialise module environment if it is not if [[ ! $(command -v module > /dev/null 2>&1) ]]; then . /usr/local/apps/module/init/bash fi module unload grib_api module unload eccodes module unload emos module unload fftw module unload libemos module unload metview module unload netcdf4 module load cmake/3.16.5 metkit-1.19.2/bamboo/CMakeLists.txt0000664000175000017500000000025415232567717017304 0ustar alastairalastairfile( GLOB_RECURSE bamboo_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" ) ecbuild_add_resources( TARGET ${PROJECT_NAME}_bamboo SOURCES_DONT_PACK ${bamboo_files} ) metkit-1.19.2/.git-blame-ignore-revs0000664000175000017500000000070015232567717017401 0ustar alastairalastair# The following refs can be ignored by git blame if 'git blame' is called with # --ignore-revs-file .git-blame-ignore-revs # Alternatively configure git to always ignore refs stored in this file by # calling: # git config blame.ignoreRevsFile .git-blame-ignore-revs # Or extend your git config manually with # [blame] # ignoreRevsFile = .git-blame-ignore-revs # Reformatted codebase with clang-format caa877e4812ef00c792326f8d71683f95422c33f metkit-1.19.2/share/0000775000175000017500000000000015232567717014406 5ustar alastairalastairmetkit-1.19.2/share/metkit/0000775000175000017500000000000015232567717015703 5ustar alastairalastairmetkit-1.19.2/share/metkit/axis.yaml0000664000175000017500000000161715232567717017540 0ustar alastairalastair--- axes: - class - country - type - stream - levtype - origin - product - section - method - system - date - refdate - hdate - offsetdate - time - offsettime - anoffset - reference - dataset - step - fcmonth - fcperiod - leadtime - opttime - expver - domain - diagnostic - iteration - quantile - number - levelist - latitude - longitude - range - param - chem - wavelength - timespan - stattype - ident - obstype - instrument - frequency - direction - channel - obsgroup - reportype - activity - experiment - generation - model - realization - resolution - year - month - bcmodel - icmodel - grib - georef - coeffindex - forcing - configuration - obscutoff metkit-1.19.2/share/metkit/prodgen-paramids.yaml0000664000175000017500000000503615232567717022027 0ustar alastairalastair2: - vpot 15: - auvp 16: - auvd 17: - anip 18: - anid 34: - sstk 39: - swv1 40: - swv2 41: - swv3 42: - swv4 66: - lailv 67: - laihv 77: - etad 139: - st 148: - ss 209: - ttru 928: - thk - thicknes 956: - thka - thicknes anomaly 999: - tc - tropical cyclone 132049: - 10gi 171039: - swv1 - swvl1a 171040: - swv2 - swvl2a 171041: - swv3 - swvl3a 171042: - swv4 - swvl4a # 171031: - ci 172205: - ro # Anomalies ############################################################ 171129: - z 171130: - t 171131: - u 171132: - v 171138: - vo 171139: - stl1 - st 171140: - swl1 171151: - msl 171155: - d 171156: - gh 171157: - r 171170: - slal2 - stl2 171171: - swl2 #171180: #- ewssa 171183: - stl3 171184: - swl3 171207: - 10si 171237: - swl4 # # ############################################################ 172044: - es ##172045: ##- smlt ##- snowmelt ## ##172048: ##- magss ## 172050: - lspf ## 172142: - lsp ## 172143: - cp ## 172144: - sf ## 172146: - sshf - 146 ## 172147: - slhf - 147 ## 172149: - snr ## 172153: - swhr ## 172154: - lwhr ## 172169: - ssrd ## 172175: - strd ## 172176: - ssr ## 172177: - str ## 172178: - tsr ## 172179: - ttr ## 172180: - ewss ## 172181: - nsss ## 172182: - e ## 172189: - sund ## ##172205: ##- ro ##- runoff ## ##172208: ##- tsrc ## ##172209: ##- ttrc ## ##172210: ##- ssrc ## ##172211: ##- strc ## 172228: - tp ## ##172239: ##- csf ## ##172240: ##- lsf ## ##173044: ##- esa ## ##173045: ##- smlta ## ##173048: ##- magssa ## ##173050: ##- lspfa ## 173142: - lspa ## 173143: - cpa ## ##173145: ##- blda ## ##173146: ##- sshfa ## ##173147: ##- slhfa ## ##173149: ##- snra ## ##173153: ##- swhra ## ##173154: ##- lwhra ## 173169: - ssrda - ssrd ## 173175: - strda ## 173176: - ssra - ssr ## 173177: - stra - str ## 173178: - tsra - tsr ## 173179: - ttra - ttr # ##173180: ##- ewssa ## ##173181: ##- nsssa ## 173182: - ea - e ## ##173195: ##- lgwsa ## ##173196: ##- mgwa ## ##173197: ##- gwa ## ##173205: ##- roa ## ##173208: ##- tsrca ## ##173209: ##- ttrca ## ##173210: ##- ssrca ## ##173211: ##- strca ## ##173212: ##- tisra ## ##173239: ##- csfa ## ##173240: ##- lsfa ## ##217004: ##- ch4 ##- methane 228024: - deg0 ## ##228028: ##- i10fg3 ##228053: ##- licga # In the param database, its sysa +2 k 131020: - temperature anomaly of at most -2 k 131021: - temperature anomaly of at least 2 k 131022: - temperature anomaly of at most -8 k 131023: - temperature anomaly of at most -4 k 131024: - temperature anomaly greater than 4 k - temperature anomaly of at least 4 k 131025: - temperature anomaly greater than 8 k - temperature anomaly of at least 8 k metkit-1.19.2/share/metkit/prodgen-params-extra.yaml0000664000175000017500000000103315232567717022624 0ustar alastairalastair# Stuff that is not archived in mars # - - stream: enfh type: - cf - pf - cv - - 1 - 2 - - stream: enfo type: cm - - 130 - - stream: msmm type: em - - 9 # ssro - 8 # sro - 205 # ro - 212 # tisr - - stream: mmsa type: em - - 39 # swvl1 - 40 # swvl2 - 41 # swvl3 - 42 # swvl4 - 172212 #soira - - stream: msmm type: fcmean - - 8 - 205 # ro - 212 # tisr - - stream: mmsa type: fcmean - - 39 # swvl1 - 40 # swvl2 - 41 # swvl3 - 42 # swvl4 metkit-1.19.2/share/metkit/params-static.yaml0000664000175000017500000000632515232567717021345 0ustar alastairalastair- - class: mc levtype: sfc stream: oper type: an - - 32 - 130 - 134 - 164 - 187 - 188 - - class: mc levtype: sfc stream: oper type: fc - - 32 - 58 - 134 - 142 - 143 - 164 - 176 - 177 - 178 - 179 - 187 - 188 - 228 - - class: mc levtype: pl stream: oper type: an - - 130 - 135 - 152 - 157 - 210181 - 217009 - - class: mc levtype: pl stream: oper type: fc - - 130 - 135 - 152 - 210181 - 217009 - - class: mc levtype: ml stream: oper type: an - - 131 - 132 - 135 - 152 - 210181 - 217009 - - class: mc levtype: ml stream: oper type: fc - - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 210181 - 217009 - - class: od levtype: sfc stream: oper type: fc - - 3100 - - class: od levtype: sfc stream: enfo type: pf - - 3100 - - class: ai levtype: sfc stream: oper type: fc - - 140229 - 228141 - 228143 - - class: ai levtype: sfc stream: enfo type: pf - - 140229 - 228141 - 228143 - - class: od levtype: ml stream: oper type: fc - - 210131 - - class: od levtype: pl stream: dacl type: pb - - 129 - 130 - - class: od levtype: ml stream: sens type: sg - - 129 - 155 - 129155 - 155129 - - type: ob - - 129 - - type: ofb - - 129 - - levtype: o2d - - 250001 - 250002 - 250003 - 250004 - 250005 - 250006 - 250007 - 250008 - 250009 - 250010 - 250011 - 250012 - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - 263000 - 263001 - 263002 - 263003 - 263004 - 263005 - 263006 - 263007 - 263100 - 263101 - 263102 - 263103 - 263104 - 263105 - 263106 - 263107 - 263108 - 263109 - 263110 - 263111 - 263112 - 263113 - 263114 - 263115 - 263116 - 263117 - 263118 - 263119 - 263120 - 263121 - 263122 - 263123 - 263124 - 263125 - 263126 - 263127 - 263128 - - levtype: o3d - - 262500 - 262501 - 262502 - 262505 - 262506 - 262507 - 263500 - 263501 - 263502 - 263503 - 263504 - 263505 - 263506 - 263507 - - class: od levtype: sfc stream: oper type: fc - - 160 - 163 - 3062 - 3099 - 160198 - 228032 - 228143 - 228144 - 228164 - 228228 - 228236 - 231001 - 231002 - 231003 - 231010 - 231012 - 260199 - 260227 - 260228 - 260259 - 260360 - 260509 - 262121 - - stream: oper type: ssd - - 260512 metkit-1.19.2/share/metkit/make-yaml.py0000775000175000017500000001154415232567717020142 0ustar alastairalastair#!/usr/bin/env python3 # (c) Copyright 1996-2012 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. # Documentation is available on Confluence # https://confluence.ecmwf.int/display/METK/MetKit+configuration import mysql.connector import yaml import re import os connectionDetails = dict( host = "webapps-db-prod", user = os.environ['PARAM_DB_USER'], passwd = os.environ['PARAM_DB_PASSWORD']) def execute(database, fun, prefix=None, asList=False, lowercase=True): connection = mysql.connector.connect(**connectionDetails) cursor = connection.cursor() cursor.execute("USE " + database) r = fun(cursor, prefix, asList, lowercase) if (prefix) else fun(cursor) cursor.close() connection.close() return r ################################################################################################ def ids(cursor, prefix, asList, lowercase): PRODGEN = {} if os.path.exists("prodgen-" + prefix + "ids.yaml"): with open("prodgen-" + prefix + "ids.yaml") as f: PRODGEN = yaml.load(f.read(), Loader=yaml.FullLoader) IDS = {} if os.path.exists(prefix + "ids.yaml"): with open(prefix + "ids.yaml") as f: IDS = yaml.load(f.read(), Loader=yaml.FullLoader) if asList: # Convert list of lists to dictionary IDS = {item[0]: item[1:] for item in IDS} cursor.execute("select * from " + prefix) for data in cursor.fetchall(): paramid, abbr, longname = int(data[0]), data[1], data[2] if lowercase: abbr = abbr.lower() abbr = re.sub(r"\W", "_", abbr) abbr = re.sub(r"_+", "_", abbr) abbr = re.sub(r"^_", "", abbr) abbr = re.sub(r"_$", "", abbr) if not abbr: abbr = "_%s_%06d" % (prefix, paramid,) entry = [abbr.strip(), longname.strip()] if paramid in PRODGEN: pgen = [str(x).lower() for x in PRODGEN[paramid]] p = [] for n in pgen: if ( n not in entry ): # and (' ' not in n) and ('.' not in n): # and ('-' not in n): entry.append(n) p.append(n) entry = tuple(entry) if entry[0].lower() == entry[1].lower(): entry = tuple([entry[0]]) else: entry = tuple(entry) if paramid in IDS: before = tuple(IDS[paramid]) if before != entry: print( "WARNING! updated {}id: {}, {} => {}".format(prefix, paramid, before, entry) ) IDS[paramid] = list(entry) else: print("new {}id: {} {}".format(prefix, paramid, entry)) IDS[paramid] = list(entry) for paramid, entry in PRODGEN.items(): if paramid not in IDS: print("WARNING! adding pseudo-{}id: {}, {}".format(prefix, paramid, tuple(entry))) IDS[paramid] = entry if asList: # Sort by key and convert to list of lists return [[k] + v for k, v in sorted(IDS.items(), key=lambda item: item[0])] return IDS ################################################################################################ def getListOfRT(cursor, group_id): def sorted(cursor): cursor.execute("SELECT code FROM odbadmin_reporttype WHERE group_id = " + str(group_id)) r = [r[0] for r in cursor.fetchall()] r.sort() return r return sorted(cursor) T = """- context:\n type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa]\n obsgroup: [%(OBSGROUP)s]\n defaults:\n reportype: [%(REPORTYPE_LIST)s]\n""" def reportype(cursor): cursor.execute("SELECT id,marsname FROM odbadmin_group") GROUP = dict([(r[0],str(r[1]).lower()) for r in cursor.fetchall()]) cursor.execute("SELECT DISTINCT group_id FROM odbadmin_reporttype ORDER BY group_id") groups = [r[0] for r in cursor.fetchall()] def expand(g): return T % dict(OBSGROUP = GROUP[g], REPORTYPE_LIST = "/".join([str(r) for r in getListOfRT(cursor, g)])) return "\n".join([expand(group_id) for group_id in groups]) ################################################################################################ def header(f,o): f.write( "---\n# File automatically generated by %s --%s\n# Do not edit\n\n" % (os.path.basename(__file__), o) ) options = 'chem param reportype'.split() def main(): import sys opts = sys.argv[1:] if len(opts) != 1 or opts[0].lstrip('-') not in options: print(sys.argv[0] + ": Possible options: --" + ", --".join(options)) sys.exit(1) o = opts[0].lstrip('-') match o: case 'chem': content = execute("param", ids, o, True, False) with open(o + "ids.yaml", "w") as f: header(f, o) yaml.safe_dump(content, f, default_flow_style=None) case 'param': content = execute("param", ids, o) with open(o + "ids.yaml", "w") as f: header(f, o) yaml.safe_dump(content, f, default_flow_style=False) case 'reportype': with open("reportype.yaml", "w") as f: header(f, o) f.write(execute("odbgov", reportype)) if __name__ == "__main__": main() metkit-1.19.2/share/metkit/obstype.yaml0000664000175000017500000001662615232567717020267 0ustar alastairalastair--- - name: all group: - name: [nsd, conv, conventional, non satellite data all] group: - name: [lsd, land surface data] group: - [1, s, synop land] - [2, s2, synop record 2 land] - [3, sa, synop auto land] - [4, sa2, synop auto record 2 land] - [7, set, soil and earth temperature] - [28, snowd, snow depth] - [140, metar] - [147, ametar, auto metar] - [165, snow, imssnow, snowcover, snow cover] - [170, bs, bufr land synop] - [172, bs2, bufr land synop 2] - [176, bsr6, bufr land synop wmo region 6] - [178, bs1h, bufr land synop 1 hourly] - name: [ssd, sea surface data] group: - [9, sab, synop abbreviated ship] - [11, ss, synop ship] - [12, ss2, synop record 2 ship] - [13,sas, synop auto ship] - [14, sas2, synop auto record 2 ship] - [19, sh, shred] - [21, dsu, dribu surface] - [22, bsu, bathy surface] - [23, tsu, tesac surface] - [26, ostia] - [27, buoy] - [180, bss, bufr ship synop] - [181, bmb, bufr moored buoys] - [182, bdb, bufr drifting buoys] - name: [vsns, vertical soundings not satellite] group: - [91, p, pilot land] - [92, ps, pilot ship] - [95, wp, wind profile] - [96, ewp, european wind profile] - [97, ewtp, european wind and temperature profile] - [101, t, temp land] - [102, ts, temp ship] - [103, td, temp drop] - [104, r, rocob land] - [105, rs, rocob ship] - [106, tm, temp mobile] - [107, ct, climate temp] - [109, hrt, high resolution temp land] - [111, hrts, high resolution temp ship] - [112, bp, bufr land pilot] - [113, bps, bufr ship pilot] - [230, btd, bufr temp drop] - [231, btdc, bufr temp descent] - name: [slns, single level upper air not satellite] group: - [141, cod, codar] - [142, air, airep] - [143, col, colba] - [144, am, amdar] - [145, ac, acars] - [146, eam, e-amdar] - [149, acmr, acars with mixing ratio] - name: [sls, single level upper air satellite] group: - [82, sat2, satob section 2] - [83, sat3, satob section 3] - [84, sat4, satob section 4] - [85, sat5, atob section 5] - [86, sat6, high resolution winds] - [87, sat7, high density unified format satellite winds] - [88, geor, geostationary radiances] - [89, gcsr, geostationary clear sky radiances] - [189, gmsgr, geostationary msg radiances] - [190, gasr, geostationary all sky radiances] - name: [od, oceanographic data] group: - [131, do, dribu oceanographic] - [132, bo, bathy oceanographic] - [133, to, tesac oceanographic] - [184, argo, bufr argo] - name: [dd, derived data] group: - [164, pa, paobs] - [237, bice, baltic_ci, baltic sea ice] - name: [cc, chemical constituent] group: # - [157, so, surface ozone] # - [158, soa, surface ozone averaged] - [162, ovs, ozone vertical sounding] - [159, op, ozone partial] - name: [sf, synoptic features] group: - [31, stor, storm] - [32, tc, tropical cyclone] # ???? - [110, pgps, gbgps, ground based gps] - name: [sd, satellite data all] group: - name: [sds, ers1, surface data satellite] group: - [121, was, wavescat, wave scatterometer] - [122, wis, scat, wind scatterometer] - [123, ral, ralt, radar altimeter] - [124, sst, sea surface temperature] - [210, esar, wavesar, envisat sar] - [212, emeris, envisat_meris, envisat meris] - [213, era, envisat radar altimeter] - [214, jra, jason radar altimeter] - [216, j2ra, jason2 radar altimeter] - [217, cryora, cryosat-2 radar altimeter] - [218, saral, saral radar altimeter] - name: [vss, vertical soundings satellite] group: - [51, hr, high resolution] - [53, rtov, fbtovs, tovh, rtovs] - [54, tovb, tovs1b] - [55, atov, atovs] - [56, atop, atovs products] - [57, airs, fbairs] - [58, amsr3, advanced microwave scanning radiometer 3] - [59, amsre, fbamsre, advanced microwave scanning radiometer] - [60, amsr2, amsr2_gc, advanced microwave scanning radiometer 2] # ???? - [61, smt, satem mean temperature] - [62, sw, satem water] - [63, shl, satem higher level] - [65, slm, satem levels merged] - [71, mt, mean temperature] - [72, w, water] - [73, hl, higher level] - [75, tlm, tovs levels merged] - [138, oscat] - [139, ascat] - [153, mwribt, micro wave radiation imager] - [155, ateu, amsu_ears, atovs eumetsat] - [211, saphir] - [240, iasi, fbiasi, iasi_screened] # ???? - [250, gpsro, fbgpsro, gps radio occultation] - [126, ssmi, microwave images] - [49, ssmis, fbssmis, special sensor microwave imager sounder] - [127, ssbt, fbssmi, sea surface brigthness temperature] - name: trmm group: - [129, tbt, fbtmi, tmi brightness temperatures] - [130, ti3r, tmi instantaneous 3-d rainfall structure] - name: qscat group: - [137, qsca, quikscat] - [138, qssw, quikscat seawinds] - name: reo3 group: - [206, reo3, fbreo3, ozone] - [207, modis, modis aerosol] - [208, limd, layer integrated mass density] - [209, limd_ak, layer integrated mass density with averaging kernels] - [156, wsat, fbwindsat, windsat] - [154, vass, vertical atmospheric sounder system] - [191, mws, microwave sounder] - [201, atms, advanced technology microwave sounder] - [202, cris, cross-track infrared sounder ] - [252, giirs] - [245, hiras2] - [246, aws] - [235, alt] - [226, earthcare_cpr] - [227, earthcare_atl] - [243, visrad] - name: bufr group: [1, 3, 9, 11, 13, 19, 21, 22, 23, 28, 61, 62, 63, 82, 83, 84, 85, 86, 87, 91, 92, 95, 96, 101, 102, 103, 106, 110, 109, 111, 112, 113, 140, 142, 143, 144, 145, 146, 147, 149, 164, 170, 172, 176, 178, 180, 230, 231] - name: fbconv group: [1, 3, 9, 11, 13, 19, 21, 22, 23, 28, 65, 82, 83, 86, 87, 91, 92, 95, 96, 101, 102, 103, 106, 110, 140, 142, 143, 144, 145, 164] - name: fbscat group: [122, 136, 137, 139] - name: fbrad1c group: [54, 55] - name: fbgeos group: [89, 189, 190] - [255, fbtrmr] - [125, rw, nexrad, nexrad_rr, reprocessed wind] - [224, gbt, gpm, gmi brightness temperatures] - name: grad group: [88, 89, 189, 190] - [203, smos] - [204, smap] - name: dmbo group: [181, 182] - [251, aeolus] metkit-1.19.2/share/metkit/reportype.yaml0000664000175000017500000003312215232567717020621 0ustar alastairalastair--- # File automatically generated by make-yaml.py --reportype # Do not edit - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [hirs] defaults: reportype: [1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [amsua] defaults: reportype: [1001/1002/1003/1004/1005/1007/1008/1009/1010] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [amsub] defaults: reportype: [2001/2002/2003/2004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mhs] defaults: reportype: [3001/3002/3003/3004/3005] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [geos] defaults: reportype: [4002/4003/4004/4005/4006/4007/4008/4009/4010/4011/4012/4013/4014/4015/4016/4017/4018/4019/4020/4021/4022/4023/4024/4025/4026/4027/4028/4029/4030/4031/4032/4033/4034/4035/4036] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [resat] defaults: reportype: [5001/5002/5003/5004/5005/5006/5007/5008/5009/5010/5011/5012/5013/5014/5015/5016/5017/5018/5019/5020/5021/5022/5023/5024/5025/5026/5027/5028/5029/5030/5031/5032/5033/5034/5035/5036/5037/5038/5039/5040/5041/5042/5043/5044/5045/5046/5047/5048/5049/5050/5051/5052/5053/5054/5055/5056/5057/5058/5059/5060/5061/5062/5063/5064/5065/5066/5067/5068/5069/5070/5071/5072/5073/5074/5075/5076/5077/5078/5079/5080/5081/5082/5083/5084/5085/5086/5087/5088/5089/5090/5091/5092/5093/5094/5096/5097/5098/5099/5100/5101/5102/5103/5104/5105/5106/5107/5108/5109] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [meris] defaults: reportype: [6001/6002/6003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [gpsro] defaults: reportype: [7001/7002/7003/7004/7005/7006/7007/7008/7009/7010/7011/7012/7013/7014/7015/7016/7017/7018/7019/7020/7021/7022/7023/7024/7025/7026/7027/7028/7029/7030/7031/7032/7033/7034/7035/7036/7037] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [satob] defaults: reportype: [8001/8002/8003/8004/8005/8006/8007/8008/8009/8010/8011/8012/8013/8014/8015/8016/8017/8018/8019/8020/8021/8022/8023/8024/8025/8026/8027/8028/8029/8030/8031/8032/8033/8034/8035/8036/8037/8038/8039/8040/8041/8042/8043/8044/8045/8046/8047/8048/8049/8050/8051/8052/8053/8054/8055/8056/8057/8058/8059/8060/8061/8062/8063/8064/8065/8066/8067/8068/8069] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [scatt] defaults: reportype: [9001/9002/9003/9004/9005/9006/9007/9008/9009/9010/9011/9012/9013/9014/9015/9016/9018/9019/9020] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmi_as] defaults: reportype: [10001/10002/10003/10004/10005/10006/10007/10008/10009/10010/10011/10012/10013/10014] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [iasi] defaults: reportype: [11001/11002/11003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [airs] defaults: reportype: [12001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmis_as] defaults: reportype: [13001/13002/13003/13004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [tmi_as] defaults: reportype: [14001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [amsre_as] defaults: reportype: [15001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [conv] defaults: reportype: [16001/16002/16003/16004/16005/16006/16007/16008/16009/16010/16011/16012/16013/16014/16015/16016/16017/16018/16019/16020/16021/16022/16023/16024/16025/16026/16027/16028/16029/16030/16031/16032/16037/16038/16039/16040/16041/16042/16043/16044/16045/16046/16047/16048/16049/16050/16051/16052/16053/16054/16055/16056/16057/16058/16059/16060/16061/16062/16063/16064/16065/16066/16067/16068/16069/16070/16071/16072/16073/16074/16075/16076/16077/16078/16079/16080/16081/16082/16083/16084/16085/16086/16087/16088/16089/16090/16091] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [storm] defaults: reportype: [17001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [smos] defaults: reportype: [18001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [windsat_as] defaults: reportype: [19001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmi] defaults: reportype: [20001/20002/20003/20004/20005/20006/20007] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [amsua_as] defaults: reportype: [21001/21002/21003/21004/21005/21007/21008/21009/21010] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [amsre] defaults: reportype: [22001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [tmi] defaults: reportype: [23001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmis] defaults: reportype: [24001/24002/24003/24004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [gbrad] defaults: reportype: [25001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mwhs] defaults: reportype: [26001/26002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mwts] defaults: reportype: [27001/27002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mwri_as] defaults: reportype: [28001/28002/28003/28004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [iras] defaults: reportype: [29001/29002/29003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [msu] defaults: reportype: [30001/30002/30003/30004/30005/30006/30007/30008/30009/30010/30011/30012/30013/30014/30015/30016/30017/30018] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssu] defaults: reportype: [31001/31002/31003/31004/31005/31006/31007/31008/31009/31010/31011/31012/31013/31014/31015/31016] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [vtpr1] defaults: reportype: [32001/32002/32003/32004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [vtpr2] defaults: reportype: [33001/33002/33003/33004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [atms] defaults: reportype: [34001/34002/34003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [resat_ak] defaults: reportype: [35001/35002/35003/35004/35005/35006/35007/35008/35009/35010/35011/35012/35013/35014/35015/35016/35017/35018/35019/35020/35021/35022/35023/35024/35025/35026/35027/35028/35029/35030/35031/35032/35033/35034/35035/35036/35037] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [cris] defaults: reportype: [36001/36002/36003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [wave_ip] defaults: reportype: [37001/37002/37003/37004/37005/37006/37007/37008/37009/37010/37012/37013] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [wave_sp] defaults: reportype: [38001/38002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [raingg] defaults: reportype: [39001/39002/39003/39004/39005] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [sfc_ms] defaults: reportype: [40001/40002/40003/40004/40005/40006/40007/40008/40009] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [amsr2_as] defaults: reportype: [41001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [saphir_as] defaults: reportype: [42001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [amsub_as] defaults: reportype: [43001/43002/43003/43004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mhs_as] defaults: reportype: [44001/44002/44003/44004/44005] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [dwl] defaults: reportype: [45001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [iris] defaults: reportype: [46001/46002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [alt] defaults: reportype: [47001/47002/47003/47004/47005/47006/47007/47008/47009/47010/47011/47012/47013/47014/47015/47016/47017] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [aatsr] defaults: reportype: [48001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [atms_as] defaults: reportype: [49001/49002/49003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [gmi_as] defaults: reportype: [50001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [godae_sst] defaults: reportype: [51001/51002/51003/51004/51005/51006/51007/51008/51009] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [atovs_ms] defaults: reportype: [52001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [atmospheric_composition] defaults: reportype: [53001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [non_sfc_ms] defaults: reportype: [54001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mwts2] defaults: reportype: [55001/55002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmi_1d] defaults: reportype: [56001/56002/56003/56004/56005/56006] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mwhs2_as] defaults: reportype: [57001/57002/57003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmt2] defaults: reportype: [58001/58002/58003/58004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [smap] defaults: reportype: [59001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [tovs_ms] defaults: reportype: [60001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [cloud_r] defaults: reportype: [61001/61002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [cloud_l] defaults: reportype: [62001/62002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [satellite_lightning] defaults: reportype: [63001/63002/63003/63004/63005] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [geos_vis] defaults: reportype: [64001/64002/64003/64004/64005/64006/64007/64008] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [oconv] defaults: reportype: [65001/65002/65049/65050/65051/65052/65053/65054/65056/65057/65061/65062/65063/65064/65083/65084/65090] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mwts3_as] defaults: reportype: [66001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [giirs] defaults: reportype: [67001/67002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [hiras] defaults: reportype: [68001/68002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [tropics_as] defaults: reportype: [69001/69002/69003/69004/69005] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmt1] defaults: reportype: [70001/70002/70003/70004/70005/70006] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmt1_as] defaults: reportype: [71001/71002/71003/71004/71005/71006] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mws_as] defaults: reportype: [72001/72002/72003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [mwiici_as] defaults: reportype: [73001/73002/73003] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [aws_as] defaults: reportype: [74001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [leos_vis] defaults: reportype: [75001/75002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssh] defaults: reportype: [76001/76002/76003/76004] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [sirs] defaults: reportype: [77001/77002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [scr] defaults: reportype: [78001/78002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [scams_as] defaults: reportype: [79001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [nems_as] defaults: reportype: [80001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [amsr3_as] defaults: reportype: [81001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [irs] defaults: reportype: [82001/82002] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [iasing] defaults: reportype: [83001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [smap_as] defaults: reportype: [84001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [ssmt2_as] defaults: reportype: [85001/85002/85003/85004/85005] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [smmr_as] defaults: reportype: [86001] - context: type: [ofb,mfb,oai,sfb,fsoifb,fcdfb,ofa] obsgroup: [test] defaults: reportype: [98001/98002/98003/98004/98005/98006/98007/98008/98009/98010/98011/98012/98013/98014/98015/98016/98017/98018/98019/98020/98021/98022/98023/98024/98025/98026/98027/98028/98029/98030/98031/98032/98033/98034/98035/98036/98037/98038/98039/98040] metkit-1.19.2/share/metkit/param-matching.yaml0000664000175000017500000000117415232567717021462 0ustar alastairalastair# wind families used by ParamID normalisation to retrieve u/v from vo/d and related gradients/anomalies wind: # GRIB2 - [131,132,138,155] - [129131,129132,129138,129155] - [171131,171132,171138,171155] - [200131,200132,200138,200155] # GRIB1 - [131.128,132.128,138.128,155.128] - [131.129,132.129,138.129,155.129] - [131.171,132.171,138.171,155.171] - [131.200,132.200,138.200,155.200] # list of tables used by ParamID normalisation to limit the param-table expansion to wind, ocean and reanalysis drop-tables: [140,150,151,160,162,170,180,190] full-table-dropping: true ml-params-single-level: [22, 127, 128, 129, 152] metkit-1.19.2/share/metkit/language.yaml0000664000175000017500000015416515232567717020366 0ustar alastairalastair--- _field: &_field style: type: enum values: - [dissemination] class: category: data defaults: - vals: [od] flatten: false type: enum values: - [a5, ECMWF Atmospheric Composition reanalysis version 5] - [ai, operational aifs] - [at, austria] - [be, belgium] - [c3, c3s] - [ce, cems] - [ch, switzerland] - [ci, cerise] - [co, cosmo] - [cr, cams research] - [cs, ecsn] - [d1, destine] - [de, germany] - [dk, denmark] - [dm, demeter] - [dt, dts] - [e2, e20c] - [e4, reanalyse40] - [e6, era6] - [ea, era5, esat] - [ed, eerie] - [ef, efas] - [eh, c3s europe] - [ei, era interim] - [el, eldas] - [em, e20cm] - [en, ensembles] - [ep, cera-20c, cera20c] - [er, reanalyse] - [es, spain] - [et, cera-sat, cerasat] - [fi, finland] - [fr, france] - [gf, glofas] - [gg, greenhouse gases] - [gh, c3s global] - [gr, greece] - [gw, global wildfire information system] - [hr, croatia] - [hu, hungary] - [ie, ireland] - [is, iceland] - [it, italy] - [j5, jra55] - [l5, era5l] - [l6, era6l] - [la, aladin-laef, laef, lace] - [lr, WMO Lead Centre for Global Climate Re-Analyses] - [lw, WMO lead centre wave forecast verification] - [ma, metaps] - [mc, macc] - [me, mersea] - [ml, machine learning] - [ms, member states] - [ng, nextgems] - [nl, netherlands] - ['no', norway] - [nr, ncep 20cr, 20cr] - [o6, 'ocean 6 reanalysis'] - [od, operations] - [pe, permanent experiment] - [pt, portugal] - [pv, provost] - [rd, research] - [rm, euro4m] - [rr, regional reanalysis] - [s2, s2s] - [se, sweden] - [si, slovenia] - [sr, sreps] - [te, test] - [ti, tigge] - [to, tost] - [tr, turkey] - [uk, united kingdom] - [ul, ulysses] - [ur, uerra] - [yp, yopp] - [yt, yotc] type: category: data defaults: - vals: [an] flatten: false type: enum values: - [3g, 3d variational gradients] - [3v, 3d variational analysis] - [4g, 4d variational gradients] - [4i, 4d variational increments] - [4v, 4d variational analysis] - [ab, analysis bias] - [af, analysis feedback] - [ai, analysis input] - [an, analysis] - [as, adjoint singular vector] - [bf, bias-corrected forecast] - [cd, climate distribution] - [cf, control forecast] - [ci, clustering information] - [cl, climatology] - [cm, cluster means] - [cpf, crossing-point forecast] - [cr, cluster representative] - [cs, cluster std deviations] - [cv, calibration validation forecast] - [ea, errors in analysis] - [ed, empirical distribution] - [ef, errors in first guess] - [efi, extreme forecast index] - [efic, extreme forecast index control] - [em, ensemble mean] - [emdn, ensemble median] - [eme, ensemble data assimilation model errors] - [emtm, ensemble mean of temporal mean] - [ep, event probability] - [es, ensemble standard deviation] - [est, ensemble statistics] - [estdtm, ensemble standard deviation of temporal mean] - [fa, forecast accumulation] - [fb, feedback] - [fc, forecast] - [fcdfb, forecast departures feedback] - [fcmax, forecast maximum] - [fcmean, forecast mean] - [fcmin, forecast minimum] - [fcstdev, forecast standard deviation] - [ff, flux forcing realtime] - [fg, first guess] - [fp, forecast probability] - [fsoifb, forecast sensitivity to observations impact feedback] - [fu, fill-up] - [fx, flux forcing] - [ga, gfas analysis] - [gbf, bias-corrected gridbox] - [gai, gridded analysis input] - [go, gridded observations] - [gsd, gridded satellite data] - [gwt, weather type gridbox] - [hcmean, hindcast mean] - [ia, init. analysis] - [icp, initial condition perturbation] - [mpp, model physics perturbation] - [if, interim forecast] - [im, images] - [me, model errors] - [mfb, mondb feedback] - [oai, odb analysis input] - [ob, observations] - [of, ocean forward] - [ofa, odb feedback from analysis] - [ofb, odb feedback] - [oi, oi analysis] - [oldim, old format images] - [or, ocean reanalysis] - [pa, perturbed analysis] - [pb, probability boundary] - [pd, probability distribution] - [pf, perturbed forecast] - [pfc, point values] - [ppm, point value metrics] - [s3, climate 30 days simulation] - [ses, scaled ensemble standard deviation] - [sf, sensitivity forecast] - [sfb, summary feedback] - [sfo, simulations with forcing] - [sg, sensitivity gradient] - [si, climate simulation] - [sim, simulated images] - [sot, shift of tails] - [ssd, simulated satellite data] - [sv, singular vector] - [svar, signal variance] - [taem, time average ensemble mean] - [taes, time average ensemble standard deviation] - [tpa, time processed analysis] - [tf, trajectory forecast] - [tu, tube] - [wem, weighted ensemble mean] - [wes, weighted ensemble standard deviation] - [wp, weather parameters] stream: category: data flatten: false type: enum values: - [amap, analysis for multianalysis project] - [ammc, melbourne] - [cher, ch, chernobyl] - [clte, climate, Climate run output] - [clmn, climate-monthly, Climate run monthly means output] - [cnrm, meteo france climate centre] - [cwao, montreal] - [dacl, daily climatology] - [dacw, daily climatology wave] - [dahc, daily archive hindcast] - [dame] - [dcda, atmospheric model (delayed cutoff)] - [dcwv, wave model (delayed cutoff)] - [edmm, ensemble data assimilation monthly means] - [edmo, ensemble data assimilation monthly means of daily means] - [edzw, offenbach] - [eefh, extended ensemble forecast hindcast] - [eefo, extended ensemble prediction system] - [eehs, extended ensemble forecast hindcast statistics] - [efas, european flood awareness system (efas)] - [efcl, european flood awareness system (efas) climatology] - [efhc, ensemble forecast hindcasts (obsolete)] - [efho, ensemble forecast hindcast overlap] - [efhs, ensemble forecast hindcast statistics] - [efov, ensemble forecast overlap] - [efrf, european flood awareness system (efas) reforecasts] - [efse, european flood awareness system (efas) seasonal forecasts] - [efsr, european flood awareness system (efas) seasonal reforecasts] - [egrr, exeter, bracknell] - [ehmm, combined multi-model hindcast monthly means] - [elda, ensemble long window data assimilation] - [enda, ensemble data assimilation] - [enfh, ensemble forecast hindcasts] - [enfo, ef, ensemble prediction system] - [enwh, ensemble forecast wave hindcasts] - [esmm, combined multi-model monthly means] - [espd, ensemble supplementary data] - [ewda, ensemble wave data assimilation] - [ewhc, wave ensemble forecast hindcast (obsolete)] - [ewho, ensemble forecast wave hindcast overlap] - [ewla, ensemble wave long window data assimilation] - [ewmm, ensemble wave data assimilation monthly means] - [ewmo, ensemble wave data assimilation monthly means of daily means] - [fgge, fg] - [fsob, forecast sensitivity to observations] - [fsow, forecast sensitivity to observations wave] - [gfas, global fire assimilation system] - [gfra, global fire assimilation system reanalysis] - [kwbc, washington] - [ldas, land data-assimilation system (near real time)] - [ldbt, land data-assimilation system behind real time] - [ldsb, land data-assimilation system statistics behind real time] - [ldst, land data-assimilation system statistics (near real time)] - [lfpw, paris, toulouse] - [lwda, long window daily archive] - [lwwv, long window wave] - [ma, means archive] - [maed, multianalysis ensemble data] - [mawm, wave anomaly means] - [mawv, multianalysis wave data] - [mdfa, monthly means of daily forecast accumulations] - [mfam, anomaly means] - [mfaw, wave anomalies] - [mfhm, hindcast means] - [mfhw, monthly forecast hindcasts wave] - [mfwm, wave real-time means] - [mhwm, wave hindcast means] - [mmaf, multi-model multi-annual forecast] - [mmam, multi-model multi-annual forecast means] - [mmaw, multi-model multi-annual forecast wave] - [mmsa, multi-model seasonal forecast monthly anomalies] - [mmsf, multi-model seasonal forecast] - [mmwm, multi-model multi-annual forecast wave means] - [mnfa, anomalies] - [mnfc, real-time] - [mnfh, hindcasts] - [mnfm, real-time means] - [mnfw, wave real-time] - [mnth, mo, monthly, monthly means] - [mnvr, monthly variance and covariance data using g. boer's step function] - [moda, monthly means of daily means] - [mofc, monthly forecast] - [mofm, monthly forecast means] - [monr, monthly means using g. boer's step function] - [mpic, max plank institute] - [msda, monthly standard deviation and covariance of daily means] - [msdc, mv, monthly standard deviation and covariance] - [msmm, multi-model seasonal forecast atmospheric monthly means] - [mswm, multi-model seasonal forecast wave monthly means] - [ocda, ocean data assimilation] - [ocea, ocean] - [olda, ocean Long window data assimilation] - [oper, da, daily archive, atmospheric model] - [rjtd, tokyo] - [rfsd, retrospective forcing and simulation data] - [scda, atmospheric model (short cutoff)] - [scwv, wave model (short cutoff)] - [seap, sensitive area prediction] - [seas, seasonal forecast] - [sens, sf, sensitivity forecast] - [sfdd, seasonal forecast daily and sub-daily data] - [sfdp, seasonal forecast derived products] - [sfmd, seasonal forecast monthly data] - [sfmm, seasonal forecast atmospheric monthly means] - [shdd, seasonal hindcast daily and sub-daily data] - [shdp, seasonal hindcast derived products] - [shmd, seasonal hindcast monthly data] - [smma, seasonal monthly means anomalies] - [sttd, Statistics of deterministic data] - [stte, Statistics of ensemble data] - [supd, sd, deterministic supplementary data] - [swmm, seasonal forecast wave monthly means] - [toga, tg] - [ukmo, ukmo climate centre] - [waef, we, wave ensemble forecast] - [wamd, wave monthly means of daily means] - [wamf, wave monthly forecast] - [wamo, wave monthly means] - [wams, multi-model seasonal forecast wave] - [wasf, wave seasonal forecast] - [wave, wv, wave model] - [wavm, wave model (standalone)] - [weef, wave extended ensemble forecast] - [weeh, wave extended ensemble forecast hindcast] - [wees, wave extended ensemble forecast hindcast statistics] - [wehs, wave ensemble forecast hindcast statistics] - [weov, wave ensemble forecast overlap] - [wfas, global flood awareness system (glofas)] - [wfcl, global flood awareness system (glofas) climatology] - [wfrf, global flood awareness system (glofas) reforecasts] - [wfse, global flood awareness system (glofas) seasonal forecasts] - [wfsr, global flood awareness system (glofas) seasonal reforecasts] - [wmfm, wave monthly forecast means] - [wvhc, wave hindcast] - [xwda, extended window data assimilation] product: category: data type: enum values: - [inst, instantaneous] - [tims, time-series] - [tavg, time-average] - [tacc, time-accumulation] section: category: data type: enum values: - [h, horizontal] - [v, vertical] - [z, zonal] - [m, meridional] range: category: data type: any use: flatten: false multiple: true type: enum values: - [frequent, f] - [infrequent, i] - [normal, n] - compare - [monthly run] - intent - disk # - [bc, boundary conditions] # - [monday, mon] # - [tuesday, tue] # - [wednesday, wed] # - [thursday, thu] # - [friday, fri] # - [saturday, sat] # - [sunday, sun] # product: # %if LEVTYPE = DP # %and TYPE = OR # %and (%not PRODUCT) # %then # %warning "Default PRODUCT for Ocean ReAnalysis set to Instantaneous" # %set PRODUCT = INST expver: category: data flatten: false type: expver defaults: - vals: ['0001'] dataset: category: data multiple: false type: - context: class: [d1] type: enum values: - [climate-dt, Climate change adaptation digital twin] - [extremes-dt, Weather and geophysical extremes digital twin] - [on-demand-climate-dt, On-demand climate change adaptation digital twin] - [on-demand-extremes-dt, On-demand weather and geophysical extremes digital twin] - type: lowercase model: category: data type: - context: class: [ms] country: [it] type: enum values: - [lm] - [hrm] - [wam] - context: class: [ti, lw, s2] type: enum values: - [lam] - [global, glob] - type: enum values: - [ecmf] - [edzw] - [egrr] - [lfpw] - [kwbc] - [none] - [hrm] - [ifs, IFS with no ocean model] - [ifs-nemo, IFS with NEMO ocean model] - [ifs-fesom, IFS with FESOM ocean model] - [icon, ICON-A with ICON-O ocean model] - [lisflood] - [lm] - [lam] - [aifs] - [aifs-single] - [aifs-single-mse] - [aifs-ens] - [aifs-ens-crps] - [aifs-ens-diff] - [aifs-subs] - [aifs-subs-crps] - [aifs-compo-single] - [aifs-compo-ens] - [aifs-compo-single-mse] - [aifs-compo-ens-crps] - [alaro] - [arome] - [harmonie-arome] - [global, glob] - [ecpoint] - [ecland] - [htessel-lisflood] - [hype] - [jules] - [mhm] - [pcr-globwb] - [vic-wur] forcing: category: data multiple: true type: enum values: - [ecmf-ifs] - [ecmf-aifs] - [ecmf-era5] - [ecmf-seas] - [edzw-icon] - [cosmo-cosmo] - [cmcc-sps] - [obs] configuration: category: data multiple: true type: enum values: - [v1.0] - [v1.0-catchment] - [v1.0-grid] - [v1.0-c3s] - [v2.0] - [v2.1] - [v3.0] - [v3.1] - [v3.5] - [v4.0] - [v5.0] - [v6.0] - [49r2-sfdd] - [50r1-oper] - [50r1-enfo] - [50r1-sfdd] - [50r2-oper] - [50r2-enfo] - [50r2-sfdd] georef: category: data type: lowercase repres: flatten: false multiple: false type: enum values: - [bu, 'bufr'] - [sh, 'spherical harmonics'] - [ll, 'lat long grid'] - [gg, 'gaussian grid'] - [sv, 'space view'] - [og, 'ocean grid'] - [np] - [rl] coeffindex: category: data multiple: true type: to-by-list by: 1 unset: - context: type: op: '!' vals: [me, eme] obscutoff: category: data multiple: true type: time obsgroup: category: data multiple: true type: enum values: # - [conventional] - [sat, satellite] - [ers1] - [trmm] - [qscat] - [reo3] # reo3 needs to stay for compatibility # previously in "obsgroups.def" - [hirs, 1] - [amsua, 2] - [amsub, 3] - [mhs, 4] - [geos, 5] - [resat, 6] - [meris, 7] - [gpsro, 8] - [satob, 9] - [scatt, 10] - [ssmi_as, 11, SSMI ALL-SKY] - [iasi, 12] - [airs, 13] - [ssmis_as, 14, SSMIS ALL-SKY] - [tmi_as, 15, TMI ALL-SKY] - [amsre_as, 16, AMSRE ALL-SKY] - [conv, 17] - [smos, 19] - [windsat_as, 20, WINDSAT ALL-SKY] - [ssmi, 21] - [amsua_as, 22, AMSUA ALL-SKY] - [amsre, 23] - [tmi, 24] - [ssmis, 25] - [gbrad, 26] - [mwhs, 27] - [mwts, 28] - [mwri_as, 29, MWRI ALL-SKY] - [iras, 30] - [msu, 31] - [ssu, 32] - [vtpr1, 33] - [vtpr2, 34] - [atms, 35] - [resat_ak, 36, RESAT AVERAGING KERNELS] - [cris, 37] - [wave_ip, 38, WAVE INTEGRATED PARAMETERS] - [wave_sp, 39, WAVE SPECTRA] - [raingg, 40] - [sfc_ms, 41, SURFACE MULTISENSOR] - [amsr2_as, 42, AMSR-2 ALL-SKY] - [saphir_as, 43, SAPHIR ALL-SKY] - [amsub_as, 44, AMSUB ALL-SKY] - [mhs_as, 45, MHS ALL-SKY] - [dwl, 46, DOPPLER WIND LIDAR] - [iris, 47] - [alt, 48, Altimeter] - [aatsr, 49] - [atms_as, 50, ATMS ALL-SKY] - [gmi_as, 51, GMI ALL-SKY] - [godae_sst, 52, GODAE SEA SURFACE TEMPERATURES] - [atovs_ms, 53, ATOVS MULTISENSOR] - [atmospheric_composition, 54, ATMOSPHERIC COMPOSITION] - [non_sfc_ms, 55, NON-SURFACE MULTISENSOR] - [mwts2, 56] - [ssmi_1d, 57, SSMI 1DVAR TCWV CLOUDY-SKY] - [mwhs2_as, 58, MWHS2 ALL-SKY] - [ssmt2, 59] - [smap, 60] - [tovs_ms, 61, TOVS MULTISENSOR] - [cloud_r, 62, CLOUD REFLECTIVITY] - [cloud_l, 63, CLOUD LIDAR] - [satellite_lightning, 64, SATELLITE LIGHTNING] - [geos_vis, 65, GEOS VIS] - [oconv, 66] - [mwts3_as, 67, MWTS3 All-sky] - [giirs, 68] - [hiras, 69] - [tropics_as, 70, TROPICS All-sky] - [ssmt1, 71] - [ssmt1_as, 72, SSMT1 All-sky] - [mws_as, 73, MWS All-sky] - [mwiici_as, 74, MWIICI All-sky] - [aws_as, 75, AWS All-sky] - [leos_vis, 76, LEOS VIS] - [ssh, 77] - [sirs, 78] - [scr, 79] - [scams_as, 80, SCAMS All-sky] - [nems_as, 81, NEMS All-sky] - [amsr3_as, 82, AMSR-3 All-sky] - [irs, 83] - [iasi_ng, 84, IASI-NG] - [smap_as, 85, SMAP All-sky] - [ssmt2_as, 86, SSMT2 All-sky] - [test, 99] reportype: category: data type: any multiple: true # rdbprefix levtype: category: data aliases: [levtyp] defaults: - context: stream: ['!', dacw, dcwv, ewda, ewhc, ewla, ewmm, ewmo, lwwv, mawm, mawv, mfaw, mfhw, mfwm, mhwm, mnfw, mswm, scwv, swmm, waef, wamd, wamf, wamo, wams, wasf, wave, weef, weeh, wees, wehs, weov, wmfm, wvhc] vals: [pl] - context: stream: [dacw, dcwv, ewda, ewhc, ewla, ewmm, ewmo, lwwv, mawm, mawv, mfaw, mfhw, mfwm, mhwm, mnfw, mswm, scwv, swmm, waef, wamd, wamf, wamo, wams, wasf, wave, weef, weeh, wees, wehs, weov, wmfm, wvhc] vals: [sfc] flatten: false type: [enum, integer] values: - [cat, category] - [dp, depth] - [layer] - [al, abstract levels] - [ml, model levels] - [pl, pressure levels] - [fl, flight levels] - [hl, height levels] - [pt, potential temperature] - [pv, potential vorticity] - [sfc, surf, surface] # sl ?? - [sol, surface other (multi)levels] - [wv, ocean wave] - [o2d, ocean surface] - [o3d, ocean model levels] levelist: category: data aliases: [leve,level,levellist] multiple: true type: to-by-list-float by: 1 units: category: postproc type: enum values: - [si] - [av] param: category: data aliases: [parameter,parameters] multiple: true type: param defaults: - vals: [129] chem: category: data multiple: true type: enum values: "chemids.yaml" wavelength: category: data multiple: true type: range default: none timespan: category: data type: enum values: - [fs, from-start, fromstart] - [10m] - [15m] - [20m] - [30m] - [1h] - [3h] - [6h] - [12h] - [18h] - [24h] - [48h] - [72h] - [120h] - [168h] - [240h] - [360h] - [none, inst, instantaneous] default: none multiple: true stattype: category: data type: enum values: - [moav, monthly average] - [momn, monthly minimum] - [momx, monthly maximum] - [mosd, monthly standard-deviation] - [daac, daily accumulation] - [daav, daily average] - [damn, daily minimum] - [damx, daily maximum] - [dasd, daily standard-deviation] - [moav_daav, monthly average of daily average] - [moav_damn, monthly average of daily minimum] - [moav_damx, monthly average of daily maximum] - [moav_dasd, monthly average of daily standard-deviation] - [momn_daav, monthly minimum of daily average] - [momn_damn, monthly minimum of daily minimum] - [momn_damx, monthly minimum of daily maximum] - [momn_dasd, monthly minimum of daily standard-deviation] - [momx_daav, monthly maximum of daily average] - [momx_damn, monthly maximum of daily minimum] - [momx_damx, monthly maximum of daily maximum] - [momx_dasd, monthly maximum of daily standard-deviation] - [mosd_daav, monthly standard-deviation of daily average] - [mosd_damn, monthly standard-deviation of daily minimum] - [mosd_damx, monthly standard-deviation of daily maximum] - [mosd_dasd, monthly standard-deviation of daily standard-deviation] multiple: false tile: category: data type: enum values: # 1-1000 Reserved for intercomparison tile classes/groupings - [seoc, 1, 'Seas and oceans'] - [inwb, 2, 'Inland water bodies'] - [bare, 3, 'Bare areas'] - [hiv, 4, 'High vegetation'] - [lov, 5, 'Low vegetation'] - [urban, 6] # 1001-1500 Reserved for tile class entries for tile models using the ECOCLIMAP-SG land cover survey - [seao, 1001, 'Sea and oceans'] - [lake, 1002, 'Lakes'] - [rive, 1003, 'Rivers'] - [no, 1004, 'Bare land'] - [rock, 1005, 'Bare rock'] - [snow, 1006, 'Permanent snow'] - [bobd, 1007, 'Boreal broadleaf deciduous'] - [tebd, 1008, 'Temperate broadleaf deciduous'] - [trbd, 1009, 'Tropical broadleaf deciduous'] - [tebe, 1010, 'Temperate broadleaf evergreen'] - [trbe, 1011, 'Tropical broadleaf evergreen'] - [bone, 1012, 'Boreal needleleaf evergreen'] - [tene, 1013, 'Temperate needleleaf evergreen'] - [bond, 1014, 'Boreal needleleaf deciduous'] - [shrb, 1015, 'Shrubs'] - [bogr, 1016, 'Boreal grassland'] - [gras, 1017, 'Temperate grassland'] - [trog, 1018, 'Tropical grassland'] - [c3w, 1019, 'Winter C3 crops'] - [c3s, 1020, 'Summer C3 crops'] - [c4, 1021, 'C4 crops'] - [fltr, 1022, 'Flooded trees'] - [flgr, 1023, 'Flooded grassland'] - [lcz1, 1024, 'Local climate zone 1 - compact high-rise (buildings)'] - [lcz2, 1025, 'Local climate zone 2 - compact midrise (buildings)'] - [lcz3, 1026, 'Local climate zone 3 - compact low-rise (buildings)'] - [lcz4, 1027, 'Local climate zone 4 - open high-rise (buildings)'] - [lcz5, 1028, 'Local climate zone 5 - open midrise (buildings)'] - [lcz6, 1029, 'Local climate zone 6 - open low-rise (buildings)'] - [lcz7, 1030, 'Local climate zone 7 - lightweight low-rise (buildings)'] - [lcz8, 1031, 'Local climate zone 8 - large low-rise (buildings)'] - [lcz9, 1032, 'Local climate zone 9 - sparsely built (buildings)'] - [lcz10, 1033, 'Local climate zone 10 - heavy industry (buildings)'] - [tebdu, 1034, 'Urban temperate broadleaf deciduous'] - [trbdu, 1035, 'Urban tropical broadleaf deciduous'] - [tebeu, 1036, 'Urban temperate broadleaf evergreen'] - [trbeu, 1037, 'Urban tropical broadleaf evergreen'] - [boneu, 1038, 'Urban boreal needleleaf evergreen'] - [teneu, 1039, 'Urban temperate needleleaf evergreen'] - [bondu, 1040, 'Urban boreal needleleaf deciduous'] # 1501-2000 Reserved for tile grouping entries for tile models using the ECOCLIMAP-SG land cover survey - [gnatu, 1501, 'Nature grouping (NO + ROCK + SNOW + BOBD + TEBD + TRBD + TEBE + TRBE + BONE + TENE + BOND + SHRB + BOGR + GRAS + TROG + C3W + C3S + C4 + FLTR + FLGR)'] - [gnofo, 1502, 'Non-forest grouping (NO + ROCK + SNOW + BOGR + GRAS + TROG + C3W + C3S + C4 + FLTR + FLGR)'] - [gfore, 1503, 'Forest grouping (BOBD + TEBD + TRBD + TEBE + TRBE + BONE + TENE + BOND + SHRB)'] - [gnove, 1504, 'Non-vegetated grouping (NO + ROCK + SNOW)'] - [gtree, 1505, 'Trees grouping (BOBD + TEBD + TRBD + TEBE + TRBE + BONE + TENE + BOND + SHRB + FLTR)'] - [glveg, 1506, 'Low vegetation grouping (BOGR + GRAS + TROG + C3W + C3S + C4 + FLGR)'] - [gnflv, 1507, 'Non-flooded low vegetation grouping (BOGR + GRAS + TROG + C3W + C3S + C4)'] - [gcrop, 1508, 'Crops grouping (C3W + C3S + C4)'] - [ggras, 1509, 'Grassland grouping (BOGR + GRAS + TROG)'] - [gbaro, 1510, 'Bare and rock grouping (NO + ROCK)'] - [gc3cr, 1511, 'C3 crops grouping (C3W + C3S)'] - [gbfor, 1512, 'Broadleaf forest grouping (BOBD + TEBD + TRBD + TEBE + TRBE + SHRB)'] - [gnfor, 1513, 'Needleleaf forest grouping (BONE + TENE + BOND)'] - [gdbfo, 1514, 'Deciduous broadleaf forest grouping (BOBD + TEBD + TRBD + TEBE + SHRB)'] - [gtgra, 1515, 'Temperate grassland grouping (BOGR + GRAS)'] - [gnfnf, 1516, 'Non-forest (no FLTR) grouping (NO + ROCK + SNOW + BOGR + GRAS + TROG + C3W + C3S + C4 + FLGR)'] - [gbfft, 1517, 'Broadleaf forest (with FLTR) grouping (BOBD + TEBD + TRBD + TEBE + TRBE + SHRB + FLTR)'] - [gbtre, 1518, 'Broadleaf trees grouping (BOBD + TEBD + TRBD + TEBE + TRBE + FLTR)'] - [gcgra, 1519, 'C3/C4 grasslands grouping (BOGR + GRAS + FLGR)'] - [gbdtr, 1520, 'Broadleaf deciduous trees grouping (BOBD + TEBD + TRBD + FLTR)'] - [gbetr, 1521, 'Broadleaf evergreen trees grouping (TEBE + TRBE)'] - [gnetr, 1522, 'Needleleaf evergreen trees grouping (BONE + TENE)'] - [glczu, 1523, 'LCZ or urban grouping (LCZ1 + LCZ2 + LCZ3 + LCZ4 + LCZ5 + LCZ6 + LCZ7 + LCZ8 + LCZ9 + LCZ10)'] - [giwat, 1524, 'Inland water grouping (LAKE + RIVE)'] - [gptebd, 1525, 'Urban parks and gardens TEBD grouping (NO + GRAS + TEBDU)'] - [gptrbd, 1526, 'Urban parks and gardens TRBD grouping (NO + GRAS + TRBDU)'] - [gptebe, 1527, 'Urban parks and gardens TEBE grouping (NO + GRAS + TEBEU)'] - [gptrbe, 1528, 'Urban parks and gardens TRBE grouping (NO + GRAS + TRBEU)'] - [gpbone, 1529, 'Urban parks and gardens BONE grouping (NO + GRAS + BONEU)'] - [gptene, 1530, 'Urban parks and gardens TENE grouping (NO + GRAS + TENEU)'] - [gpbond, 1531, 'Urban parks and gardens BOND grouping (NO + GRAS + BONDU)'] # 2001-2500 Reserved for tile class entries for tile models using the GLCC v2.0 BATS Classification survey - [crmf, 2001, 'Crops, mixed farming'] - [shgr, 2002, 'Short grass'] - [evne, 2003, 'Evergreen needleleaf trees'] - [dene, 2004, 'Deciduous needleleaf trees'] - [debr, 2005, 'Deciduous broadleaf trees'] - [evbr, 2006, 'Evergreen broadleaf trees'] - [tagr, 2007, 'Tall grass'] - [dese, 2008, 'Desert'] - [tund, 2009, 'Tundra'] - [ircr, 2010, 'Irrigated crops'] - [sede, 2011, 'Semidesert'] - [icgl, 2012, 'Ice caps and glaciers'] - [boma, 2013, 'Bogs and marshes'] - [inwa, 2014, 'Inland water'] - [ocea, 2015, 'Ocean'] - [evsh, 2016, 'Evergreen shrubs'] - [desh, 2017, 'Deciduous shrubs'] - [mfwo, 2018, 'Mixed forest/woodland'] - [info, 2019, 'Interrupted forest'] - [wlmi, 2020, 'Water and land mixtures'] - [baso, 2021, 'Bare soil'] - [urba, 2022, 'Urban (GLCC BATS)'] - [lori, 2023, 'Low-rise building'] - [hiri, 2024, 'High-rise building'] # 2501-3000 Reserved for tile grouping entries for tile models using the GLCC v2.0 BATS Classification survey - [glve, 2501, 'Low vegetation grouping (CRMF + SHGR + TAGR + TUND + IRCR + SEDE + BOMA + EVSH + DESH + WLMI)'] - [ghve, 2502, 'High vegetation grouping (EVNE + DENE + DEBR + EVBR + MFWO + INFO)'] # 3001-3500 Reserved for tile class entries for tile models using the ESA-GlobCover GCV2009 survey - [pfic, 3001, 'Post-flooding or irrigated croplands'] - [racr, 3002, 'Rainfed croplands'] - [mcve, 3003, 'Mosaic cropland (50%-70%)/vegetation (grassland, shrubland, forest) (20-50%)'] - [mvcr, 3004, 'Mosaic vegetation (grassland, shrubland, forest) (50%-70%)/cropland (20%-50%)'] - [cobs, 3005, 'Closed to open (>15%) broadleaved evergreen and/or semi-deciduous forest (>5m)'] - [clbd, 3006, 'Closed (>40%) broadleaved deciduous forest (>5m)'] - [opbd, 3007, 'Open (15-40%) broadleaved deciduous forest (>5m)'] - [clne, 3008, 'Closed (>40%) needleleaved evergreen forest (>5m)'] - [onde, 3009, 'Open (15-40%) needleleaved deciduous or evergreen forest (>5m)'] - [cobn, 3010, 'Closed to open (>15%) mixed broadleaved and needleleaved forest (>5m)'] - [mfsg, 3011, 'Mosaic forest/shrubland (50%-70%)/grassland (20%-50%)'] - [mgfs, 3012, 'Mosaic grassland (50%-70%)/forest/shrubland (20%-50%)'] - [cosh, 3013, 'Closed to open (>15%) shrubland (<5m)'] - [cohv, 3014, 'Closed to open (>15%) herbaceous vegetation'] - [spve, 3015, 'Sparse (<15%) vegetation (woody vegetation, shrubs, grassland)'] - [cobff, 3016, 'Closed to open (>15%) broadleaved forest regularly flooded (semi-permanently or temporarily) - fresh or brackish water'] - [clbff, 3017, 'Closed (>40%) broadleaved forest or shrubland permanently flooded - saline or brackish water'] - [covef, 3018, 'Closed to open (>15%) vegetation (grassland, shrubland, woody vegetation) on regularly flooded or waterlogged soil - fresh, brackish or saline water'] - [asaa, 3019, 'Artificial surfaces and associated areas (urban areas >50%)'] - [baar, 3020, 'Bare areas (GCV2009)'] - [wabo, 3021, 'Water bodies'] - [pesi, 3022, 'Permanent snow and ice'] # 3501-4000 Reserved for tile grouping entries for tile models using the ESA-GlobCover GCV2009 survey - [grdt, 3501, 'Grouping using the dominant tiles approach (dominant tile varies per gridbox)'] # 4001-4500 Reserved for tile class entries for tile models using the European Commission-Global Land Cover Project GLC2000 survey - [tbeco, 4001, 'Tree cover, broadleaved evergreen, closed to open (>15%)'] - [tbdc, 4002, 'Tree cover, broadleaved deciduous, closed (>40%)'] - [tbdo, 4003, 'Tree cover, broadleaved deciduous, open (15-40%)'] - [tneco, 4004, 'Tree cover, needleleaved evergreen, closed to open (>15%)'] - [tndco, 4005, 'Tree cover, needleleaved deciduous, closed to open (>15%)'] - [tmlco, 4006, 'Tree cover, mixed leaftype, closed to open (>15%)'] - [tcoff, 4007, 'Tree cover, closed to open (>15%), regularly flooded, fresh or brackish water - swamp forests'] - [tcofs, 4008, 'Tree cover, closed to open (>15%), regularly flooded, saline water - mangrove forests'] - [mtvg, 4009, 'Mosaic of tree cover and other natural vegetation (crop component possible)'] - [tburn, 4010, 'Tree cover, burnt (mainly boreal forests)'] - [scoe, 4011, 'Shrubcover, closed to open (>15%), evergreen (broadleaved or needleleaved)'] - [scod, 4012, 'Shrubcover, closed to open (>15%), deciduous (broadleaved)'] - [hcco, 4013, 'Herbaceous cover, closed to open (>15%)'] - [shsc, 4014, 'Sparse herbaceous or sparse shrub cover'] - [fshco, 4015, 'Regularly flooded (>2 month) shrub or herbaceous cover, closed to open'] - [crop, 4016, 'Cropland (upland crops or inundated/ flooded crops)'] - [mctnv, 4017, 'Mosaic of cropland / tree cover/ other natural vegetation'] - [mcsh, 4018, 'Mosaic of cropland / shrub or herbaceous cover'] - [baare, 4019, 'Bare areas (GLC2000)'] - [wbna, 4020, 'Water bodies (natural or artificial)'] - [sina, 4021, 'Snow or ice (natural or artificial)'] - [urar, 4022, 'Urban areas'] # 4501-5000 Reserved for tile grouping entries for tile models using the European Commission-Global Land Cover Project GLC2000 survey default: none multiple: true tileattribute: category: data type: enum values: - [undef, 0, Undefined] - [unmod, 1, Unmodified] - [snow, 2, Snow covered] - [flood, 3, Flooded] - [ice, 4, Ice covered] - [itcw, 5, With intercepted water] - [itcs, 6, With intercepted snow] - [agg, 7, Aggregated] - [snow_flood, Snow covered and flooded] - [snow_ice, Snow covered and ice covered] - [snow_itcw, Snow covered and with intercepted water] - [snow_itcs, Snow covered and with intercepted snow] - [flood_ice, Flooded and ice covered] - [flood_itcw, Flooded and with intercepted water] - [flood_itcs, Flooded and with intercepted snow] - [ice_itcw, Ice covered and with intercepted water] - [ice_itcs, Ice covered and with intercepted snow] - [itcw_itcs, With intercepted water and with intercepted snow] - [snow_flood_ice, Snow covered and flooded and ice covered] - [snow_flood_itcw, Snow covered and flooded and with intercepted water] - [snow_flood_itcs, Snow covered and flooded and with intercepted snow] - [snow_ice_itcw, Snow covered and ice covered and with intercepted water] - [snow_ice_itcs, Snow covered and ice covered and with intercepted snow] - [snow_itcw_itcs, Snow covered and with intercepted water and with intercepted snow] - [flood_ice_itcw, Flooded and ice covered and with intercepted water] - [flood_ice_itcs, Flooded and ice covered and with intercepted snow] - [flood_itcw_itcs, Flooded and with intercepted water and with intercepted snow] - [ice_itcw_itcs, Ice covered and with intercepted water and with intercepted snow] - [snow_flood_ice_itcw, Snow covered and flooded and ice covered and with intercepted water] - [snow_flood_ice_itcs, Snow covered and flooded and ice covered and with intercepted snow] - [snow_flood_itcw_itcs, Snow covered and flooded and with intercepted water and with intercepted snow] - [snow_ice_itcw_itcs, Snow covered and ice covered and with intercepted water and with intercepted snow] - [flood_ice_itcw_itcs, Flooded and ice covered and with intercepted water and with intercepted snow] - [snow_flood_ice_itcw_itcs, Snow covered and flooded and ice covered and with intercepted water and with intercepted snow] default: none multiple: true tilescheme: category: data type: - context: class: [d1] dataset: [on-demand-extremes-dt] type: enum values: - [unknown, 0] - [simple, 1, Simple tile scheme configuration with most-grouped tiles] - [granular, 2, Granular tile scheme configuration with most-separated tile groupings] default: none multiple: true ################################################################# # year # decade # month date: category: data multiple: true type: date by: 1 defaults: - vals: [-1] year: category: data type: to-by-list multiple: true by: 1 month: category: data flatten: true type: enum multiple: true values: - [1, jan, january] - [2, feb, february] - [3, mar, march] - [4, apr, april] - [5, may] - [6, jun, june] - [7, jul, july] - [8, aug, august] - [9, sep, september] - [10, oct, october] - [11, nov, november] - [12, dec, december] hdate: category: data multiple: true type: date offsetdate: category: data multiple: true type: date fcmonth: category: data multiple: true by: 1 type: to-by-list fcperiod: category: data multiple: true type: any time: category: data multiple: true type: time by: 6h defaults: - vals: ['1200'] offsettime: category: data multiple: true type: time leadtime: category: data multiple: true type: any opttime: category: data multiple: true type: any step: category: data multiple: true by: 12 type: range defaults: - vals: [0] anoffset: category: data multiple: true type: integer reference: category: data multiple: true type: integer ################################################################# # cluster # probability number: category: data multiple: true type: to-by-list by: 1 quantile: category: data multiple: true type: to-by-list-quantile denominators: [2,3,4,5,10,100,1000] by: 1 domain: category: data type: - context: class: [ms] country: [it] type: enum values: - [italy] - [euroatl] - [europe] - [mediterranean] - type: enum flatten: false type: enum values: - [a, north west europe] - [b, north east europe, baltic and black sea] - [c, south west europe] - [d, south east europe] - [e, europe] - [f, fastex] - [g, globe, general european area] - [h] - [i] - [j] - [k] - [l] - [m, mediterranean] - ['n', northern hemisphere] - [o] - [p] - [q] - [r] - [s, southern hemisphere] - [t, tropics] - [u, tropics 2] - [v] - [w, western atlantic] - [x] - ['y'] - [z] bcmodel: category: data type: - context: class: [ms] country: [it] type: enum values: - [gme] - [ifs] icmodel: category: data type: - context: class: [ms] country: [it] type: enum values: - [gme] - [ifs] - [mvoi] - [nudging] - [3dvar] country: category: data multiple: false type: - context: class: [ms] type: enum values: - [it] - [de] grib: category: data multiple: false type: - context: class: [ms] country: [it] type: enum values: - [original] - [ecmwf] frequency: category: data multiple: true by: 1 type: to-by-list direction: category: data multiple: true by: 1 type: to-by-list diagnostic: category: data type: integer multiple: true iteration: category: data type: integer multiple: true channel: category: data type: integer multiple: true # TODO : Migration from old images to new images ident: category: data type: integer multiple: true instrument: category: data type: integer multiple: true # TODO : Migration from old images to new images method: category: data type: integer origin: category: data multiple: true type: enum values: - [aladinhuneps-omsz-eu] - [aladinlaef-zamg-eu] - [ammc, 1, melbourne] - [anso] - [aromeeps-mf-eu] - [babj, 38, beijing] - [cmcc] - [cnmc, 80] - [consensus, 255] - [cosmo] - [cosmodeeps-dwd-eu] - [cosmoleps-arpasimc-eu] - [crfc, 239, cerfacs] - [cwao, 54, montreal] - [dems] - [ecmf, 98, ecmwf] - [ecmf_ea, ecmwf_ea] - [ecmf_od, ecmwf_od] - [edzw, dwd, 78, offenbach] - [egrr, 74, exeter, bracknell] - [ekmi] - [enmi, 88, oslo] - [eswi] - [fapr] - [fnmo, fnmoc, 58, fleet numerical] - [fr-ms-ec] - [glameps-hirlamcons-eu] - [hadc, 247, hadley centre] - [hirlam-dmi-eu] - [ifmk, 246] - [ingv, 235] - [isac] - [knmi, 245] - [kwbc, 7, washington] - [lemm, 214, madrid] - [lfpw, 84, 85, paris, toulouse] - [lops] - [mogreps-mo-eua] - [nasa] - [niwa] - [no-ar-ce] - [no-ar-cw] - [no-ar-pa] - [nzkl] - [pearp-mf-eu] - [rjtd, 34, tokyo] - [rksl, 40, seoul] - [rums] - [sabm] - [sbsj, 46, cptec] - [se-al-ec] - [sreps-aemet-eua] - [srnwppeps-dwd-eua] - [vabb] - [vuwien, 244, university of vienna] system: category: data type: integer ####################################################################### # DestinE ClimateDT related keywords activity: category: data type: enum values: - [cmip6, Coupled Model Intercomparison Project Phase 6] - [scenariomip, Scenario Model Intercomparison Project] - [highresmip, High Resolution Model Intercomparison Project] - [story-nudging, Climate storylines by nudging to reanalysis] - [baseline, Baseline simulations for climate model evaluation] - [projections, Future climate projections] experiment: category: data type: enum values: - [hist, Historical] - [cont, Control] - [amip, Atmospheric Model Intercomparison Project] - [ssp1-1.9, Shared Socio-economic Pathways 1-1.9] - [ssp1-2.6, Shared Socio-economic Pathways 1-2.6] - [ssp2-4.5, Shared Socio-economic Pathways 2-4.5] - [ssp3-7.0, Shared Socio-economic Pathways 3-7.0] - [ssp5-8.5, Shared Socio-economic Pathways 5-8.5] - [tplus1.5k, Warmer world at 1.5 degrees K above pre-industrial temperatures] - [tplus2.0k, Warmer world at 2.0 degrees K above pre-industrial temperatures] - [tplus3.0k, Warmer world at 3.0 degrees K above pre-industrial temperatures] - [tplus4.0k, Warmer world at 4.0 degrees K above pre-industrial temperatures] - [abrupt4xco2, CO2 abruptly quadrupled and then held constant] generation: category: data type: integer realization: category: data multiple: true type: to-by-list by: 1 resolution: category: data type: enum values: - [standard, Standard resolution model output with longer availability] - [high, High resolution model output with limited availability] # - product # - section # - refdate # - latitude # - longitude # - range ####################################################################### _observation: &_observation obstype: category: data multiple: true defaults: - context: _verb: [retrieve] type: [ob] vals: [1] type: enum values: "obstype.yaml" # TODO : Migration from old images to new images range: category: data type: any latitude: category: data type: any longitude: category: data type: any # type: [im] # obstype: [=0 %and (ident=52 %or ident=53 %or ident=54)) %then # %warning "replacing obstype=0 by channel=2, instrument=205" # %unset obstype # %set channel = 2 # %set instrument = 205 # %if (type=im %and obstype=10 %and (ident=52 %or ident=53 %or ident=54)) %then # %warning "replacing obstype=10 by channel=3, instrument=205" # %unset obstype # %set channel = 3 # %set instrument = 205 # %if (type=im %and obstype=20 %and (ident=52 %or ident=53 %or ident=54)) %then # %warning "replacing obstype=10 by channel=1, instrument=205" # %unset obstype # %set channel = 1 # %set instrument = 205 # # goes # %if (type=im %and obstype=0 %and (ident=252 %or ident=253 %or ident=254 %or ident=255 %or ident=256)) %then # %warning "replacing obstype=0 by channel=4, instrument=615" # %unset obstype # %set channel = 4 # %set instrument = 615 # %if (type=im %and obstype=10 %and (ident=252 %or ident=253 %or ident=254 %or ident=255 %or ident=256)) %then # %warning "replacing obstype=10 by channel=1, instrument=615" # %unset obstype # %set channel = 1 # %set instrument = 615 # %if (type=im %and obstype=20 %and (ident=252 %or ident=253 %or ident=254 %or ident=255 %or ident=256)) %then # %warning "replacing obstype=20 by channel=3, instrument=615" # %unset obstype ####################################################################### _postproc: &_postproc accuracy: category: postproc flatten: false type: [enum, integer] values: - [av, archived value] - ['n', normal, auto] - [r, reduced] - [l, low] bitmap: category: postproc flatten: false type: any format: category: postproc flatten: false type: enum values: - [grib1, grib, gb, grib edition 1] - [grib2, grib edition 2] - [bufr, bf] - [grid, gd] - [netcdf] - [odb] - [ascii] - [p, packed] - [u, unpacked] frame: category: postproc type: integer gaussian: category: postproc type: enum values: - reduced - regular area: category: postproc flatten: false multiple: true type: [float, enum] values: - [g, global] - [e, europe] grid: category: postproc flatten: false multiple: true type: [enum, regex, float] values: - auto - [av, archived value] - [F16, 16] - [F24, 24] - [F32, 32] - [F48, 48] - [F64, 64] - [F80, 80] - [F96, 96] - [F128, 128] - [F160, 160] - [F192, 192] - [F200, 200] - [F256, 256] - [F320, 320] - [F400, 400] - [F512, 512] - [F576, 576] - [F640, 640] - [F800, 800] - [F912, 912] - [F1024, 1024] - [F1280, 1280] - [F1600, 1600] - [F2000, 2000] - [F2560, 2560] - [F4000, 4000] - [F8000, 8000] regex: - '^[oOfF][1-9][0-9]*$' - '^[nN](32|48|64|80|96|128|160|200|256|320|400|512|640|800|1024|1280|8000)$' - '^[hH][rRnN]?(1|2|4|8|16|32|64|128|256|512|1024|2048|4096|8192)$' uppercase: true interpolation: category: postproc flatten: false type: enum values: - [linear] - [bilinear] - [nearest-lsm, nlsm, nearest lsm] - [nearest-neighbour, nn, nearest neighbour] - [grid-box-average, average] packing: category: postproc flatten: false type: enum values: - [so, second order] - [av] - [complex, co] - [simple, si] - [ccsds] resol: category: postproc flatten: false type: [enum, integer] values: - [av, archived value] - [auto, automatic resolution] - [N128, reduced gaussian 128] - [N200, reduced gaussian 200] rotation: category: postproc flatten: false multiple: true type: float intgrid: category: postproc flatten: false type: [enum, regex] values: - auto - none - source - N32 - N48 - N64 - N80 - N96 - N128 - N160 - N192 - N200 - N256 - N320 - N400 - N512 - N640 - N800 - N912 - N1024 - N1280 regex: - '^[oOfFhH][1-9][0-9]+$' truncation: category: postproc flatten: false type: [enum, integer] values: - auto - none process: category: postproc flatten: false type: enum values: - local - server ####################################################################### _obspproc: &_obspproc filter: type: any category: postproc ident: type: any category: postproc ####################################################################### disseminate: <<: *_field <<: *_postproc requirements: type: any resol: category: postproc type: any # repres: # category: postproc # type: any day: category: postproc multiple: true type: to-by-list by: 1 range: [1, 31] use: category: sink flatten: false multiple: true type: enum values: # - [frequent, f] # - [infrequent, i] # - [normal, n] # - compare # - [monthly run] # - intent # - disk - [bc, boundary conditions] - [monday, mon] - [tuesday, tue] - [wednesday, wed] - [thursday, thu] - [friday, fri] - [saturday, sat] - [sunday, sun] option: category: sink default: normal flatten: false multiple: true type: enum values: - normal - delay - asap - gts - opendata compatibility: category: postproc flatten: false multiple: true type: enum values: - 'no-local-extension' - 'adjust-spectral-packing' priority: category: sink flatten: false type: integer target: category: sink flatten: false type: any filename: category: sink flatten: false type: any ############################################################## archive: <<: *_field <<: *_observation resol: category: postproc type: any # repres: # category: postproc # type: any database: flatten: false multiple: true type: any # TODO set source: category: sink flatten: false multiple: true type: any sourcebase: category: sink flatten: false multiple: true type: any expect: category: sink flatten: false multiple: false type: [enum, integer] values: - any disp: category: sink flatten: false multiple: false type: enum values: - [n, new] - [old] - [remove] - [fail] _clear_defaults: [step] ############################################################## retrieve: _aliases: [r, ret, retr] <<: *_field <<: *_observation <<: *_postproc <<: *_obspproc target: category: sink flatten: false multiple: true type: any source: category: sink flatten: false multiple: true type: any expect: category: sink flatten: false multiple: false type: [enum, integer] values: - any fieldset: category: sink aliases: [field] flatten: false multiple: false type: any database: category: sink flatten: false multiple: true aliases: [dbase] type: any optimise: category: sink type: enum values: - 'on' - 'off' defaults: - vals: ["off"] duplicates: category: sink defaults: - context: _verb: [retrieve] type: [ob] vals: [keep] type: enum values: - keep - remove padding: category: sink flatten: false type: enum defaults: - context: _verb: [retrieve] class: [ti] vals: [0] values: - [0, none, n] - [auto, automatic] ############################################################## read: source: category: sink flatten: false multiple: true type: any <<: *_field <<: *_observation <<: *_postproc <<: *_obspproc target: category: sink flatten: false multiple: true type: any fieldset: category: sink aliases: [field] flatten: false multiple: false type: any _clear_defaults: [class, date, domain, expver, levelist, levtype, param, step, stream, time, type] _options: param: # expand_with: # In case not type/stream/levtype is provided # type: an # stream: oper # levtype: pl first_rule: true ############################################################## flush: <<: *_field <<: *_observation <<: *_postproc <<: *_obspproc wait: flatten: false multiple: false type: enum values: - [true, yes, y, on, 1] - [false, no, n, off, 0] decache: flatten: false multiple: false type: enum values: - [true, yes, y, on, 1] - [false, no, n, off, 0] _clear_defaults: [class, date, domain, expver, levelist, levtype, param, step, stream, time, type] ############################################################## get: tape: category: sink flatten: false multiple: false type: any database: category: sink flatten: false multiple: true type: any target: category: sink flatten: false multiple: true type: any ############################################################## list: <<: *_field <<: *_observation database: category: sink flatten: false multiple: true type: any target: category: sink flatten: false multiple: true type: any pseudodate: flatten: false multiple: false type: enum values: - [latest] - [earliest] output: category: sink flatten: false multiple: false defaults: - vals: [table] type: enum values: - [table] - [browse] - [cost] - [search] - [tree] - [json] _clear_defaults: [date, domain, expver, levelist, levtype, param, step, stream, time, type] _multiple: [class, expver, stream, type] ############################################################## compute: formula: flatten: false multiple: false type: any fieldset: category: sink aliases: [field] flatten: false multiple: false type: any ############################################################## write: fieldset: category: sink aliases: [field] flatten: false multiple: false type: any target: category: sink flatten: false multiple: true type: any ############################################################## pointdb: lat: multiple: false type: float lon: multiple: false type: float <<: *_field _clear_defaults: [class, date, domain, expver, levelist, levtype, param, step, stream, time, type] _options: param: # expand_with: # In case not type/stream/levtype is provided # type: an # stream: oper # levtype: pl first_rule: true end: {} metkit-1.19.2/share/metkit/check-params-yaml.py0000775000175000017500000000106715232567717021562 0ustar alastairalastair#!/usr/bin/env python import MySQLdb import yaml import re import os db = MySQLdb.connect( "grib-param-db-prod.ecmwf.int", os.environ['PARAM_DB_USER'], os.environ['PARAM_DB_PASSWORD'], "param") cursor = db.cursor() with open("params.yaml") as f: PARAMS = yaml.load(f.read()) for entry in PARAMS: when, parms = entry for p in parms: cursor.execute("select count(*) from param where id=%s" % p) for data in cursor.fetchall(): if data[0] == 0: print("%s: %s is not in param DB" % (when, p)) metkit-1.19.2/share/metkit/modifiers.yaml0000664000175000017500000001527415232567717020561 0ustar alastairalastair--- - context: _verb: [list] output: ['!', browser] class: 'undefined' set: class: [od] # type = ob is already the default && stream comes after type in axis order # - context: # _verb: [retrieve] # stream: [ssmi] # set: # type: [ob] - context: _verb: [list] output: ['!', browser] class: ['!', rd'] defaults: type: [an] stream: [oper] expver: [0001] - context: _verb: [list] output: ['!', browser] class: ['!', rd] pseudodate: 'undefined' defaults: date: [-1] - context: _verb: [list] output: ['!', browser] class: [rd] expver: 'undefined' error: "Missing expver. Please, specify one" - context: class: [ti] defaults: stream: [enfo] - context: class: ['!', ti] defaults: stream: [oper] - context: _verb: [retrieve] stream: [ssbt, e1, ssmi] set: stream: [oper] warn: "Changed stream=oper" - context: class: [ti] type: [fc] set: stream: [oper] - context: class: [ti, s2, ur, rr, lw] expver: 'undefined' set: expver: [prod] - context: class: [ml, ur, rr] unset: [model] - context: _verb: [retrieve] class: [ai] defaults: model: [aifs-single-mse] - context: class: ['!', d1] dataset: ['!', on-demand-extremes-dt] unset: [georef] - context: type: ['!', oldim, ob, fb, ai, af, ab, tf, ofb, mfb, oai, sfb, fsoifb, fcdfb, ofa] unset: [obsgroup, obstype, duplicates] - context: stream: ['!', ssmi] type: [im, sim, oldim] unset: [levtype, levelist, param, grid, rotation, duplicates] - context: type: [ssd, mpp, gsd, ob, fb, ai, af, ab, ofb, mfb, oai, sfb, fsoifb, fcdfb, ofa] unset: [levtype] - context: stream: [ssmi] unset: [levtype, levelist, param, grid, rotation] - context: levtype: [ml] defaults: levelist: [1] - context: levtype: [pl] defaults: levelist: [1000, 850, 700, 500, 400, 300] - context: levtype: [fl] defaults: levelist: [100, 140, 180, 240, 300, 340, 390, 450] - context: stream: ['!', xwda] unset: [obscutoff] - context: type: [ssd, mpp, gsd, ob, fb, ai, af, ab, ofb, mfb, oai, sfb, fsoifb, fcdfb, tf, ofa] unset: [levelist] - context: levtype: [o2d, sfc, wave, al] unset: [levelist] - context: class: [er] levtype: [o3d] unset: [levelist] - context: section: [m, z] product: [inst, tacc, tavg] levtype: [dp] unset: [levelist] - context: section: [v] product: [tims] levtype: [dp] unset: [levelist] - context: type: [ob, fb, ai, af, ab, ofb, mfb, oai, sfb, fsoifb, fcdfb, tf, ofa] unset: [param] - context: stream: [mfhm] type: [em, es, ses] unset: [date] - context: stream: [mnfh] type: [em, es, ses, ed] unset: [date] - context: stream: [clmn] unset: [date] - context: stream: ['!', enfh, enwh, efho, ehmm, ewho, eefh, weeh, efrf, wfrf, efsr, wfsr, wfcl, efcl] unset: [hdate] - context: class: [dm] type: [fc] set: time: [0] - context: type: [ab] unset: [time] - context: stream: [clmn, dame, mdfa] unset: [time] - context: class: ['!', er] stream: [moda, msda, edmo, ewmo, wamd] unset: [time] - context: class: ['!', to, dt] unset: [leadtim, opttime] - context: stream: ['!', seap] unset: [leadtime, opttime] - context: stream: ['!', ssmi] type: [im, oldim] unset: [step] - context: stream: [ssmi, mnfm, mfhm, mfam, mfwm, mhwm, esmm, ehmm, mawm, dame, sfmm, swmm, smma, mmsa] unset: [step] - context: type: [gsd, ob, fb, ai, af, ab, ofb, mfb, oai, sfb, fsoifb, fcdfb, cl, ofa] unset: [step] - context: class: ['!', er] stream: [moda, msda, edmo, ewmo, wamd] unset: [step] - context: class: [ed,ng] unset: [step] - context: class: [d1] dataset: [climate-dt] unset: [step] - context: stream: ['!', lwda, lwwv, elda, ewla, wfas, wfcl, wfse, wfrf, wfsr, efas, efcl, efse, efrf, efsr] unset: [anoffset] - context: stream: [efas, efcl, efse, efrf, efsr, wfas, wfcl, wfse, wfrf, wfsr] type: ['!', go, sfo, fu] unset: [anoffset] - context: class: [ti, s2] type: [cf] defaults: number: [0] - context: type: ['!', pf, cm, cs, cr, cv, sv, as, fp, ed, tu, ci, me, ff, icp, sot, fcmean, fcmax, fcmin, fcstdev, svar, gwt, gbf, bf, wp] class: ['!', ti, lw, s2] stream: ['!', mofc, wamf, mofm, wmfm, seas, wasf, wamf, sfmm, smma, seap, swmm, ocea, mnfc, mnfh, mnfa, mnfw, mfhw, mfaw, mnfm, mfhm, mfam, mfwm, mhwm, mawm, mmsf, msmm, wams, mswm, mmsa, enda, ewda, edmm, edmo, ewmm, ewmo, elda, ewla, espd, mmaf, mmam, mmaw, mmwm, seas, efsr, wfsr, efse, wfse, ocda] unset: [number] - context: _verb: [retrieve, archive] class: ['!', ti, s2] type: [cf, em, es, ses, taem, taes] stream: ['!', mofc, wamf, mofm, wmfm] unset: [number] - context: type: ['!', pd, cd, pb, taem, pfc] unset: [quantile] - context: class: ['!', d1, ed, ng, ms] defaults: domain: [g] - context: class: [od] type: [cm, cs, cr] domain: [g] set: domain: [e] - context: class: [ep] levtype: [o2d, o3d] unset: [domain] - context: class: [d1, ng, ti, s2, ur, lw, rr, ed] unset: [domain] - context: type: ['!', im, sim, oldim, ob, fb, ai, af, ab, tf, ofb, mfb, ssd, oai, gsd, sfb, fsoifb, fcdfb, ofa] unset: [ident] - context: class: [ci, dt] unset: [method] - context: class: [ti, s2] defaults: origin: [ecmf] - context: class: ['!', od, en, me, c3, ci] unset: [system] - context: accuracy: ['n'] unset: [accuracy] - context: accuracy: [l, r] set: accuracy: [8] - context: format: [p] unset: [format] - context: area: [e] set: area: [73.5,-27,33,45] - context: grid: [av] unset: [grid] - context: type: [ai, ob, fb, af, ab, ofb, mfb, oai, sfb, fsoifb, fcdfb, tf, ofa] unset: [grid, rotation] - context: style: [dissemination] set: resol: [av] warn: "Force resol=av when style=dissemination" - context: style: [dissemination] resol: [auto] set: resol: [av] warn: "Force resol=av when style=dissemination" - context: resol: [av] set: intgrid: [source] truncation: [none] - context: resol: [N128,N200] set: intgrid: _resol - context: resol: ['!',av,auto,N128,N200] set: truncation: _resol - context: _verb: [retrieve] class: [ti] defaults: process: [local] - context: _verb: [retrieve] source: 'defined' defaults: database: [file] - context: obsgroup: 'defined' defaults: duplicates: [keep] - context: obstype: 'defined' defaults: duplicates: [keep] - context: _verb: [retrieve] class: [ti] defaults: padding: [0] - context: class: [ms] country: [it] defaults: grib: [ecmwf] metkit-1.19.2/share/metkit/bufr-subtypes.yaml0000664000175000017500000000343515232567717021406 0ustar alastairalastair--- subtypes: - [1, 1] - [2, 1] - [3, 1] - [4, 1] - [7, 1] - [9, 1] - [11, 1] - [12, 1] - [13, 1] - [14, 1] - [19, 1] - [21, 1] - [22, 1] - [23, 1] - [26, 1] - [28, 1] - [31, 8] - [32, 8] - [49, 2] - [51, 2] - [53, 2] - [54, 2] - [55, 2] - [56, 2] - [57, 2] - [58, 3] - [59, 2] - [60, 2] - [61, 2] - [62, 2] - [63, 2] - [65, 2] - [71, 2] - [72, 2] - [73, 2] - [75, 2] - [82, 3] - [83, 3] - [84, 3] - [85, 3] - [86, 3] - [87, 3] - [88, 3] - [89, 3] - [91, 4] - [92, 4] - [95, 4] - [96, 4] - [97, 4] - [101, 5] - [102, 5] - [103, 5] - [104, 5] - [106, 5] - [109, 5] - [110, 1] - [111, 5] - [112, 5] - [113, 5] - [121, 12] - [122, 12] - [123, 12] - [124, 12] - [125, 6] - [126, 12] - [127, 12] - [129, 2] - [130, 2] - [131, 6] - [132, 6] - [133, 6] - [136, 12] - [137, 12] - [138, 12] - [139, 12] - [140, 1] - [142, 7] - [143, 7] - [144, 7] - [145, 7] - [146, 7] - [147, 1] - [148, 7] - [149, 7] - [150, 7] - [151, 7] - [153, 12] - [154, 2] - [155, 2] - [156, 2] - [159, 9] - [161, 2] - [162, 9] - [164, 10] - [165, 1] - [170, 1] - [172, 1] - [176, 1] - [178, 1] - [180, 1] - [181, 1] - [182, 1] - [184, 1] - [189, 3] - [190, 3] - [191, 3] - [201, 12] - [202, 12] - [203, 30] - [204, 30] - [206, 2] - [207, 2] - [208, 2] - [209, 2] - [210, 12] - [211, 2] - [212, 3] - [213, 12] - [214, 12] - [216, 2] - [217, 12] - [218, 12] - [220, 12] - [224, 12] - [226, 2] - [227, 2] - [228, 3] - [230, 5] - [231, 5] - [233, 12] - [235, 12] - [237, 1] - [240, 2] - [243, 5] - [244, 30] - [245, 12] - [246, 12] - [250, 2] - [251, 2] - [252, 21] - [255, 2] metkit-1.19.2/share/metkit/prodgen-params.yaml0000664000175000017500000000265615232567717021517 0ustar alastairalastair- - file: bin repres: np - - 129 - 130 - 131 - 132 - 133 - 135 - 151 - 152 - 156 - 157 - 164 - 166 - 167 - 228 - 928 - 3025 - - type: tf - - 129 # This is just because param expands to 129 - 999 - - stream: enfo type: cs - - 129 - 130 - 156 - - stream: enfo type: fp - - 131130 - - stream: efov - - 230008 - 230009 - 230020 - 230021 - 230022 - 230044 - 230045 - 230046 - 230047 - 230050 - 230057 - 230058 - 230080 - 230081 - 230082 - 230129 - 230130 - 230142 - 230143 - 230144 - 230145 - 230146 - 230147 - 230169 - 230174 - 230175 - 230176 - 230177 - 230178 - 230179 - 230180 - 230181 - 230182 - 230189 - 230195 - 230196 - 230197 - 230198 - 230205 - 230208 - 230209 - 230210 - 230211 - 230212 - 230213 - 230216 - 230228 - 230239 - 230240 - 230251 - - type: wp levtype: pl - - 129 - 131 - 132 - 157 - 130 - - type: wp levtype: sfc - - 121 - 122 - 123 - 129 - 151 - 134 - 139 - 141 - 142 - 143 - 144 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 183 - 186 - 187 - 188 - 189 - 228 - 236 - 39 - 40 - 41 - 42 - 59 metkit-1.19.2/share/metkit/params.yaml0000664000175000017500000033160515232567717020062 0ustar alastairalastair# File automatically generated by make-params-yaml.py # Do not edit - - class: ai levtype: pl stream: enfo type: cf - - 129 - 130 - 131 - 132 - 133 - 135 - 156 - - class: ai levtype: sfc stream: enfo type: cf - - 129 - 134 - 136 - 151 - 156 - 160 - 163 - 165 - 166 - 167 - 168 - 169 - 172 - 175 - 235 - 3073 - 3074 - 3075 - 228143 - 228144 - 228164 - 228228 - 228246 - 228247 - 231002 - 260289 - - class: ai levtype: sol stream: enfo type: cf - - 260199 - 260360 - - class: ai levtype: pl stream: enfo type: em - - 10 - 129 - 130 - 156 - - class: ai levtype: sfc stream: enfo type: em - - 151 - 167 - 207 - 228249 - - class: ai levtype: sfc stream: enfo type: ep - - 131060 - 131061 - 131062 - 131063 - 131064 - 131065 - 131066 - 131067 - 131068 - 131069 - 131073 - 131085 - 131098 - 131099 - - class: ai levtype: pl stream: enfo type: es - - 10 - 129 - 130 - 156 - - class: ai levtype: sfc stream: enfo type: es - - 151 - 167 - 207 - 228249 - - class: ai levtype: pl stream: enfo type: pf - - 129 - 130 - 131 - 132 - 133 - 135 - 156 - - class: ai levtype: sfc stream: enfo type: pf - - 129 - 134 - 136 - 151 - 156 - 160 - 163 - 165 - 166 - 167 - 168 - 169 - 172 - 175 - 235 - 3073 - 3074 - 3075 - 228143 - 228144 - 228164 - 228228 - 228246 - 228247 - 231002 - 260289 - - class: ai levtype: sol stream: enfo type: pf - - 260199 - 260360 - - class: ai levtype: pl stream: oper type: fc - - 129 - 130 - 131 - 132 - 133 - 135 - 156 - - class: ai levtype: sfc stream: oper type: fc - - 129 - 134 - 136 - 151 - 156 - 160 - 163 - 165 - 166 - 167 - 168 - 169 - 172 - 175 - 235 - 3073 - 3074 - 3075 - 228143 - 228144 - 228164 - 228228 - 228246 - 228247 - 231002 - 260289 - - class: ai levtype: sol stream: oper type: fc - - 260199 - 260360 - - class: ai levtype: sfc stream: waef type: cf - - 3100 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140219 - 140230 - 140232 - 140233 - - class: ai levtype: sfc stream: waef type: ep - - 131074 - 131075 - 131076 - 131077 - 131078 - 131079 - 131080 - 131081 - - class: ai levtype: sfc stream: waef type: pf - - 3100 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140219 - 140230 - 140232 - 140233 - - class: ai levtype: sfc stream: wave type: fc - - 3100 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140219 - 140230 - 140232 - 140233 - - class: mc levtype: ml stream: oper type: an - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 246 - 247 - 248 - 210001 - 210002 - 210003 - 210004 - 210005 - 210006 - 210007 - 210008 - 210009 - 210010 - 210011 - 210121 - 210122 - 210123 - 210124 - 210170 - 210181 - 210203 - 210247 - 210248 - 210249 - 210252 - 210253 - 217003 - 217004 - 217006 - 217007 - 217010 - 217011 - 217012 - 217013 - 217014 - 217015 - 217016 - 217018 - 217019 - 217021 - 217022 - 217023 - 217024 - 217026 - 217027 - 217028 - 217029 - 217030 - 217032 - 217033 - 217034 - 217035 - 217036 - 217040 - 217042 - 217043 - 217044 - 217045 - 217046 - 217047 - 217048 - 217049 - 217050 - 217051 - 217052 - 217053 - 217059 - 217063 - 217064 - 217065 - 217066 - 217067 - 217068 - 217069 - 217070 - 217071 - 217072 - 217073 - 217074 - 217075 - 217076 - 217077 - 217078 - 217079 - 217080 - 217082 - 217083 - 217107 - 217174 - 217175 - 217176 - 217191 - 217192 - 217193 - 217194 - 217195 - 217197 - 217198 - 217200 - 217202 - 217224 - 217226 - - class: mc levtype: pl stream: oper type: an - - 60 - 129 - 130 - 133 - 135 - 156 - 157 - 210001 - 210002 - 210003 - 210004 - 210005 - 210006 - 210007 - 210008 - 210009 - 210010 - 210011 - 210121 - 210122 - 210123 - 210124 - 210170 - 210181 - 210203 - 210247 - 210248 - 210249 - 210252 - 210253 - 217003 - 217004 - 217006 - 217007 - 217010 - 217011 - 217012 - 217013 - 217014 - 217015 - 217016 - 217018 - 217019 - 217021 - 217022 - 217023 - 217024 - 217026 - 217027 - 217028 - 217029 - 217030 - 217032 - 217033 - 217034 - 217035 - 217036 - 217040 - 217042 - 217043 - 217044 - 217045 - 217046 - 217047 - 217048 - 217049 - 217050 - 217051 - 217052 - 217053 - 217059 - 217063 - 217064 - 217065 - 217066 - 217067 - 217068 - 217069 - 217070 - 217071 - 217072 - 217073 - 217074 - 217075 - 217076 - 217077 - 217078 - 217079 - 217080 - 217082 - 217083 - 217107 - 217174 - 217175 - 217176 - 217191 - 217192 - 217193 - 217194 - 217195 - 217197 - 217198 - 217200 - 217202 - 217224 - 217226 - - class: mc levtype: sfc stream: oper type: an - - 26 - 31 - 32 - 34 - 66 - 67 - 78 - 79 - 129 - 134 - 136 - 137 - 141 - 151 - 156 - 164 - 165 - 166 - 167 - 168 - 172 - 186 - 187 - 188 - 198 - 235 - 210072 - 210073 - 210074 - 210125 - 210126 - 210127 - 210128 - 210183 - 210206 - 210207 - 210208 - 210209 - 210210 - 210211 - 210212 - 210213 - 210214 - 210215 - 210216 - 210217 - 210218 - 210219 - 210220 - 210221 - 210222 - 210223 - 210224 - 210225 - 210226 - 210227 - 210228 - 210229 - 210230 - 210250 - 210251 - 214002 - 214003 - 215019 - 215020 - 215021 - 215022 - 215023 - 215024 - 215043 - 215044 - 215045 - 215046 - 215047 - 215048 - 215061 - 215062 - 215063 - 215064 - 215077 - 215078 - 215079 - 215080 - 215087 - 215096 - 215097 - 215098 - 215099 - 215100 - 215101 - 215102 - 215103 - 215104 - 215105 - 215106 - 215107 - 215108 - 215109 - 215110 - 215111 - 215112 - 215113 - 215114 - 215115 - 215116 - 215117 - 215118 - 215119 - 215120 - 215121 - 215122 - 215123 - 215124 - 215125 - 215126 - 215127 - 215128 - 215129 - 215130 - 215131 - 215132 - 215133 - 215134 - 215135 - 215136 - 215137 - 215138 - 215139 - 215140 - 215141 - 215142 - 215143 - 215144 - 215145 - 215146 - 215147 - 215148 - 215149 - 215150 - 215151 - 215152 - 215153 - 215154 - 215155 - 215156 - 215157 - 215158 - 215159 - 215160 - 215161 - 215162 - 215163 - 215164 - 215165 - 215166 - 215167 - 215176 - 215177 - 215178 - 215179 - 215201 - 215202 - 215203 - 215204 - 215211 - 215226 - 218003 - 218004 - 218006 - 218007 - 218009 - 218010 - 218011 - 218012 - 218013 - 218014 - 218015 - 218016 - 218018 - 218019 - 218021 - 218022 - 218023 - 218024 - 218026 - 218027 - 218028 - 218029 - 218030 - 218032 - 218033 - 218034 - 218035 - 218036 - 218038 - 218039 - 218040 - 218041 - 218042 - 218043 - 218044 - 218045 - 218046 - 218047 - 218048 - 218049 - 218050 - 218051 - 218052 - 218053 - 218054 - 218055 - 218056 - 218059 - 218063 - 218064 - 218065 - 218066 - 218067 - 218068 - 218069 - 218070 - 218071 - 218072 - 218073 - 218074 - 218075 - 218076 - 218077 - 218078 - 218079 - 218080 - 218082 - 218083 - 218107 - 218174 - 218175 - 218176 - 218191 - 218192 - 218193 - 218194 - 218195 - 218197 - 218198 - 218200 - 218202 - 218221 - 218224 - 218226 - 228089 - 228090 - - class: mc levtype: ml stream: oper type: fc - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 246 - 247 - 248 - 210001 - 210002 - 210003 - 210004 - 210005 - 210006 - 210007 - 210008 - 210009 - 210010 - 210011 - 210121 - 210122 - 210123 - 210124 - 210170 - 210181 - 210203 - 210247 - 210248 - 210249 - 210252 - 210253 - 215180 - 215181 - 215182 - 215183 - 215184 - 215185 - 215186 - 215187 - 215188 - 217003 - 217004 - 217006 - 217007 - 217010 - 217011 - 217012 - 217013 - 217014 - 217015 - 217016 - 217018 - 217019 - 217021 - 217022 - 217023 - 217024 - 217026 - 217027 - 217028 - 217029 - 217030 - 217032 - 217033 - 217034 - 217035 - 217036 - 217040 - 217042 - 217043 - 217044 - 217045 - 217046 - 217047 - 217048 - 217049 - 217050 - 217051 - 217052 - 217053 - 217059 - 217063 - 217064 - 217065 - 217066 - 217067 - 217068 - 217069 - 217070 - 217071 - 217072 - 217073 - 217074 - 217075 - 217076 - 217077 - 217078 - 217079 - 217080 - 217082 - 217083 - 217107 - 217174 - 217175 - 217176 - 217191 - 217192 - 217193 - 217194 - 217195 - 217197 - 217198 - 217200 - 217202 - 217224 - 217226 - - class: mc levtype: pl stream: oper type: fc - - 60 - 129 - 130 - 133 - 135 - 156 - 246 - 247 - 248 - 210001 - 210002 - 210003 - 210004 - 210005 - 210006 - 210007 - 210008 - 210009 - 210010 - 210011 - 210121 - 210122 - 210123 - 210124 - 210170 - 210181 - 210203 - 210247 - 210248 - 210249 - 210252 - 210253 - 215180 - 215181 - 215182 - 215183 - 215184 - 215185 - 215186 - 215187 - 215188 - 217003 - 217004 - 217006 - 217007 - 217010 - 217011 - 217012 - 217013 - 217014 - 217015 - 217016 - 217018 - 217019 - 217021 - 217022 - 217023 - 217024 - 217026 - 217027 - 217028 - 217029 - 217030 - 217032 - 217033 - 217034 - 217035 - 217036 - 217040 - 217042 - 217043 - 217044 - 217045 - 217046 - 217047 - 217048 - 217049 - 217050 - 217051 - 217052 - 217053 - 217059 - 217063 - 217064 - 217065 - 217066 - 217067 - 217068 - 217069 - 217070 - 217071 - 217072 - 217073 - 217074 - 217075 - 217076 - 217077 - 217078 - 217079 - 217080 - 217082 - 217083 - 217107 - 217174 - 217175 - 217176 - 217191 - 217192 - 217193 - 217194 - 217195 - 217197 - 217198 - 217200 - 217202 - 217224 - 217226 - - class: mc levtype: sfc stream: oper type: fc - - 20 - 26 - 31 - 32 - 34 - 47 - 57 - 66 - 67 - 78 - 79 - 129 - 134 - 136 - 137 - 141 - 142 - 143 - 146 - 147 - 151 - 156 - 159 - 164 - 165 - 166 - 167 - 168 - 169 - 172 - 175 - 176 - 177 - 178 - 179 - 182 - 186 - 187 - 188 - 189 - 198 - 208 - 209 - 210 - 211 - 212 - 213 - 228 - 235 - 243 - 3020 - 210072 - 210073 - 210074 - 210125 - 210126 - 210127 - 210128 - 210183 - 210206 - 210207 - 210208 - 210209 - 210210 - 210211 - 210212 - 210213 - 210214 - 210215 - 210216 - 210217 - 210218 - 210219 - 210220 - 210221 - 210222 - 210223 - 210224 - 210225 - 210226 - 210227 - 210228 - 210229 - 210230 - 210250 - 210251 - 214002 - 214003 - 215001 - 215002 - 215003 - 215004 - 215005 - 215006 - 215007 - 215008 - 215009 - 215010 - 215011 - 215012 - 215013 - 215014 - 215015 - 215019 - 215020 - 215021 - 215022 - 215023 - 215024 - 215025 - 215026 - 215027 - 215028 - 215029 - 215030 - 215031 - 215032 - 215033 - 215034 - 215035 - 215036 - 215037 - 215038 - 215039 - 215043 - 215044 - 215045 - 215046 - 215047 - 215048 - 215049 - 215050 - 215051 - 215052 - 215053 - 215054 - 215055 - 215056 - 215057 - 215058 - 215061 - 215062 - 215063 - 215064 - 215065 - 215066 - 215067 - 215068 - 215069 - 215070 - 215071 - 215072 - 215073 - 215074 - 215077 - 215078 - 215079 - 215080 - 215081 - 215082 - 215083 - 215084 - 215085 - 215087 - 215096 - 215097 - 215098 - 215099 - 215100 - 215101 - 215102 - 215103 - 215104 - 215105 - 215106 - 215107 - 215108 - 215109 - 215110 - 215111 - 215112 - 215113 - 215114 - 215115 - 215116 - 215117 - 215118 - 215119 - 215120 - 215121 - 215122 - 215123 - 215124 - 215125 - 215126 - 215127 - 215128 - 215129 - 215130 - 215131 - 215132 - 215133 - 215134 - 215135 - 215136 - 215137 - 215138 - 215139 - 215140 - 215141 - 215142 - 215143 - 215144 - 215145 - 215146 - 215147 - 215148 - 215149 - 215150 - 215151 - 215152 - 215153 - 215154 - 215155 - 215156 - 215157 - 215158 - 215159 - 215160 - 215161 - 215162 - 215163 - 215164 - 215165 - 215166 - 215167 - 215176 - 215177 - 215178 - 215179 - 215189 - 215190 - 215191 - 215192 - 215193 - 215194 - 215195 - 215196 - 215197 - 215198 - 215201 - 215202 - 215203 - 215204 - 215205 - 215206 - 215207 - 215208 - 215209 - 215211 - 215226 - 218003 - 218004 - 218006 - 218007 - 218009 - 218010 - 218011 - 218012 - 218013 - 218014 - 218015 - 218016 - 218018 - 218019 - 218021 - 218022 - 218023 - 218024 - 218026 - 218027 - 218028 - 218029 - 218030 - 218032 - 218033 - 218034 - 218035 - 218036 - 218038 - 218039 - 218040 - 218041 - 218042 - 218043 - 218044 - 218045 - 218046 - 218047 - 218048 - 218049 - 218050 - 218051 - 218052 - 218053 - 218054 - 218055 - 218056 - 218059 - 218063 - 218064 - 218065 - 218066 - 218067 - 218068 - 218069 - 218070 - 218071 - 218072 - 218073 - 218074 - 218075 - 218076 - 218077 - 218078 - 218079 - 218080 - 218082 - 218083 - 218107 - 218174 - 218175 - 218176 - 218191 - 218192 - 218193 - 218194 - 218195 - 218197 - 218198 - 218200 - 218202 - 218221 - 218224 - 218226 - 222001 - 222006 - 222013 - 222015 - 222017 - 222019 - 222027 - 222031 - 222033 - 223006 - 223013 - 223015 - 223017 - 223019 - 223027 - 223031 - 223033 - 228003 - 228021 - 228022 - 228023 - 228028 - 228046 - 228088 - 228089 - 228090 - 228129 - 228130 - 228235 - 228236 - 228251 - 260015 - - class: od levtype: ml stream: eefh type: cf - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 260290 - - class: od levtype: o2d stream: eefh type: cf - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: pl stream: eefh type: cf - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 260290 - - class: od levtype: pt stream: eefh type: cf - - 54 - 60 - 131 - 132 - 138 - 155 - 203 - - class: od levtype: pv stream: eefh type: cf - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: eefh type: cf - - 8 - 9 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 45 - 47 - 49 - 57 - 74 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 210 - 228 - 234 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174098 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228046 - 228047 - 228048 - 228050 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228235 - 228236 - 228239 - 228240 - 228241 - 228246 - 228247 - 228251 - 260015 - 260048 - 260109 - 260121 - 260123 - 260289 - - class: od levtype: sol stream: eefh type: cf - - 33 - 238 - 228038 - 228141 - - class: od levtype: sfc stream: eefh type: fcmax - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228246 - 228247 - 237117 - - class: od levtype: pl stream: eefh type: fcmean - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 138 - 155 - 156 - - class: od levtype: pt stream: eefh type: fcmean - - 60 - - class: od levtype: pv stream: eefh type: fcmean - - 3 - - class: od levtype: sfc stream: eefh type: fcmean - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228246 - 228247 - 235117 - - class: od levtype: sfc stream: eefh type: fcmin - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 228246 - 228247 - 238117 - - class: od levtype: sfc stream: eefh type: fcstdev - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228246 - 228247 - 239117 - - class: od levtype: ml stream: eefh type: pf - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 260290 - - class: od levtype: o2d stream: eefh type: pf - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: pl stream: eefh type: pf - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 260290 - - class: od levtype: pt stream: eefh type: pf - - 54 - 60 - 131 - 132 - 138 - 155 - 203 - - class: od levtype: pv stream: eefh type: pf - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: eefh type: pf - - 8 - 9 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 45 - 47 - 49 - 57 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 210 - 228 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174098 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228046 - 228047 - 228048 - 228050 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228235 - 228236 - 228239 - 228240 - 228241 - 228246 - 228247 - 228251 - 260015 - 260048 - 260109 - 260121 - 260123 - 260289 - - class: od levtype: sol stream: eefh type: pf - - 33 - 238 - 228038 - 228141 - - class: od levtype: ml stream: eefo type: cf - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 260290 - - class: od levtype: o2d stream: eefo type: cf - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: pl stream: eefo type: cf - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 260290 - - class: od levtype: pt stream: eefo type: cf - - 54 - 60 - 131 - 132 - 138 - 155 - 203 - - class: od levtype: pv stream: eefo type: cf - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: eefo type: cf - - 8 - 9 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 45 - 47 - 49 - 57 - 66 - 67 - 74 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 210 - 228 - 234 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174098 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228046 - 228047 - 228048 - 228050 - 228051 - 228057 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228235 - 228236 - 228239 - 228240 - 228241 - 228246 - 228247 - 228249 - 228251 - 260015 - 260048 - 260109 - 260121 - 260123 - 260289 - - class: od levtype: sol stream: eefo type: cf - - 33 - 238 - 228038 - 228141 - - class: od levtype: pl stream: eefo type: ed - - 130 - - class: od levtype: sfc stream: eefo type: ed - - 164 - 167 - 207 - 228 - - class: od levtype: sfc stream: eefo type: efi - - 132167 - 132228 - - class: od levtype: pl stream: eefo type: em - - 10 - 129 - 130 - 156 - - class: od levtype: sfc stream: eefo type: em - - 121 - 122 - 151 - 167 - 207 - - class: od levtype: sfc stream: eefo type: ep - - 131001 - 131002 - 131003 - 131004 - 131005 - 131006 - 131007 - 131008 - 131009 - 131010 - - class: od levtype: pl stream: eefo type: es - - 10 - 129 - 130 - 156 - - class: od levtype: sfc stream: eefo type: es - - 121 - 122 - 151 - 167 - 207 - - class: od levtype: sfc stream: eefo type: fcmax - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228235 - 228246 - 228247 - 235117 - 237117 - - class: od levtype: pl stream: eefo type: fcmean - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 138 - 155 - 156 - 171001 - 171002 - 171129 - 171130 - 171131 - 171132 - 171133 - 171156 - - class: od levtype: pt stream: eefo type: fcmean - - 60 - - class: od levtype: pv stream: eefo type: fcmean - - 3 - - class: od levtype: sfc stream: eefo type: fcmean - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 171006 - 171007 - 171033 - 171034 - 171078 - 171079 - 171121 - 171122 - 171136 - 171137 - 171139 - 171141 - 171151 - 171164 - 171165 - 171166 - 171167 - 171168 - 171201 - 171202 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 173144 - 173189 - 173228 - 228235 - 228246 - 228247 - 235117 - - class: od levtype: sfc stream: eefo type: fcmin - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228235 - 228246 - 228247 - 235117 - 238117 - - class: od levtype: sfc stream: eefo type: fcstdev - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228235 - 228246 - 228247 - 235117 - 239117 - - class: od levtype: pl stream: eefo type: pb - - 171129 - 171156 - - class: od levtype: sfc stream: eefo type: pb - - 171139 - 171151 - 171167 - 173228 - - class: od levtype: pl stream: eefo type: pd - - 131129 - - class: od levtype: sfc stream: eefo type: pd - - 131139 - 131151 - 131167 - 131228 - - class: od levtype: ml stream: eefo type: pf - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 260290 - - class: od levtype: o2d stream: eefo type: pf - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: pl stream: eefo type: pf - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 260290 - - class: od levtype: pt stream: eefo type: pf - - 54 - 60 - 131 - 132 - 138 - 155 - 203 - - class: od levtype: pv stream: eefo type: pf - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: eefo type: pf - - 8 - 9 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 45 - 47 - 49 - 57 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 210 - 228 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174098 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228046 - 228047 - 228048 - 228050 - 228051 - 228057 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228235 - 228236 - 228239 - 228240 - 228241 - 228246 - 228247 - 228249 - 228251 - 260015 - 260048 - 260109 - 260121 - 260123 - 260289 - - class: od levtype: sol stream: eefo type: pf - - 33 - 238 - 228038 - 228141 - - class: od levtype: sfc stream: eefo type: sot - - 132167 - 132228 - - class: od levtype: pl stream: eefo type: taem - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 156 - 171001 - 171002 - 171129 - 171130 - 171131 - 171132 - 171133 - 171156 - - class: od levtype: sfc stream: eefo type: taem - - 33 - 34 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 201 - 202 - 171006 - 171007 - 171033 - 171034 - 171078 - 171079 - 171121 - 171122 - 171136 - 171137 - 171139 - 171141 - 171151 - 171164 - 171165 - 171166 - 171167 - 171168 - 171201 - 171202 - 172142 - 172143 - 172144 - 172189 - 172228 - 173144 - 173189 - 173228 - 228235 - 228246 - 228247 - 234139 - 234151 - 234167 - 234228 - 235117 - - class: od levtype: pl stream: eefo type: taes - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 156 - - class: od levtype: sfc stream: eefo type: taes - - 33 - 34 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 201 - 202 - 172142 - 172143 - 172144 - 172189 - 172228 - 228235 - 228246 - 228247 - 235117 - - class: od levtype: sfc stream: eehs type: cd - - 228 - 228004 - - class: od levtype: pl stream: eehs type: em - - 129 - 130 - 156 - - class: od levtype: sfc stream: eehs type: em - - 228 - 228004 - - class: od levtype: pl stream: eehs type: es - - 129 - 130 - 156 - - class: od levtype: sfc stream: eehs type: es - - 228 - 228004 - - class: od levtype: pl stream: eehs type: taem - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 156 - - class: od levtype: sfc stream: eehs type: taem - - 33 - 34 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 201 - 202 - 172142 - 172143 - 172144 - 172189 - 172228 - 228246 - 228247 - 235117 - - class: od levtype: pl stream: eehs type: taes - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 156 - - class: od levtype: sfc stream: eehs type: taes - - 33 - 34 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 201 - 202 - 172142 - 172143 - 172144 - 172189 - 172228 - 228246 - 228247 - 235117 - - class: od levtype: sfc stream: efhs type: cd - - 49 - 59 - 144 - 201 - 202 - 228 - 162045 - 228004 - 228005 - 228006 - 228044 - - class: od levtype: pl stream: efhs type: em - - 129 - 130 - 156 - - class: od levtype: sfc stream: efhs type: em - - 49 - 59 - 144 - 201 - 202 - 228 - 162045 - 228004 - 228005 - 228006 - 228044 - - class: od levtype: pl stream: efhs type: es - - 129 - 130 - 156 - - class: od levtype: sfc stream: efhs type: es - - 49 - 59 - 144 - 201 - 202 - 228 - 162045 - 228004 - 228005 - 228006 - 228044 - - class: od levtype: pl stream: efhs type: taem - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 156 - - class: od levtype: sfc stream: efhs type: taem - - 33 - 34 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 201 - 202 - 172142 - 172143 - 172144 - 172189 - 172228 - 228246 - 228247 - 235117 - - class: od levtype: pl stream: efhs type: taes - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 156 - - class: od levtype: sfc stream: efhs type: taes - - 33 - 34 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 201 - 202 - 172142 - 172143 - 172144 - 172189 - 172228 - 228246 - 228247 - 235117 - - class: od levtype: ml stream: elda type: 4i - - 200130 - 200131 - 200132 - 200133 - 200138 - 200152 - 200155 - 200203 - - class: od levtype: ml stream: elda type: 4v - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - - class: od levtype: pl stream: elda type: 4v - - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: elda type: 4v - - 53 - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: elda type: 4v - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: elda type: 4v - - 8 - 9 - 15 - 16 - 17 - 18 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 44 - 45 - 57 - 58 - 59 - 66 - 67 - 74 - 78 - 79 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 148 - 151 - 156 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 183 - 186 - 187 - 188 - 195 - 196 - 197 - 198 - 205 - 206 - 228 - 229 - 230 - 234 - 235 - 236 - 238 - 244 - 245 - 174096 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228089 - 228090 - 228131 - 228132 - 228239 - 228240 - 228246 - 228247 - - class: od levtype: al stream: elda type: an - - 213101 - 213102 - 213103 - 213104 - 213105 - 213106 - 213107 - 213108 - 213109 - 213110 - 213111 - 213112 - 213113 - 213114 - 213115 - 213116 - 213117 - 213118 - 213119 - 213120 - 213121 - 213122 - 213123 - 213124 - 213125 - 213126 - 213127 - 213128 - 213129 - 213130 - 213131 - 213132 - 213133 - 213134 - 213135 - 213136 - 213137 - 213138 - 213139 - 213140 - 213141 - 213142 - 213143 - 213144 - 213145 - 213146 - 213147 - 213148 - 213149 - 213150 - 213151 - 213152 - 213153 - 213154 - 213155 - 213156 - 213157 - 213158 - 213159 - 213160 - - class: od levtype: ml stream: elda type: an - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - - class: od levtype: pl stream: elda type: an - - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: elda type: an - - 53 - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: elda type: an - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: elda type: an - - 15 - 16 - 17 - 18 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 66 - 67 - 74 - 78 - 79 - 129 - 134 - 136 - 137 - 139 - 141 - 148 - 151 - 156 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 170 - 172 - 173 - 174 - 183 - 186 - 187 - 188 - 198 - 206 - 229 - 230 - 234 - 235 - 236 - 238 - 129172 - 151131 - 151132 - 174096 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228089 - 228090 - 228239 - 228240 - 228246 - 228247 - 229001 - 260289 - - class: od levtype: sol stream: elda type: an - - 33 - 238 - 228038 - 228141 - - class: od levtype: ml stream: elda type: em - - 21 - 22 - 23 - 130 - 131 - 132 - 133 - 138 - 152 - 156 - 157 - 203 - 246 - 247 - 228055 - 228056 - - class: od levtype: sfc stream: elda type: em - - 194 - - class: od levtype: ml stream: elda type: eme - - 200130 - 200131 - 200132 - 200138 - 200152 - 200155 - - class: od levtype: ml stream: elda type: es - - 21 - 22 - 23 - 130 - 131 - 132 - 133 - 138 - 152 - 156 - 157 - 203 - 246 - 247 - 228055 - 228056 - - class: od levtype: sfc stream: elda type: es - - 39 - 40 - 41 - 42 - 139 - 140 - 170 - 171 - 183 - 184 - 194 - 235 - 236 - 237 - - class: od levtype: al stream: elda type: fc - - 213101 - 213102 - 213103 - 213104 - 213105 - 213106 - 213107 - 213108 - 213109 - 213110 - 213111 - 213112 - 213113 - 213114 - 213115 - 213116 - 213117 - 213118 - 213119 - 213120 - 213121 - 213122 - 213123 - 213124 - 213125 - 213126 - 213127 - 213128 - 213129 - 213130 - 213131 - 213132 - 213133 - 213134 - 213135 - 213136 - 213137 - 213138 - 213139 - 213140 - 213141 - 213142 - 213143 - 213144 - 213145 - 213146 - 213147 - 213148 - 213149 - 213150 - 213151 - 213152 - 213153 - 213154 - 213155 - 213156 - 213157 - 213158 - 213159 - 213160 - - class: od levtype: ml stream: elda type: fc - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - - class: od levtype: o2d stream: elda type: fc - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: o3d stream: elda type: fc - - 262500 - 262501 - 262502 - 262505 - 262506 - 262507 - - class: od levtype: pl stream: elda type: fc - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: elda type: fc - - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: elda type: fc - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: elda type: fc - - 8 - 9 - 20 - 26 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 44 - 45 - 47 - 49 - 50 - 57 - 58 - 66 - 67 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 208 - 209 - 210 - 211 - 212 - 213 - 228 - 229 - 230 - 231 - 232 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151131 - 151132 - 162071 - 162072 - 174096 - 228003 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228046 - 228047 - 228048 - 228050 - 228051 - 228057 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228129 - 228130 - 228131 - 228132 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228235 - 228236 - 228239 - 228240 - 228241 - 228246 - 228247 - 228251 - 260015 - 260048 - 260109 - 260121 - 260123 - 260289 - 260318 - 260319 - 260320 - 260321 - 260338 - 260339 - - class: od levtype: sol stream: elda type: fc - - 33 - 238 - 228038 - 228141 - - class: od levtype: ml stream: elda type: me - - 200130 - 200131 - 200132 - 200138 - 200152 - 200155 - - class: od levtype: ml stream: elda type: ses - - 21 - 22 - 23 - 130 - 131 - 132 - 133 - 138 - 152 - 156 - 157 - 203 - 246 - 247 - 228055 - 228056 - - class: od levtype: sfc stream: elda type: ses - - 194 - - class: od levtype: ml stream: enfh type: cf - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 260290 - - class: od levtype: o2d stream: enfh type: cf - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: pl stream: enfh type: cf - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 260290 - - class: od levtype: pt stream: enfh type: cf - - 54 - 60 - 131 - 132 - 138 - 155 - 203 - - class: od levtype: pv stream: enfh type: cf - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: enfh type: cf - - 8 - 9 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 45 - 47 - 49 - 57 - 74 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 210 - 228 - 234 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174098 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228046 - 228047 - 228048 - 228050 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228235 - 228236 - 228239 - 228240 - 228241 - 228246 - 228247 - 228251 - 260015 - 260048 - 260109 - 260121 - 260123 - 260289 - - class: od levtype: sol stream: enfh type: cf - - 33 - 238 - 228038 - 228141 - - class: od levtype: sfc stream: enfh type: fcmax - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228246 - 228247 - 237117 - - class: od levtype: pl stream: enfh type: fcmean - - 1 - 2 - 129 - 130 - 131 - 132 - 133 - 138 - 155 - 156 - - class: od levtype: pt stream: enfh type: fcmean - - 60 - - class: od levtype: pv stream: enfh type: fcmean - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: enfh type: fcmean - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228246 - 228247 - 235117 - - class: od levtype: sfc stream: enfh type: fcmin - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 228246 - 228247 - 238117 - - class: od levtype: sfc stream: enfh type: fcstdev - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 78 - 79 - 121 - 122 - 136 - 137 - 139 - 141 - 151 - 159 - 164 - 165 - 166 - 167 - 168 - 170 - 201 - 202 - 207 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172228 - 228246 - 228247 - 239117 - - class: od levtype: ml stream: enfh type: icp - - 130 - 131 - 132 - 133 - 138 - 152 - 155 - - class: od levtype: ml stream: enfh type: pf - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 260290 - - class: od levtype: o2d stream: enfh type: pf - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: pl stream: enfh type: pf - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 260290 - - class: od levtype: pt stream: enfh type: pf - - 54 - 60 - 131 - 132 - 138 - 155 - 203 - - class: od levtype: pv stream: enfh type: pf - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: enfh type: pf - - 8 - 9 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 45 - 47 - 49 - 57 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 210 - 228 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174098 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228046 - 228047 - 228048 - 228050 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228235 - 228236 - 228239 - 228240 - 228241 - 228246 - 228247 - 228251 - 260015 - 260048 - 260109 - 260121 - 260123 - 260289 - - class: od levtype: sol stream: enfh type: pf - - 33 - 238 - 228038 - 228141 - - class: od levtype: pl stream: enfo type: cm - - 129 - 156 - - class: od levtype: pl stream: enfo type: cr - - 129 - 156 - - class: od levtype: sfc stream: enfo type: efi - - 132044 - 132045 - 132049 - 132059 - 132144 - 132165 - 132167 - 132201 - 132202 - 132228 - - class: od levtype: sfc stream: enfo type: efic - - 132044 - 132045 - 132049 - 132059 - 132144 - 132165 - 132167 - 132201 - 132202 - 132228 - - class: od levtype: pl stream: enfo type: em - - 10 - 129 - 130 - 156 - - class: od levtype: sfc stream: enfo type: em - - 121 - 122 - 151 - 167 - 207 - 228026 - 228027 - 228249 - - class: od levtype: pl stream: enfo type: ep - - 131020 - 131021 - 131022 - 131023 - 131024 - 131025 - 133093 - 133094 - 133095 - 133096 - 133097 - 133098 - - class: od levtype: sfc stream: enfo type: ep - - 131060 - 131061 - 131062 - 131063 - 131064 - 131065 - 131066 - 131067 - 131068 - 131069 - 131070 - 131071 - 131072 - 131073 - 131085 - 131089 - 131090 - 131091 - 131098 - 131099 - 131100 - - class: od levtype: pl stream: enfo type: es - - 10 - 129 - 130 - 156 - - class: od levtype: sfc stream: enfo type: es - - 121 - 122 - 151 - 167 - 207 - 228026 - 228027 - 228249 - - class: od levtype: ml stream: enfo type: icp - - 130 - 131 - 132 - 138 - 152 - 155 - - class: od levtype: fl stream: enfo type: pf - - 260290 - - class: od levtype: ml stream: enfo type: pf - - 75 - 76 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 260290 - - class: od levtype: o2d stream: enfo type: pf - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: o3d stream: enfo type: pf - - 262500 - 262501 - 262502 - 262505 - 262506 - 262507 - - class: od levtype: pl stream: enfo type: pf - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 260290 - - class: od levtype: pt stream: enfo type: pf - - 54 - 60 - 131 - 132 - 138 - 155 - 203 - - class: od levtype: pv stream: enfo type: pf - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: enfo type: pf - - 8 - 9 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 45 - 47 - 49 - 57 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 210 - 228 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174098 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228020 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228046 - 228047 - 228048 - 228050 - 228051 - 228057 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228131 - 228132 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228231 - 228232 - 228233 - 228234 - 228235 - 228236 - 228237 - 228239 - 228240 - 228241 - 228246 - 228247 - 228249 - 228251 - 260004 - 260005 - 260015 - 260048 - 260109 - 260121 - 260123 - 260242 - 260255 - 260289 - 260318 - 260319 - 260320 - 260321 - 260338 - 260339 - 261001 - 261002 - 261014 - 261015 - 261016 - 261018 - 261023 - - class: od levtype: sol stream: enfo type: pf - - 33 - 238 - 228038 - 228141 - - class: od levtype: sfc stream: enfo type: sot - - 132044 - 132045 - 132049 - 132059 - 132144 - 132165 - 132167 - 132201 - 132202 - 132228 - - class: od stream: enfo type: tf - - 129 - 999 - - class: od levtype: pl stream: enfo type: wp - - 129 - 130 - 131 - 132 - 157 - - class: od levtype: sfc stream: enfo type: wp - - 39 - 40 - 41 - 42 - 121 - 122 - 123 - 129 - 134 - 139 - 141 - 142 - 143 - 144 - 151 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 183 - 186 - 187 - 188 - 189 - 228 - 236 - 228235 - - class: od levtype: sfc stream: enwh type: cf - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: enwh type: fcmax - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: enwh type: fcmean - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: enwh type: fcmin - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: enwh type: fcstdev - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: enwh type: pf - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: ewla type: 4v - - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140246 - 140247 - 140248 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: ewla type: an - - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140246 - 140247 - 140248 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: ewla type: fc - - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: ml stream: lwda type: 4i - - 200130 - 200131 - 200132 - 200133 - 200138 - 200152 - 200155 - 200203 - - class: od levtype: ml stream: lwda type: 4v - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - - class: od levtype: pl stream: lwda type: 4v - - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: lwda type: 4v - - 53 - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: lwda type: 4v - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: lwda type: 4v - - 8 - 9 - 15 - 16 - 17 - 18 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 44 - 45 - 57 - 58 - 59 - 66 - 67 - 74 - 78 - 79 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 148 - 151 - 156 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 183 - 186 - 187 - 188 - 195 - 196 - 197 - 198 - 205 - 206 - 228 - 229 - 230 - 234 - 235 - 236 - 238 - 244 - 245 - 174096 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228089 - 228090 - 228131 - 228132 - 228239 - 228240 - 228246 - 228247 - - class: od levtype: ml stream: lwda type: an - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - - class: od levtype: pl stream: lwda type: an - - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: lwda type: an - - 53 - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: lwda type: an - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: lwda type: an - - 15 - 16 - 17 - 18 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 66 - 67 - 74 - 78 - 79 - 129 - 134 - 136 - 137 - 139 - 141 - 148 - 151 - 156 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 170 - 172 - 173 - 174 - 183 - 186 - 187 - 188 - 198 - 206 - 229 - 230 - 234 - 235 - 236 - 238 - 129172 - 151131 - 151132 - 174096 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228089 - 228090 - 228239 - 228240 - 228246 - 228247 - 229001 - 260289 - - class: od levtype: sol stream: lwda type: an - - 33 - 238 - 228038 - 228141 - - class: od levtype: ml stream: lwda type: fc - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 162110 - 260290 - - class: od levtype: o2d stream: lwda type: fc - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: o3d stream: lwda type: fc - - 262500 - 262501 - 262502 - 262505 - 262506 - 262507 - - class: od levtype: pl stream: lwda type: fc - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: lwda type: fc - - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: lwda type: fc - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: lwda type: fc - - 8 - 9 - 20 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 44 - 45 - 47 - 49 - 50 - 57 - 58 - 66 - 67 - 74 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 208 - 209 - 210 - 211 - 212 - 213 - 228 - 229 - 230 - 231 - 232 - 234 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174096 - 174098 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228003 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228020 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228045 - 228046 - 228047 - 228048 - 228050 - 228051 - 228057 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228129 - 228130 - 228131 - 228132 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228231 - 228232 - 228233 - 228234 - 228235 - 228236 - 228237 - 228239 - 228240 - 228241 - 228246 - 228247 - 228249 - 228251 - 260015 - 260048 - 260109 - 260121 - 260123 - 260289 - 260318 - 260319 - 260320 - 260321 - 260338 - 260339 - - class: od levtype: sol stream: lwda type: fc - - 33 - 238 - 228038 - 228141 - - class: od levtype: ml stream: lwda type: me - - 200130 - 200131 - 200132 - 200138 - 200152 - 200155 - - class: od stream: lwda type: ssd - - 260510 - - class: od levtype: sfc stream: lwwv type: 4v - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140246 - 140247 - 140248 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: lwwv type: an - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140246 - 140247 - 140248 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: lwwv type: fc - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: pl stream: mmsa type: em - - 171129 - 171130 - 171131 - 171132 - 171133 - 171138 - 171155 - 171156 - 171203 - - class: od levtype: pt stream: mmsa type: em - - 171060 - - class: od levtype: pv stream: mmsa type: em - - 171003 - - class: od levtype: sfc stream: mmsa type: em - - 171024 - 171025 - 171031 - 171033 - 171034 - 171039 - 171040 - 171041 - 171042 - 171049 - 171051 - 171052 - 171078 - 171079 - 171137 - 171139 - 171141 - 171151 - 171164 - 171165 - 171166 - 171167 - 171168 - 171170 - 171183 - 171186 - 171206 - 171207 - 171229 - 171230 - 171236 - 171243 - 173008 - 173009 - 173142 - 173143 - 173144 - 173146 - 173147 - 173169 - 173175 - 173176 - 173177 - 173178 - 173179 - 173180 - 173181 - 173182 - 173189 - 173205 - 173212 - 173228 - - class: od levtype: pl stream: mmsa type: fcmean - - 171129 - 171130 - 171131 - 171132 - 171133 - 171138 - 171155 - 171156 - 171203 - - class: od levtype: pt stream: mmsa type: fcmean - - 171060 - - class: od levtype: pv stream: mmsa type: fcmean - - 171003 - - class: od levtype: sfc stream: mmsa type: fcmean - - 171024 - 171025 - 171031 - 171033 - 171034 - 171039 - 171040 - 171041 - 171042 - 171049 - 171051 - 171052 - 171078 - 171079 - 171137 - 171139 - 171141 - 171151 - 171164 - 171165 - 171166 - 171167 - 171168 - 171170 - 171183 - 171186 - 171206 - 171207 - 171229 - 171230 - 171236 - 171243 - 173008 - 173009 - 173142 - 173143 - 173144 - 173146 - 173147 - 173169 - 173175 - 173176 - 173177 - 173178 - 173179 - 173180 - 173181 - 173182 - 173189 - 173205 - 173212 - 173228 - - class: od levtype: ml stream: mmsf type: fc - - 129 - 130 - 131 - 132 - 133 - 138 - 152 - 155 - 156 - 246 - 247 - - class: od levtype: pl stream: mmsf type: fc - - 129 - 130 - 131 - 132 - 133 - 138 - 152 - 155 - 156 - 203 - - class: od levtype: pt stream: mmsf type: fc - - 60 - - class: od levtype: pv stream: mmsf type: fc - - 3 - - class: od levtype: sfc stream: mmsf type: fc - - 8 - 9 - 26 - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 51 - 52 - 55 - 78 - 79 - 129 - 137 - 139 - 141 - 142 - 143 - 144 - 146 - 147 - 151 - 156 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 189 - 201 - 202 - 205 - 206 - 212 - 228 - 229 - 230 - 236 - 243 - 228007 - 228008 - 228014 - - class: od levtype: ml stream: mmsf type: icp - - 130 - 131 - 132 - 133 - 138 - 152 - 155 - - class: od levtype: pl stream: msmm type: em - - 129 - 130 - 131 - 132 - 133 - 138 - 155 - 156 - 203 - - class: od levtype: pt stream: msmm type: em - - 60 - - class: od levtype: pv stream: msmm type: em - - 3 - - class: od levtype: sfc stream: msmm type: em - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 51 - 52 - 78 - 79 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 170 - 183 - 186 - 206 - 207 - 229 - 230 - 236 - 243 - 172008 - 172009 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172205 - 172212 - 172228 - 228008 - 228014 - - class: od levtype: sfc stream: msmm type: fcmax - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 51 - 52 - 78 - 79 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 170 - 183 - 186 - 206 - 207 - 229 - 230 - 236 - 243 - 172008 - 172009 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172205 - 172212 - 172228 - 228008 - 228014 - - class: od levtype: pl stream: msmm type: fcmean - - 129 - 130 - 131 - 132 - 133 - 138 - 155 - 156 - 203 - - class: od levtype: pt stream: msmm type: fcmean - - 60 - - class: od levtype: pv stream: msmm type: fcmean - - 3 - - class: od levtype: sfc stream: msmm type: fcmean - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 51 - 52 - 78 - 79 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 170 - 183 - 186 - 206 - 207 - 229 - 230 - 236 - 243 - 172008 - 172009 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172205 - 172212 - 172228 - 228008 - 228014 - - class: od levtype: sfc stream: msmm type: fcmin - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 51 - 52 - 78 - 79 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 170 - 183 - 186 - 206 - 207 - 229 - 230 - 236 - 243 - 172008 - 172009 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172205 - 172212 - 228008 - 228014 - - class: od levtype: sfc stream: msmm type: fcstdev - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 51 - 52 - 78 - 79 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 170 - 183 - 186 - 206 - 207 - 229 - 230 - 236 - 243 - 172008 - 172009 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172205 - 172212 - 172228 - 228008 - 228014 - - class: od levtype: pl stream: msmm type: hcmean - - 129 - 130 - 131 - 132 - 133 - 138 - 155 - 156 - 203 - - class: od levtype: pt stream: msmm type: hcmean - - 60 - - class: od levtype: pv stream: msmm type: hcmean - - 3 - - class: od levtype: sfc stream: msmm type: hcmean - - 31 - 33 - 34 - 39 - 40 - 41 - 42 - 49 - 51 - 52 - 78 - 79 - 137 - 139 - 141 - 151 - 164 - 165 - 166 - 167 - 168 - 170 - 183 - 186 - 206 - 207 - 229 - 230 - 236 - 243 - 172008 - 172009 - 172142 - 172143 - 172144 - 172146 - 172147 - 172169 - 172175 - 172176 - 172177 - 172178 - 172179 - 172180 - 172181 - 172182 - 172189 - 172205 - 172212 - 172228 - 228008 - 228014 - - class: od levtype: ml stream: oper type: 4i - - 200130 - 200131 - 200132 - 200133 - 200138 - 200152 - 200155 - 200203 - - class: od levtype: ml stream: oper type: 4v - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - - class: od levtype: pl stream: oper type: 4v - - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: oper type: 4v - - 53 - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: oper type: 4v - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: oper type: 4v - - 8 - 9 - 15 - 16 - 17 - 18 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 44 - 45 - 57 - 58 - 59 - 66 - 67 - 74 - 78 - 79 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 148 - 151 - 156 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 183 - 186 - 187 - 188 - 195 - 196 - 197 - 198 - 205 - 206 - 228 - 229 - 230 - 234 - 235 - 236 - 238 - 244 - 245 - 174096 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228089 - 228090 - 228131 - 228132 - 228239 - 228240 - 228246 - 228247 - - class: od levtype: ml stream: oper type: an - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - - class: od levtype: pl stream: oper type: an - - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: oper type: an - - 53 - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: oper type: an - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: oper type: an - - 15 - 16 - 17 - 18 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 66 - 67 - 74 - 78 - 79 - 129 - 134 - 136 - 137 - 139 - 141 - 148 - 151 - 156 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 170 - 172 - 173 - 174 - 183 - 186 - 187 - 188 - 198 - 206 - 229 - 230 - 234 - 235 - 236 - 238 - 129172 - 151131 - 151132 - 174096 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228021 - 228022 - 228089 - 228090 - 228239 - 228240 - 228246 - 228247 - 229001 - 260289 - - class: od levtype: sol stream: oper type: an - - 33 - 238 - 228038 - 228141 - - class: od levtype: fl stream: oper type: fc - - 260290 - - class: od levtype: ml stream: oper type: fc - - 75 - 76 - 77 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 203 - 246 - 247 - 248 - 162110 - 260290 - - class: od levtype: o2d stream: oper type: fc - - 262000 - 262001 - 262002 - 262003 - 262004 - 262005 - 262006 - 262008 - 262009 - 262011 - 262014 - 262015 - 262017 - 262018 - 262023 - 262100 - 262101 - 262102 - 262103 - 262104 - 262105 - 262106 - 262108 - 262113 - 262114 - 262115 - 262116 - 262118 - 262119 - 262120 - 262121 - 262122 - 262123 - 262124 - 262125 - 262130 - 262139 - 262140 - 262141 - 262143 - - class: od levtype: o3d stream: oper type: fc - - 262500 - 262501 - 262502 - 262505 - 262506 - 262507 - - class: od levtype: pl stream: oper type: fc - - 1 - 2 - 60 - 129 - 130 - 131 - 132 - 133 - 135 - 138 - 152 - 155 - 156 - 157 - 203 - 246 - 247 - 248 - - class: od levtype: pt stream: oper type: fc - - 54 - 60 - 131 - 132 - 133 - 138 - 155 - 203 - - class: od levtype: pv stream: oper type: fc - - 3 - 54 - 129 - 131 - 132 - 133 - 156 - 203 - - class: od levtype: sfc stream: oper type: fc - - 8 - 9 - 20 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 44 - 45 - 47 - 49 - 50 - 57 - 58 - 66 - 67 - 74 - 78 - 79 - 121 - 122 - 123 - 129 - 134 - 136 - 137 - 139 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 151 - 156 - 159 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 186 - 187 - 188 - 189 - 195 - 196 - 197 - 198 - 201 - 202 - 205 - 206 - 207 - 208 - 209 - 210 - 211 - 212 - 213 - 228 - 229 - 230 - 231 - 232 - 234 - 235 - 236 - 238 - 243 - 244 - 245 - 3020 - 151130 - 151131 - 151132 - 151145 - 151148 - 151163 - 151164 - 151175 - 162071 - 162072 - 174096 - 174098 - 210186 - 210187 - 210188 - 210189 - 210190 - 210191 - 228003 - 228007 - 228008 - 228009 - 228010 - 228011 - 228012 - 228013 - 228014 - 228020 - 228021 - 228022 - 228023 - 228024 - 228026 - 228027 - 228028 - 228029 - 228035 - 228036 - 228044 - 228045 - 228046 - 228047 - 228048 - 228050 - 228051 - 228057 - 228058 - 228080 - 228081 - 228082 - 228083 - 228084 - 228085 - 228088 - 228089 - 228090 - 228129 - 228130 - 228131 - 228132 - 228216 - 228217 - 228218 - 228219 - 228220 - 228221 - 228222 - 228223 - 228224 - 228225 - 228226 - 228227 - 228231 - 228232 - 228233 - 228234 - 228235 - 228236 - 228237 - 228239 - 228240 - 228241 - 228246 - 228247 - 228249 - 228251 - 260004 - 260005 - 260015 - 260048 - 260109 - 260121 - 260123 - 260242 - 260255 - 260289 - 260318 - 260319 - 260320 - 260321 - 260338 - 260339 - 261001 - 261002 - 261014 - 261015 - 261016 - 261018 - 261023 - - class: od levtype: sol stream: oper type: fc - - 33 - 238 - 228038 - 228141 - - class: od levtype: ml stream: oper type: me - - 200130 - 200131 - 200132 - 200138 - 200152 - 200155 - - class: od stream: oper type: ssd - - 260510 - 260512 - - class: od stream: oper type: tf - - 129 - 999 - - class: od levtype: pl stream: oper type: wp - - 129 - 130 - 131 - 132 - 157 - - class: od levtype: sfc stream: oper type: wp - - 39 - 40 - 41 - 42 - 121 - 122 - 123 - 129 - 134 - 139 - 141 - 142 - 143 - 144 - 151 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 172 - 183 - 186 - 187 - 188 - 189 - 228 - 236 - 228235 - - class: od stream: scda type: tf - - 129 - 999 - - class: od levtype: sfc stream: swmm type: fcmax - - 140220 - 140221 - 140229 - 140231 - 140232 - 140233 - 140244 - 140245 - - class: od levtype: sfc stream: swmm type: fcmean - - 140220 - 140221 - 140229 - 140231 - 140232 - 140233 - 140244 - 140245 - - class: od levtype: sfc stream: swmm type: fcmin - - 140220 - 140221 - 140229 - 140231 - 140232 - 140233 - 140244 - 140245 - - class: od levtype: sfc stream: swmm type: fcstdev - - 140220 - 140221 - 140229 - 140231 - 140232 - 140233 - 140244 - 140245 - - class: od levtype: sfc stream: waef type: efi - - 132216 - - class: od levtype: sfc stream: waef type: efic - - 132216 - - class: od levtype: sfc stream: waef type: ep - - 131074 - 131075 - 131076 - 131077 - 131078 - 131079 - 131080 - 131081 - - class: od levtype: sfc stream: waef type: pf - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: waef type: sot - - 132216 - - class: od levtype: sfc stream: wasf type: fc - - 140220 - 140221 - 140229 - 140230 - 140231 - 140232 - 140233 - 140244 - 140245 - 140249 - - class: od levtype: sfc stream: wave type: 4v - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140246 - 140247 - 140248 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: wave type: an - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140246 - 140247 - 140248 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: wave type: fc - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: weef type: cf - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: weef type: fcmax - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: weef type: fcmean - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: weef type: fcmin - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: weef type: fcstdev - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: weef type: pf - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140211 - 140212 - 140214 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: weeh type: cf - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: weeh type: fcmax - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: weeh type: fcmean - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: weeh type: fcmin - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: weeh type: fcstdev - - 140229 - 140231 - 140232 - 140233 - 140245 - - class: od levtype: sfc stream: weeh type: pf - - 140098 - 140099 - 140100 - 140101 - 140102 - 140103 - 140104 - 140105 - 140112 - 140113 - 140114 - 140115 - 140116 - 140117 - 140118 - 140119 - 140120 - 140121 - 140122 - 140123 - 140124 - 140125 - 140126 - 140127 - 140128 - 140129 - 140131 - 140132 - 140133 - 140134 - 140207 - 140208 - 140209 - 140215 - 140216 - 140217 - 140218 - 140219 - 140220 - 140221 - 140222 - 140223 - 140224 - 140225 - 140226 - 140227 - 140228 - 140229 - 140230 - 140231 - 140232 - 140233 - 140234 - 140235 - 140236 - 140237 - 140238 - 140239 - 140244 - 140245 - 140249 - 140251 - 140252 - 140253 - 140254 - - class: od levtype: sfc stream: wehs type: cd - - 140200 metkit-1.19.2/share/metkit/.gitignore0000664000175000017500000000001315232567717017665 0ustar alastairalastair*.list tmp metkit-1.19.2/share/metkit/chemids.yaml0000664000175000017500000001702215232567717020205 0ustar alastairalastair--- # File automatically generated by make-yaml.py --chem # Do not edit - [2, CO, Carbon monoxide] - [3, H2O2, Hydrogen peroxide] - [4, CH4_c, Methane (chemistry)] - [5, HCHO, Formaldehyde] - [6, HNO3, Nitric acid] - [7, CH3OOH, Methyl peroxide] - [8, SO2_v, Volcanic sulphur dioxide] - [9, par, Paraffins] - [10, C2H4, Ethene] - [11, Rn, Radon] - [12, ALD2, Aldehydes] - [13, PAN, Peroxyacetyl nitrate] - [14, ROOH, Peroxides] - [15, NO3_org, Organic nitrates] - [16, C5H8, Isoprene] - [17, NO2, Nitrogen dioxide] - [18, DMS, Dimethyl sulfide] - [19, NH3, Ammonia] - [20, SO4, Sulfate] - [21, NH4, Ammonium] - [22, MSA, Methane sulfonic acid] - [23, CH3COCHO, Methyl glyoxal] - [24, O3S, Stratospheric ozone] - [25, CO2, Carbon dioxide] - [26, Pb, Lead] - [27, 'NO', Nitrogen monoxide] - [28, HO2_r, Hydroperoxy radical] - [29, CH3O2_r, Methylperoxy radical] - [30, OH_r, Hydroxyl radical] - [31, N2O, Nitrous oxide] - [32, NO3, Nitrate radical] - [33, N2O5, Dinitrogen pentoxide] - [34, HO2NO2, Pernitric acid] - [35, C2O3_r, Peroxy acetyl radical] - [36, ROR, Organic ethers] - [37, rxpar, PAR budget corrector] - [38, xo2, NO to NO2 operator] - [39, xo2n, NO to alkyl nitrate operator] - [40, NH2, Amine] - [41, psc, Polar stratospheric cloud] - [42, CH3OH, Methanol] - [43, HCOOH, Formic acid] - [44, MCOOH, Methacrylic acid] - [45, C2H6, Ethane] - [46, C2H5OH, Ethanol] - [47, C3H8, Propane] - [48, C3H6, Propene] - [49, C10H16, Terpenes] - [50, ISPD, Methacrolein MVK] - [52, CH3COCH3, Acetone] - [53, aco2, Acetone product] - [55, hypropo2] - [56, noxa, Nitrogen oxides Transp] - [57, CO2_c, Carbon dioxide (chemistry)] - [58, N2O_c, Nitrous oxide (chemistry)] - [59, H2O_c, Water vapour (chemistry)] - [60, O2, Dioxygen] - [61, O2_1s, Singlet sigma dioxygen] - [62, O2_1d, Singlet Delta dioxygen] - [63, OClO, Chlorine dioxide] - [64, ClONO2, Chlorine nitrate] - [65, HOCl, Hypochlorous acid] - [66, Cl2, Chlorine] - [67, ClNO2, Nitryl chloride] - [68, HBr, Hydrogen bromide] - [69, Cl2O2, Dichlorine dioxide] - [70, HOBr, Hypobromous acid] - [71, CFC11, Trichlorofluoromethane] - [72, CFC12, Dichlorodifluoromethane] - [73, CFC113, Trichlorotrifluoroethane] - [74, CFC114, Dichlorotetrafluoroethane] - [75, CFC115, Chloropentafluoroethane] - [76, ccl4, Tetrachloromethane] - [77, ch3ccl3, Methyl chloroform] - [78, CH3Cl, Methyl chloride] - [79, hcfc22, Chlorodifluoromethane] - [80, CH3Br, Methyl bromide] - [82, ha1211, Bromochlorodifluoromethane] - [83, ha1301, Trifluorobromomethane] - [85, H2SO4, Sulfuric acid] - [86, HONO, Nitrous acid] - [93, DIEN, Butadiene] - [94, C2H5OOH, Ethyl hydroperoxide] - [96, CH3COOH, Acetic acid] - [98, CH3CHO, Acetaldehyde] - [99, tol, Toluene and less reactive aromatics] - [100, xyl, Xylene and more reactive aromatics] - [101, GLYALD, Glycolaldehyde] - [102, CRESOL] - [104, CH3COOOH, Peracetic acid] - [105, KET, Ketones] - [106, EO2, Hoch2ch2o2] - [107, GLYOXAL] - [108, EO, Hoch2ch2o] - [109, DCB, Unsaturated dicarbonyls] - [110, MACR, Methacrolein] - [111, UDD, Unsaturated hydroxy dicarbonyl] - [112, C3H7O2, Isopropyldioxidanyl] - [113, HKET, Hydroxy ketone] - [114, C3H7OOH, Isopropyl hydroperoxide] - [115, PO2, C3h6oho2] - [116, POOH, C3h6ohooh] - [118, HYAC, Hydroxyacetone] - [119, PAA, Peroxyacetic acid] - [124, BIGENE, Lumped alkenes] - [126, BIGALK, Lumped alkanes] - [129, NOx, Nitrogen oxides] - [138, MVK, Methylvinylketone] - [139, PhO, Phenoxy radical] - [155, Ox, Oxides] - [159, BrOx, Bromine oxides] - [161, ISOPOOH, Hoch2c(ooh)(ch3)ch=ch2] - [163, DMSO, Dimethyl sulfoxyde] - [166, H2S, Hydrogensulfide] - [168, NOy, All nitrogen oxides] - [169, cly, Chlorine family] - [171, bry, Bromine family] - [173, N, Nitrogen atom] - [174, ClO, Chlorine monoxide] - [175, Cl, Chlorine atom] - [176, BrO, Bromine monoxide] - [177, h_c, Hydrogen atom] - [178, ch3, Methyl group] - [186, sog1, Condensable gas type 1] - [187, sog2a, Condensable gas type 2a] - [188, sog2b, Condensable gas type 2b] - [189, SO3, Sulfur trioxide] - [190, OCS_c, Carbonyl sulfide] - [191, Br, Bromine atom] - [192, Br2, Bromine] - [193, BrCl, Bromine monochloride] - [194, BrONO2, Bromine nitrate] - [195, CH2Br2, Dibromomethane] - [196, CH3O, Methoxy radical] - [197, CHBr3, Tribromomethane] - [198, cloo, Asymmetric chlorine dioxide radical] - [199, H2, Dihydrogen] - [200, HCl, Hydrogen chloride] - [201, HCO, Formyl radical] - [202, HF, Hydrogen fluoride] - [203, O, Oxygen atom] - [204, O_1d, Excited oxygen atom] - [205, O_3p, Ground state oxygen atom] - [222, AROO2, Aromatic peroxide radical] - [224, CH3CN, Acetonitrile] - [225, CH3O2NO2, Methyl peroxy nitrate] - [226, HCN, Hydrogen cyanide] - [227, HPALD1, Hydroperoxy aldehydes type 1] - [228, HPALD2, Hydroperoxy aldehydes type 2] - [229, ISOPBO2, Isoprene peroxy type B] - [230, ISOPDO2, Isoprene peroxy type D] - [231, gO3, GEMS ozone] - [232, SF6, Sulphur hexafluoride] - [233, SO2, Sulphur dioxide] - [236, O3, Ozone] - [311, ole, Olefins] - [331, aVOC, Anthropogenic volatile organic compounds] - [332, bbVOC, Biomass burning volatile organic compounds] - [359, H2O, Water vapour] - [360, H2Orain, Rain water] - [361, H2Ocloud, Cloud water] - [400, gasmol, Gaseous molecules] - [404, CH4, Methane] - [431, C7H8, Toluene] - [432, C6H6, Benzene] - [433, C8H10, Xylene] - [438, C4H10, Butane] - [439, C4H8, Butene] - [440, C5H10, Pentene] - [441, C6H12, Hexene] - [442, C7H14, Heptene] - [443, C8H16, Octene] - [444, C5H12, Pentane] - [445, C6H14, Hexane] - [446, C7H16, Heptane] - [447, C8H18, Octane] - [900, aer_NO3, Nitrate] - [901, aer_seasalt_003_05, Sea salt aerosol (0.03 - 0.5 um)] - [902, aer_seasalt_05_5, Sea salt aerosol (0.5 - 5 um)] - [903, aer_seasalt_5_20, Sea salt aerosol (5 - 20 um)] - [904, aer_dust_003_055, Dust aerosol (0.03 - 0.55 um)] - [905, aer_dust_055_09, Dust aerosol (0.55 - 0.9 um)] - [906, aer_dust_09_20, Dust aerosol (0.9 - 20 um)] - [907, aer_hicorgmat, Hydrophilic organic matter aerosol] - [908, aer_hocorgmat, Hydrophobic organic matter aerosol] - [909, aer_hicblackcarb, Hydrophilic black carbon aerosol] - [910, aer_hocblackcarb, Hydrophobic black carbon aerosol] - [911, aer_sulph, Sulphate aerosol] - [912, aer_nitrfine, Nitrate fine mode aerosol (<= 2.5 um)] - [913, aer_nitrcoars, Nitrate coarse mode aerosol (>2.5 um)] - [914, aer_nh4, Ammonium aerosol] - [915, aer_biosecorg, Biogenic secondary organic aerosol] - [916, aer_antsecorg, Anthropogenic secondary organic aerosol] - [917, strataer, Stratospheric aerosol] - [918, SOA, Secondary particulate organic matter dry] - [919, aer_biosecorg_pre, Biogenic secondary organic aerosol precursor] - [920, aer_antsecorg_pre, Anthropogenic secondary organic aerosol precursor] - [921, aer_so2_pre, SO2 precursor] - [922, aer_total, Total aerosol] - [923, aer_sm, Aerosol small mode] - [924, aer_lm, Aerosol large mode] - [925, aer_dust_09_2p5, Dust aerosol (0.9 - 2.5 um)] - [926, aer_dust_2p5_5, Dust aerosol (2.5 - 5.0 um)] - [927, aer_dust_5_10, Dust aerosol (5.0 - 10.0 um)] - [928, aer_dust_10_20, Dust aerosol (10.0 - 20.0 um)] - [929, aer_pm_1, Particulate matter <= 1 um] - [930, aer_pm_2p5, Particulate matter <= 2.5 um] - [931, aer_pm_10, Particulate matter <= 10 um] - [932, volash, Volcanic Ash] - [933, aer_blackcarb, Black carbon organic aerosol] - [934, aer_orgmat, Organic matter aerosol] - [935, aer_dust, Dust aerosol] - [936, aer_seasalt, Sea salt aerosol] - [937, aer_dust_1, Dust aerosol smaller than 1 um] - [938, aer_dust_2p5, Dust aerosol smaller than 2.5 um] - [939, aer_dust_10, Dust aerosol smaller than 10 um] - [940, aer_dust_20, Dust aerosol smaller than 20 um] - [941, SO4_v, Volcanic Sulphate] - [942, aer_seasalt_wet80p, Sea salt wet at 80% relative humidity] metkit-1.19.2/share/metkit/make-params-yaml.py0000664000175000017500000000477315232567717021426 0ustar alastairalastair# (c) Copyright 1996-2012 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. # Documentation is available on Confluence # https://confluence.ecmwf.int/display/METK/MetKit+configuration import os import sys from collections import OrderedDict import psycopg2 import yaml DEFAULT_PORT = 5432 def main(): try: HOST = os.environ['PRODUCT_CATALOGUE_HOST'] DB = os.environ['PRODUCT_CATALOGUE_DB'] USER = os.environ['PRODUCT_CATALOGUE_USER'] PASSWORD = os.environ['PRODUCT_CATALOGUE_PASSWORD'] PORT = ( int(os.environ['PRODUCT_CATALOGUE_PORT']) if 'PRODUCT_CATALOGUE_PORT' in os.environ else DEFAULT_PORT ) except KeyError as e: print("ERROR: Environment variable not found: {}".format(e)) sys.exit(1) with psycopg2.connect(host=HOST, dbname=DB, user=USER, password=PASSWORD, port=PORT) as conn: with conn.cursor() as cur: cur.execute( "SELECT DISTINCT class, stream, type, levtype, param::INTEGER FROM fields WHERE param != '' ORDER BY class, stream, type, levtype, param::INTEGER" ) rows = cur.fetchall() index = OrderedDict() for row in rows: cl, stream, type_, levtype, param = row key = (cl, stream, type_, levtype) if key not in index: index[key] = [] index[key].append(param) # Manually add type=tf parameters for PGEN index[("od", "oper", "tf", "")] = [129, 999] index[("od", "scda", "tf", "")] = [129, 999] index[("od", "enfo", "tf", "")] = [129, 999] yaml_dump_data = [] for key, vals in sorted(index.items()): if key[3]: yaml_dump_data.append( [{"class":key[0], "stream":key[1], "type":key[2], "levtype":key[3]}, vals] ) else: yaml_dump_data.append([{"class":key[0], "stream":key[1], "type":key[2]}, vals]) with open("params.yaml", "w") as f: f.write( "# File automatically generated by %s\n# Do not edit\n\n" % (os.path.basename(__file__)) ) f.write(yaml.safe_dump(yaml_dump_data, default_flow_style=False)) if __name__ == "__main__": main() metkit-1.19.2/share/metkit/CMakeLists.txt0000664000175000017500000000110715232567717020442 0ustar alastairalastairlist( APPEND files language.yaml modifiers.yaml reportype.yaml obstype.yaml paramids.yaml params.yaml params-static.yaml param-matching.yaml chemids.yaml bufr-subtypes.yaml axis.yaml shortname-context.yaml ) foreach( _file ${files} ) configure_file( ${_file} ${CMAKE_BINARY_DIR}/share/metkit/${_file} COPYONLY ) if( HAVE_METKIT_CONFIG ) install( FILES ${CMAKE_BINARY_DIR}/share/metkit/${_file} DESTINATION "share/metkit" PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ ) endif() endforeach() add_subdirectory( odb ) metkit-1.19.2/share/metkit/shortname-context.yaml0000664000175000017500000000254315232567717022255 0ustar alastairalastair--- - 10spg10 - 10spg15 - al - apab_s - apt - asn - bas_con - bld - bli - btp - cap - cape - cape_con - ccc - cdct - clc - cp - cpa - d - dhcc - dhlc - dhr - dhvd - dirc - dqv_con - dslm - dt_con - du_con - dv_con - e - ewgd - gh - hbas_con - hc300m - hc700m - hcbtm - hcc - hfds - htcc - htlc - htop_con - htop_dc - hzerocl - icec - iced - iceg - imgd - lai - lcc - lnsp - lsp - lspa - lwhr - mcc - mdps - mdww - mld - mlotst010 - mlotst030 - mlotst125 - mlott02 - mlott05 - mpps - mpww - msl - msror - mssror - mtha - mthd - mucape - mwd - ndvi - nlwrs - nlwrt - nsf - nsgd - nswrt - o3 - par - pp - pp1d - prec - pres - prr_con - prr_gsp - prs_con - prs_gsp - pta - qc - qi - r - ra - rain_con - rain_gsp - rn - ro - ru-103 - sc300m - sc700m - sd - sf - shf - shww - sialb - sigmat - sipd - sisnthick - sitemptop - sithick - siue - sivn - slhf - snowlmt - so - sohr_rad - sos - sp - spc - sr - sro - srweq - sshf - ssr - ssrd - sst - stfbarot - str - strd - strda - sund - swdi - swh - swhr - swl2 - swmp - t - t14d - t17d - t20d - t26d - t28d - t_ice - t_snow - ta - tauuo - tauvo - tcc - tcond - thetao - thhr_rad - tke - tmax - top_con - tos - tp - tprate - tsec - tsr - tsw - ttr - twater - u - ucq - uflx - ustr - ut - uu - uv - uvi - v - vapp - vdh - vdmw - vdzw - veg - vflx - vmax_10m - vo - vp - vt - vv - w - w_i - wilt - wind - wo - wvsp1 - wvsp2 - wvsp3 - z - zos metkit-1.19.2/share/metkit/odb/0000775000175000017500000000000015232567717016447 5ustar alastairalastairmetkit-1.19.2/share/metkit/odb/group.txt0000664000175000017500000001114415232567717020345 0ustar alastairalastair id↑ ; name ; kind_id ; marsname ; description ; 1 ; HIRS ; 2 ; HIRS ; ; 2 ; AMSUA ; 2 ; AMSUA ; ; 3 ; AMSUB ; 2 ; AMSUB ; ; 4 ; MHS ; 2 ; MHS ; ; 5 ; GEOS ; 2 ; GEOS ; ; 6 ; RESAT ; 2 ; RESAT ; ; 7 ; MERIS ; 2 ; MERIS ; ; 8 ; GPSRO ; 2 ; GPSRO ; ; 9 ; SATOB ; 3 ; SATOB ; ; 10 ; SCATT ; 2 ; SCATT ; ; 11 ; SSMI All-sky ; 2 ; SSMI_AS ; ; 12 ; IASI ; 2 ; IASI ; ; 13 ; AIRS ; 2 ; AIRS ; ; 14 ; SSMIS All-sky ; 2 ; SSMIS_AS ; ; 15 ; TMI All-sky ; 2 ; TMI_AS ; ; 16 ; AMSRE All-sky ; 2 ; AMSRE_AS ; ; 17 ; CONV ; 1 ; CONV ; ; 18 ; STORM ; 1 ; STORM ; STORM ; 19 ; SMOS ; 2 ; SMOS ; ; 20 ; WINDSAT All-sky ; 2 ; WINDSAT_AS ; ; 21 ; SSMI ; 2 ; SSMI ; ; 22 ; AMSUA All-sky ; 2 ; AMSUA_AS ; ; 23 ; AMSRE ; 2 ; AMSRE ; ; 24 ; TMI ; 2 ; TMI ; ; 25 ; SSMIS ; 2 ; SSMIS ; ; 26 ; GBRAD ; 1 ; GBRAD ; ; 27 ; MWHS ; 2 ; MWHS ; ; 28 ; MWTS ; 2 ; MWTS ; ; 29 ; MWRI All-sky ; 2 ; MWRI_AS ; ; 30 ; IRAS ; 2 ; IRAS ; ; 31 ; MSU ; 2 ; MSU ; ; 32 ; SSU ; 2 ; SSU ; ; 33 ; VTPR1 ; 2 ; VTPR1 ; ; 34 ; VTPR2 ; 2 ; VTPR2 ; ; 35 ; ATMS ; 2 ; ATMS ; ; 36 ; RESAT Averaging Kernels ; 2 ; RESAT_AK ; ; 37 ; CRIS ; 2 ; CRIS ; Cross-track Infrared Sounder ; 38 ; WAVE integrated Parameters ; 3 ; WAVE_IP ; WAVE integrated Parameters ; 39 ; WAVE spectra ; 3 ; WAVE_SP ; WAVE spectra ; 40 ; RAINGG ; 5 ; RAINGG ; Rain Gauge ; 41 ; SURFACE MULTISENSOR ; 1 ; SFC_MS ; SURFACE MULTISENSOR ; 42 ; AMSR-2 All-sky ; 2 ; AMSR2_AS ; AMSR-2 All-sky ; 43 ; SAPHIR All-sky ; 2 ; SAPHIR_AS ; SAPHIR All-sky ; 44 ; AMSUB All-sky ; 2 ; AMSUB_AS ; AMSUB All-sky ; 45 ; MHS All-sky ; 2 ; MHS_AS ; MHS All-sky ; 46 ; Doppler Wind Lidar ; 2 ; DWL ; Doppler Wind Lidar ; 47 ; IRIS ; 2 ; IRIS ; Infrared Interferometer Spectrometer ; 48 ; Altimeter ; 3 ; ALT ; Altimeter ; 49 ; AATSR ; 2 ; AATSR ; Advanced Along Track Scanning Radiometer ; 50 ; ATMS All-sky ; 2 ; ATMS_AS ; ATMS All-sky ; 51 ; GMI All-sky ; 2 ; GMI_AS ; GMI All-sky ; 52 ; GODAE Sea Surface Temperatures ; 4 ; GODAE_SST ; GODAE Sea Surface Temperatures ; 53 ; ATOVS MULTISENSOR ; 4 ; ATOVS_MS ; ATOVS MULTISENSOR ; 54 ; Atmospheric composition ; 1 ; ATMOSPHERIC_COMPOSITION ; Atmospheric composition ; 55 ; NON-SURFACE MULTISENSOR ; 4 ; NON_SFC_MS ; NON-SURFACE MULTISENSOR ; 56 ; MWTS2 ; 2 ; MWTS2 ; Microwave Temperature Sounder 2 ; 57 ; SSMI 1DVAR TCWV Cloudy-Sky ; 2 ; SSMI_1D ; SSMI 1DVAR TCWV Cloudy-Sky ; 58 ; MWHS2 All-sky ; 2 ; MWHS2_AS ; Microwave Humidity Sounder 2 ; 59 ; SSMT2 ; 2 ; SSMT2 ; SSMT2 ; 60 ; SMAP ; 2 ; SMAP ; Soil Moisture Active and Passive ; 61 ; TOVS MULTISENSOR ; 4 ; TOVS_MS ; TOVS MULTISENSOR ; 62 ; Cloud reflectivity ; 2 ; CLOUD_R ; Cloud reflectivity ; 63 ; Cloud Lidar ; 2 ; CLOUD_L ; Cloud Lidar ; 64 ; Satellite Lightning ; 2 ; SATELLITE_LIGHTNING ; Satellite Lightning ; 65 ; GEOS VIS ; 2 ; GEOS_VIS ; GEOS visible reflectances ; 66 ; OCONV ; 1 ; OCONV ; Ocean conventional in situ ; 67 ; MWTS3 All-sky ; 2 ; MWTS3_AS ; MWTS3 All-sky ; 68 ; GIIRS ; 2 ; GIIRS ; GIIRS ; 69 ; HIRAS ; 2 ; HIRAS ; HIRAS ; 70 ; TROPICS All-sky ; 2 ; TROPICS_AS ; TROPICS All-sky ; 71 ; SSMT1 ; 2 ; SSMT1 ; SSMT1 ; 72 ; SSMT1 All-sky ; 2 ; SSMT1_AS ; SSMT1 All-sky ; 73 ; MWS All-sky ; 2 ; MWS_AS ; MWS All-sky ; 74 ; MWIICI All-sky ; 2 ; MWIICI_AS ; MWIICI All-sky ; 75 ; AWS All-sky ; 2 ; AWS_AS ; AWS All-sky ; 76 ; LEOS VIS ; 2 ; LEOS_VIS ; LEOS visible reflectances ; 77 ; SSH ; 2 ; SSH ; SSH ; 78 ; SIRS ; 2 ; SIRS ; SIRS ; 79 ; SCR ; 2 ; SCR ; SCR (Selective Chopper Radiometer) ; 80 ; SCAMS All-sky ; 2 ; SCAMS_AS ; SCAMS All-sky ; 81 ; NEMS All-sky ; 2 ; NEMS_AS ; NEMS All-sky ; 82 ; AMSR-3 All-sky ; 2 ; AMSR3_AS ; AMSR-3 All-sky ; 83 ; IRS ; 2 ; IRS ; IRS ; 84 ; IASI-NG ; 2 ; IASING ; IASI-NG ; 85 ; SMAP All-sky ; 2 ; SMAP_AS ; SMAP All-sky ; 86 ; SSMT2 All-sky ; 2 ; SSMT2_AS ; SSMT2 All-sky ; 87 ; SMMR All-sky ; 2 ; SMMR_AS ; SMMR All-sky ; 99 ; TEST ; 4 ; TEST ; ; metkit-1.19.2/share/metkit/odb/class.table0000664000175000017500000000413015232567717020563 0ustar alastairalastair0 0 Unknown 1 od Operational archive 2 rd Research department 3 er 15 years reanalysis (ERA15) 4 cs ECSN 5 e4 40 years reanalysis (ERA40) 6 dm DEMETER 7 pv PROVOST 8 el ELDAS 9 to TOST 10 co COSMO-LEPS 11 en ENSEMBLES 12 ti TIGGE 13 me MERSEA 14 ei ERA Interim 15 sr Short-Range Ensemble Prediction System 16 dt Data Targeting System 17 la ALADIN-LAEF 18 yt YOTC 19 mc Copernicus Atmosphere Monitoring Service (CAMS, previously MACC) 20 pe Permanent experiments 21 em ERA-CLIM model integration for the 20th-century (ERA-20CM) 22 e2 ERA-CLIM reanalysis of the 20th-century using surface observations only (ERA-20C) 23 ea ERA5 24 ep ERA-CLIM2 coupled reanalysis of the 20th-century (CERA-20C) 25 rm EURO4M 26 nr NOAA/CIRES 20th Century Reanalysis version II 27 s2 Sub-seasonal to seasonal prediction project (S2S) 28 j5 Japanese 55 year Reanalysis (JRA55) 29 ur UERRA 30 et ERA-CLIM2 coupled reanalysis of the satellite era (CERA-SAT) 31 c3 Copernicus Climate Change Service (C3S) 32 yp YOPP 33 l5 ERA5/LAND 34 lw WMO Lead Centre Wave Forecast Verification 35 ce Copernicus Emergency Management Service (CEMS) 36 cr Copernicus Atmosphere Monitoring Service (CAMS) Research 37 rr Copernicus Regional ReAnalysis (CARRA/CERRA) 38 ul Project ULYSSES 39 gw Global Wildfire Information System 40 e6 ERA6 41 l6 ERA6/LAND 42 ef EFAS (European flood awareness system) 43 gf GLOFAS (Global flood awareness system) 44 gg Greenhouse Gases 45 ml Machine learning 46 d1 Destination Earth 47 o6 Ocean ReAnalysis 6 48 eh C3S European hydrology 49 gh C3S Global hydrology 50 ci CERISE project 51 ai Operational AIFS 52 ed EERIE project 53 ng nextGEMS project 54 lr WMO Lead Centre for Global Climate Re-Analyses 55 a5 ECMWF Atmospheric Composition reanalysis version 5 (EAC5) 99 te Test 100 at Austria 101 be Belgium 102 hr Croatia 103 dk Denmark 104 fi Finland 105 fr France 106 de Germany 107 gr Greece 108 hu Hungary 109 is Iceland 110 ie Ireland 111 it Italy 112 nl Netherlands 113 no Norway 114 pt Portugal 115 si Slovenia 116 es Spain 117 se Sweden 118 ch Switzerland 119 tr Turkey 120 uk United Kingdom 121 ms Member States projects 199 ma Metaps metkit-1.19.2/share/metkit/odb/marsrequest.yaml0000664000175000017500000000017715232567717021713 0ustar alastairalastair--- CLASS: class DATE: andate TIME: antime TYPE: type OBSGROUP: groupid@hdr REPORTYPE: reportype STREAM: stream EXPVER: expver metkit-1.19.2/share/metkit/odb/type.table0000664000175000017500000000035415232567717020443 0ustar alastairalastair262 mfb MonDB feedback 263 ofb ODB feedback 264 oai ODB analysis input 265 sfb Summary Feedback 266 fsoifb Forecast Sensitivity to Observations Impact Feedback 267 fcdfb Forecast Departures Feedback 268 ofa ODB Feedback from Analysis metkit-1.19.2/share/metkit/odb/stream.table0000664000175000017500000001275015232567717020760 0ustar alastairalastair0 0 Unknown 1022 fsob Forecast sensitivity to observations 1023 fsow Forecast sensitivity to observations wave 1024 dahc Daily archive hindcast 1025 oper Atmospheric model 1026 scda Atmospheric model (short cutoff) 1027 scwv Wave model (short cutoff) 1028 dcda Atmospheric model (delayed cutoff) 1029 dcwv Wave model (delayed cutoff) 1030 enda Ensemble data assimilation 1032 efho Ensemble forecast hindcast overlap 1033 enfh Ensemble forecast hindcasts 1034 efov Ensemble forecast overlap 1035 enfo Ensemble prediction system 1036 sens Sensitivity forecast 1037 maed Multianalysis ensemble data 1038 amap Analysis for multianalysis project 1039 efhc Ensemble forecast hindcasts (obsolete) 1040 efhs Ensemble forecast hindcast statistics 1041 toga TOGA 1042 cher Chernobyl 1043 mnth Monthly means 1044 supd Deterministic supplementary data 1045 wave Wave model 1046 ocea Ocean 1047 fgge FGGE 1050 egrr Bracknell 1051 kwbc Washington 1052 edzw Offenbach 1053 lfpw Toulouse 1054 rjtd Tokyo 1055 cwao Montreal 1056 ammc Melbourne 1057 efas European flood awareness system (EFAS) 1058 efse European flood awareness system (EFAS) seasonal forecasts 1059 efcl European flood awareness system (EFAS) climatology 1060 wfas Global flood awareness system (GLOFAS) 1061 wfcl Global flood awareness system (GLOFAS) climatology 1062 wfse Global flood awareness system (GLOFAS) seasonal forecasts 1063 efrf European flood awareness system (EFAS) reforecasts 1064 efsr European flood awareness system (EFAS) seasonal reforecasts 1065 wfrf Global flood awareness system (GLOFAS) reforecasts 1066 wfsr Global flood awareness system (GLOFAS) seasonal reforecasts 1070 msdc Monthly standard deviation and covariance 1071 moda Monthly means of daily means 1072 monr Monthly means using G. Boer's step function 1073 mnvr Monthly variance and covariance data using G. Boer's step function 1074 msda Monthly standard deviation and covariance of daily means 1075 mdfa Monthly means of daily forecast accumulations 1076 dacl Daily climatology 1077 wehs Wave ensemble forecast hindcast statistics 1078 ewho Ensemble forecast wave hindcast overlap 1079 enwh Ensemble forecast wave hindcasts 1080 wamo Wave monthly means 1081 waef Wave ensemble forecast 1082 wasf Wave seasonal forecast 1083 mawv Multianalysis wave data 1084 ewhc Wave ensemble forecast hindcast (obsolete) 1085 wvhc Wave hindcast 1086 weov Wave ensemble forecast overlap 1087 wavm Wave model (standalone) 1088 ewda Ensemble wave data assimilation 1089 dacw Daily climatology wave 1090 seas Seasonal forecast 1091 sfmm Seasonal forecast atmospheric monthly means 1092 swmm Seasonal forecast wave monthly means 1093 mofc Monthly forecast 1094 mofm Monthly forecast means 1095 wamf Wave monthly forecast 1096 wmfm Wave monthly forecast means 1097 smma Seasonal monthly means anomalies 1098 clte Climate run output 1099 clmn Climate run monthly means output 1100 dame Daily means 1110 seap Sensitive area prediction 1120 eefh Extended ensemble forecast hindcast 1121 eehs Extended ensemble forecast hindcast statistics 1122 eefo Extended ensemble prediction system 1123 weef Wave extended ensemble forecast 1124 weeh Wave extended ensemble forecast hindcast 1125 wees Wave extended ensemble forecast hindcast statistics 1200 mnfc Real-time 1201 mnfh Hindcasts 1202 mnfa Anomalies 1203 mnfw Wave real-time 1204 mfhw Monthly forecast hindcasts wave 1205 mfaw Wave anomalies 1206 mnfm Real-time means 1207 mfhm Hindcast means 1208 mfam Anomaly means 1209 mfwm Wave real-time means 1210 mhwm Wave hindcast means 1211 mawm Wave anomaly means 1220 mmsf Multi-model seasonal forecast 1221 msmm Multi-model seasonal forecast atmospheric monthly means 1222 wams Multi-model seasonal forecast wave 1223 mswm Multi-model seasonal forecast wave monthly means 1224 mmsa Multi-model seasonal forecast monthly anomalies 1230 mmaf Multi-model multi-annual forecast 1231 mmam Multi-model multi-annual forecast means 1232 mmaw Multi-model multi-annual forecast wave 1233 mmwm Multi-model multi-annual forecast wave means 1240 esmm Combined multi-model monthly means 1241 ehmm Combined multi-model hindcast monthly means 1242 edmm Ensemble data assimilation monthly means 1243 edmo Ensemble data assimilation monthly means of daily means 1244 ewmo Ensemble wave data assimilation monthly means of daily means 1245 ewmm Ensemble wave data assimilation monthly means 1246 espd Ensemble supplementary data 1247 lwda Long window daily archive 1248 lwwv Long window wave 1249 elda Ensemble Long window Data Assimilation 1250 ewla Ensemble Wave Long window data Assimilation 1251 wamd Wave monthly means of daily means 1252 gfas Global fire assimilation system 1253 ocda Ocean data assimilation 1254 olda Ocean Long window data assimilation 1255 gfra Global Fire assimilation system reanalysis 1256 rfsd Retrospective forcing and simulation data 1257 sttd Statistics of deterministic data 1258 stte Statistics of ensemble data 1259 xwda eXtended Window Data Assimilation 1260 sfdd Seasonal forecast daily and sub-daily data 1261 sfmd Seasonal forecast monthly data 1262 shdd Seasonal hindcast daily and sub-daily data 1263 shmd Seasonal hindcast monthly data 1264 sfdp Seasonal forecast derived products 1265 shdp Seasonal hindcast derived products 1266 ldas Land data-assimilation system (near real time) 1267 ldbt Land data-assimilation system behind real time 1268 ldst Land data-assimilation system statistics (near real time) 1269 ldsb Land data-assimilation system statistics behind real time 2231 cnrm Meteo France climate centre 2232 mpic Max Plank Institute 2233 ukmo UKMO climate centre metkit-1.19.2/share/metkit/odb/CMakeLists.txt0000664000175000017500000000071315232567717021210 0ustar alastairalastairlist( APPEND files_odb marsrequest.yaml class.table type.table stream.table group.txt ) foreach( _file ${files_odb} ) configure_file( ${_file} ${CMAKE_BINARY_DIR}/share/metkit/odb/${_file} COPYONLY ) if( HAVE_METKIT_CONFIG ) install( FILES ${CMAKE_BINARY_DIR}/share/metkit/odb/${_file} DESTINATION "share/metkit/odb" PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ ) endif() endforeach() metkit-1.19.2/share/metkit/paramids.yaml0000664000175000017500000171764315232567717020412 0ustar alastairalastair--- # File automatically generated by make-yaml.py --param # Do not edit 1: - strf - Stream function 2: - vp - Velocity potential - vpot 3: - pt - Potential temperature 4: - eqpt - Equivalent potential temperature 5: - sept - Saturated equivalent potential temperature 6: - ssfr - Soil sand fraction 7: - scfr - Soil clay fraction 8: - sro - Surface runoff 9: - ssro - Sub-surface runoff 10: - ws - Wind speed 11: - udvw - U component of divergent wind 12: - vdvw - V component of divergent wind 13: - urtw - U component of rotational wind 14: - vrtw - V component of rotational wind 15: - aluvp - UV visible albedo for direct radiation (climatological) - auvp 16: - aluvd - UV visible albedo for diffuse radiation (climatological) - auvd 17: - alnip - Near IR albedo for direct radiation (climatological) - anip 18: - alnid - Near IR albedo for diffuse radiation (climatological) - anid 19: - uvcs - Clear sky surface UV 20: - parcs - Surface photosynthetically active radiation, clear sky 21: - uctp - Unbalanced component of temperature 22: - ucln - Unbalanced component of logarithm of surface pressure 23: - ucdv - Unbalanced component of divergence 24: - _param_000024 - Reserved for future unbalanced components 25: - _param_000025 - Reserved for future unbalanced components 26: - cl - Lake cover 27: - cvl - Low vegetation cover 28: - cvh - High vegetation cover 29: - tvl - Type of low vegetation 30: - tvh - Type of high vegetation 31: - ci - Sea ice area fraction 32: - asn - Snow albedo 33: - rsn - Snow density 34: - sst - Sea surface temperature - sstk 35: - istl1 - Ice temperature layer 1 36: - istl2 - Ice temperature layer 2 37: - istl3 - Ice temperature layer 3 38: - istl4 - Ice temperature layer 4 39: - swvl1 - Volumetric soil water layer 1 - swv1 40: - swvl2 - Volumetric soil water layer 2 - swv2 41: - swvl3 - Volumetric soil water layer 3 - swv3 42: - swvl4 - Volumetric soil water layer 4 - swv4 43: - slt - Soil type 44: - es - Snow evaporation 45: - smlt - Snowmelt 46: - sdur - Solar duration 47: - dsrp - Surface direct normal short-wave (solar) radiation 48: - magss - Time-integrated magnitude of turbulent surface stress 49: - 10fg - Maximum 10 metre wind gust since previous post-processing 50: - lspf - Large-scale precipitation fraction 51: - mx2t24 - Maximum temperature at 2 metres in the last 24 hours 52: - mn2t24 - Minimum temperature at 2 metres in the last 24 hours 53: - mont - Montgomery potential 54: - pres - Pressure 55: - mean2t24 - Mean temperature at 2 metres in the last 24 hours 56: - mn2d24 - Mean 2 metre dewpoint temperature in the last 24 hours 57: - uvb - Surface downward UV radiation 58: - par - Photosynthetically active radiation at the surface 59: - cape - Convective available potential energy 60: - pv - Potential vorticity 62: - obct - Observation count 63: - stsktd - Start time for skin temperature difference 64: - ftsktd - Finish time for skin temperature difference 65: - sktd - Skin temperature difference 66: - lai_lv - Leaf area index, low vegetation - lailv 67: - lai_hv - Leaf area index, high vegetation - laihv 68: - msr_lv - Minimum stomatal resistance, low vegetation 69: - msr_hv - Minimum stomatal resistance, high vegetation 70: - bc_lv - Biome cover, low vegetation 71: - bc_hv - Biome cover, high vegetation 72: - issrd - Instantaneous surface solar radiation downwards 73: - istrd - Instantaneous surface thermal radiation downwards 74: - sdfor - Standard deviation of filtered subgrid orography (climatological) 75: - crwc - Specific rain water content 76: - cswc - Specific snow water content 77: - etadot - Eta-coordinate vertical velocity - etad 78: - tclw - Total column cloud liquid water 79: - tciw - Total column cloud ice water 80: - _param_000080 - Experimental product 81: - _param_000081 - Experimental product 82: - _param_000082 - Experimental product 83: - _param_000083 - Experimental product 84: - _param_000084 - Experimental product 85: - _param_000085 - Experimental product 86: - _param_000086 - Experimental product 87: - _param_000087 - Experimental product 88: - _param_000088 - Experimental product 89: - _param_000089 - Experimental product 90: - _param_000090 - Experimental product 91: - _param_000091 - Experimental product 92: - _param_000092 - Experimental product 93: - _param_000093 - Experimental product 94: - _param_000094 - Experimental product 95: - _param_000095 - Experimental product 96: - _param_000096 - Experimental product 97: - _param_000097 - Experimental product 98: - _param_000098 - Experimental product 99: - _param_000099 - Experimental product 100: - _param_000100 - Experimental product 101: - _param_000101 - Experimental product 102: - _param_000102 - Experimental product 103: - _param_000103 - Experimental product 104: - _param_000104 - Experimental product 105: - _param_000105 - Experimental product 106: - _param_000106 - Experimental product 107: - _param_000107 - Experimental product 108: - _param_000108 - Experimental product 109: - _param_000109 - Experimental product 110: - _param_000110 - Experimental product 111: - _param_000111 - Experimental product 112: - _param_000112 - Experimental product 113: - _param_000113 - Experimental product 114: - _param_000114 - Experimental product 115: - _param_000115 - Experimental product 116: - _param_000116 - Experimental product 117: - _param_000117 - Experimental product 118: - _param_000118 - Experimental product 119: - _param_000119 - Experimental product 120: - _param_000120 - Experimental product 121: - mx2t6 - Maximum temperature at 2 metres in the last 6 hours 122: - mn2t6 - Minimum temperature at 2 metres in the last 6 hours 123: - 10fg6 - Maximum 10 metre wind gust in the last 6 hours 124: - emis - Surface emissivity 125: - vite - Vertically integrated total energy 126: - _param_000126 - Generic parameter for sensitive area prediction 127: - at - Atmospheric tide 128: - bv - Budget values 129: - z - Geopotential 130: - t - Temperature 131: - u - U component of wind 132: - v - V component of wind 133: - q - Specific humidity 134: - sp - Surface pressure 135: - w - Vertical velocity 136: - tcw - Total column water 137: - tcwv - Total column vertically-integrated water vapour 138: - vo - Vorticity (relative) 139: - stl1 - Soil temperature level 1 - st 140: - swl1 - Soil wetness level 1 141: - sd - Snow depth 142: - lsp - Large-scale precipitation 143: - cp - Convective precipitation 144: - sf - Snowfall 145: - bld - Time-integrated boundary layer dissipation 146: - sshf - Time-integrated surface sensible heat net flux 147: - slhf - Time-integrated surface latent heat net flux 148: - chnk - Charnock - ss 149: - snr - Surface net radiation (SW and LW) 150: - tnr - Top net radiation (SW and LW) 151: - msl - Mean sea level pressure 152: - lnsp - Logarithm of surface pressure 153: - swhr - Short-wave heating rate 154: - lwhr - Long-wave heating rate 155: - d - Divergence 156: - gh - Geopotential height 157: - r - Relative humidity 158: - tsp - Tendency of surface pressure 159: - blh - Boundary layer height 160: - sdor - Standard deviation of sub-gridscale orography 161: - isor - Anisotropy of sub-gridscale orography 162: - anor - Angle of sub-gridscale orography 163: - slor - Slope of sub-gridscale orography 164: - tcc - Total cloud cover 165: - 10u - 10 metre U wind component 166: - 10v - 10 metre V wind component 167: - 2t - 2 metre temperature 168: - 2d - 2 metre dewpoint temperature 169: - ssrd - Surface short-wave (solar) radiation downwards 170: - stl2 - Soil temperature level 2 171: - swl2 - Soil wetness level 2 172: - lsm - Land-sea mask 173: - sr - Surface roughness (climatological) 174: - al - Albedo (climatological) 175: - strd - Surface long-wave (thermal) radiation downwards 176: - ssr - Surface net short-wave (solar) radiation 177: - str - Surface net long-wave (thermal) radiation 178: - tsr - Top net short-wave (solar) radiation 179: - ttr - Top net long-wave (thermal) radiation 180: - ewss - Time-integrated eastward turbulent surface stress 181: - nsss - Time-integrated northward turbulent surface stress 182: - e - Evaporation 183: - stl3 - Soil temperature level 3 184: - swl3 - Soil wetness level 3 185: - ccc - Convective cloud cover 186: - lcc - Low cloud cover 187: - mcc - Medium cloud cover 188: - hcc - High cloud cover 189: - sund - Sunshine duration 190: - ewov - East-West component of sub-gridscale orographic variance 191: - nsov - North-South component of sub-gridscale orographic variance 192: - nwov - North-West/South-East component of sub-gridscale orographic variance 193: - neov - North-East/South-West component of sub-gridscale orographic variance 194: - btmp - Brightness temperature 195: - lgws - Eastward gravity wave surface stress 196: - mgws - Northward gravity wave surface stress 197: - gwd - Gravity wave dissipation 198: - src - Skin reservoir content 199: - veg - Vegetation fraction 200: - vso - Variance of sub-gridscale orography 201: - mx2t - Maximum temperature at 2 metres since previous post-processing 202: - mn2t - Minimum temperature at 2 metres since previous post-processing 203: - o3 - Ozone mass mixing ratio 204: - paw - Precipitation analysis weights 205: - ro - Runoff 206: - tco3 - Total column ozone 207: - 10si - 10 metre wind speed 208: - tsrc - Top net short-wave (solar) radiation, clear sky 209: - ttrc - Top net long-wave (thermal) radiation, clear sky - ttru 210: - ssrc - Surface net short-wave (solar) radiation, clear sky 211: - strc - Surface net long-wave (thermal) radiation, clear sky 212: - tisr - TOA incident short-wave (solar) radiation 213: - vimd - Vertically integrated moisture divergence 214: - dhr - Diabatic heating by radiation 215: - dhvd - Diabatic heating by vertical diffusion 216: - dhcc - Diabatic heating by cumulus convection 217: - dhlc - Diabatic heating large-scale condensation 218: - vdzw - Vertical diffusion of zonal wind 219: - vdmw - Vertical diffusion of meridional wind 220: - ewgd - East-West gravity wave drag tendency 221: - nsgd - North-South gravity wave drag tendency 222: - ctzw - Convective tendency of zonal wind 223: - ctmw - Convective tendency of meridional wind 224: - vdh - Vertical diffusion of humidity 225: - htcc - Humidity tendency by cumulus convection 226: - htlc - Humidity tendency by large-scale condensation 227: - crnh - Tendency due to removal of negative humidity 228: - tp - Total precipitation 229: - iews - Instantaneous eastward turbulent surface stress 230: - inss - Instantaneous northward turbulent surface stress 231: - ishf - Instantaneous surface sensible heat net flux 232: - ie - Instantaneous moisture flux 233: - asq - Apparent surface humidity 234: - lsrh - Logarithm of surface roughness length for heat (climatological) 235: - skt - Skin temperature 236: - stl4 - Soil temperature level 4 237: - swl4 - Soil wetness level 4 238: - tsn - Temperature of snow layer 239: - csf - Convective snowfall 240: - lsf - Large-scale snowfall 241: - acf - Accumulated cloud fraction tendency 242: - alw - Accumulated liquid water tendency 243: - fal - Forecast albedo 244: - fsr - Forecast surface roughness 245: - flsr - Forecast logarithm of surface roughness for heat 246: - clwc - Specific cloud liquid water content 247: - ciwc - Specific cloud ice water content 248: - cc - Fraction of cloud cover 249: - aiw - Accumulated ice water tendency 250: - ice - Ice age 251: - atte - Adiabatic tendency of temperature 252: - athe - Adiabatic tendency of humidity 253: - atze - Adiabatic tendency of zonal wind 254: - atmw - Adiabatic tendency of meridional wind 255: - _param_000255 - Indicates a missing value 928: - thk - thicknes 956: - thka - thicknes anomaly 999: - tc - tropical cyclone 3003: - ptend - Pressure tendency 3005: - icaht - ICAO Standard Atmosphere reference height 3008: - h - Geometrical height above ground 3009: - hstdv - Standard deviation of height 3012: - vptmp - Virtual potential temperature 3014: - papt - Pseudo-adiabatic potential temperature 3015: - tmax - Maximum temperature 3016: - tmin - Minimum temperature 3017: - dpt - Dew point temperature 3018: - depr - Dew point depression (or deficit) 3019: - lapr - Lapse rate 3020: - vis - Visibility 3021: - rdsp1 - Radar spectra (1) 3022: - rdsp2 - Radar spectra (2) 3023: - rdsp3 - Radar spectra (3) 3024: - pli - Parcel lifted index (to 500 hPa) 3025: - ta - Temperature anomaly 3026: - presa - Pressure anomaly 3027: - gpa - Geopotential height anomaly 3028: - wvsp1 - Wave spectra (1) 3029: - wvsp2 - Wave spectra (2) 3030: - wvsp3 - Wave spectra (3) 3031: - wdir - Wind direction 3037: - mntsf - Montgomery stream Function 3038: - sgcvv - Sigma coordinate vertical velocity 3041: - absv - Absolute vorticity 3042: - absd - Absolute divergence 3045: - vucsh - Vertical u-component shear 3046: - vvcsh - Vertical v-component shear 3047: - dirc - Direction of current 3048: - spc - Speed of current 3049: - ucurr - U-component of current 3050: - vcurr - V-component of current 3053: - mixr - Humidity mixing ratio 3054: - pwat - Precipitable water 3055: - vp - Vapour pressure 3056: - satd - Saturation deficit 3059: - prate - Precipitation rate 3060: - tstm - Thunderstorm probability 3062: - lsp - Large-scale precipitation 3063: - acpcp - Convective precipitation (water) 3064: - srweq - Snow fall rate water equivalent 3066: - sde - Snow depth 3067: - mld - Mixed layer depth 3068: - tthdp - Transient thermocline depth 3069: - mthd - Main thermocline depth 3070: - mtha - Main thermocline anomaly 3072: - ccc - Convective cloud cover 3073: - lcc - Low cloud cover 3074: - mcc - Medium cloud cover 3075: - hcc - High cloud cover 3077: - bli - Best lifted index (to 500 hPa) 3079: - lssf - Large scale snow 3080: - wtmp - Water temperature 3082: - dslm - Deviation of sea-level from mean 3086: - ssw - Soil moisture content 3088: - s - Salinity 3089: - den - Density 3091: - icec - Ice cover (1=ice, 0=no ice) 3092: - icetk - Ice thickness 3093: - diced - Direction of ice drift 3094: - siced - Speed of ice drift 3095: - uice - U-component of ice drift 3096: - vice - V-component of ice drift 3097: - iceg - Ice growth rate 3098: - iced - Ice divergence 3099: - snom - Snowmelt 3100: - swh - Significant height of combined wind waves and swell 3101: - mdww - Mean direction of wind waves 3102: - shww - Significant height of wind waves 3103: - mpww - Mean period of wind waves 3104: - swdir - Direction of swell waves 3105: - swell - Significant height of swell waves 3106: - swper - Mean period of swell waves 3107: - mdps - Primary wave direction 3108: - mpps - Primary wave mean period 3109: - dirsw - Secondary wave direction 3110: - swp - Secondary wave mean period 3111: - nswrs - Net short-wave radiation flux (surface) 3112: - nlwrs - Net long-wave radiation flux (surface) 3113: - nswrt - Net short-wave radiation flux(atmosph.top) 3114: - nlwrt - Net long-wave radiation flux(atmosph.top) 3115: - lwavr - Long wave radiation flux 3116: - swavr - Short wave radiation flux 3117: - grad - Global radiation flux 3119: - lwrad - Radiance (with respect to wave number) 3120: - swrad - Radiance (with respect to wave length) 3121: - lhf - Latent heat flux 3122: - shf - Sensible heat flux 3123: - bld - Boundary layer dissipation 3124: - uflx - Momentum flux, u-component 3125: - vflx - Momentum flux, v-component 3126: - wmixe - Wind mixing energy 3127: - imgd - Image data 129001: - strfgrd - Stream function gradient 129002: - vpotgrd - Velocity potential gradient 129003: - ptgrd - Potential temperature gradient 129004: - eqptgrd - Equivalent potential temperature gradient 129005: - septgrd - Saturated equivalent potential temperature gradient 129011: - udvwgrd - U component of divergent wind gradient 129012: - vdvwgrd - V component of divergent wind gradient 129013: - urtwgrd - U component of rotational wind gradient 129014: - vrtwgrd - V component of rotational wind gradient 129021: - uctpgrd - Unbalanced component of temperature gradient 129022: - uclngrd - Unbalanced component of logarithm of surface pressure gradient 129023: - ucdvgrd - Unbalanced component of divergence gradient 129024: - _param_129024 - Reserved for future unbalanced components 129025: - _param_129025 - Reserved for future unbalanced components 129026: - clgrd - Lake cover gradient 129027: - cvlgrd - Low vegetation cover gradient 129028: - cvhgrd - High vegetation cover gradient 129029: - tvlgrd - Type of low vegetation gradient 129030: - tvhgrd - Type of high vegetation gradient 129031: - sicgrd - Sea-ice cover gradient 129032: - asngrd - Snow albedo gradient 129033: - rsngrd - Snow density gradient 129034: - sstkgrd - Sea surface temperature gradient 129035: - istl1grd - Ice surface temperature layer 1 gradient 129036: - istl2grd - Ice surface temperature layer 2 gradient 129037: - istl3grd - Ice surface temperature layer 3 gradient 129038: - istl4grd - Ice surface temperature layer 4 gradient 129039: - swvl1grd - Volumetric soil water layer 1 gradient 129040: - swvl2grd - Volumetric soil water layer 2 gradient 129041: - swvl3grd - Volumetric soil water layer 3 gradient 129042: - swvl4grd - Volumetric soil water layer 4 gradient 129043: - sltgrd - Soil type gradient 129044: - esgrd - Snow evaporation gradient 129045: - smltgrd - Snowmelt gradient 129046: - sdurgrd - Solar duration gradient 129047: - dsrpgrd - Direct solar radiation gradient 129048: - magssgrd - Magnitude of turbulent surface stress gradient 129049: - 10fggrd - 10 metre wind gust gradient 129050: - lspfgrd - Large-scale precipitation fraction gradient 129051: - mx2t24grd - Maximum 2 metre temperature gradient 129052: - mn2t24grd - Minimum 2 metre temperature gradient 129053: - montgrd - Montgomery potential gradient 129054: - presgrd - Pressure gradient 129055: - mean2t24grd - Mean 2 metre temperature in the last 24 hours gradient 129056: - mn2d24grd - Mean 2 metre dewpoint temperature in the last 24 hours gradient 129057: - uvbgrd - Downward UV radiation at the surface gradient 129058: - pargrd - Photosynthetically active radiation at the surface gradient 129059: - capegrd - Convective available potential energy gradient 129060: - pvgrd - Potential vorticity gradient 129061: - tpogrd - Total precipitation from observations gradient 129062: - obctgrd - Observation count gradient 129063: - _param_129063 - Start time for skin temperature difference 129064: - _param_129064 - Finish time for skin temperature difference 129065: - _param_129065 - Skin temperature difference 129066: - _param_129066 - Leaf area index, low vegetation 129067: - _param_129067 - Leaf area index, high vegetation 129070: - _param_129070 - Biome cover, low vegetation 129071: - _param_129071 - Biome cover, high vegetation 129078: - _param_129078 - Total column liquid water 129079: - _param_129079 - Total column ice water 129080: - _param_129080 - Experimental product 129081: - _param_129081 - Experimental product 129082: - _param_129082 - Experimental product 129083: - _param_129083 - Experimental product 129084: - _param_129084 - Experimental product 129085: - _param_129085 - Experimental product 129086: - _param_129086 - Experimental product 129087: - _param_129087 - Experimental product 129088: - _param_129088 - Experimental product 129089: - _param_129089 - Experimental product 129090: - _param_129090 - Experimental product 129091: - _param_129091 - Experimental product 129092: - _param_129092 - Experimental product 129093: - _param_129093 - Experimental product 129094: - _param_129094 - Experimental product 129095: - _param_129095 - Experimental product 129096: - _param_129096 - Experimental product 129097: - _param_129097 - Experimental product 129098: - _param_129098 - Experimental product 129099: - _param_129099 - Experimental product 129100: - _param_129100 - Experimental product 129101: - _param_129101 - Experimental product 129102: - _param_129102 - Experimental product 129103: - _param_129103 - Experimental product 129104: - _param_129104 - Experimental product 129105: - _param_129105 - Experimental product 129106: - _param_129106 - Experimental product 129107: - _param_129107 - Experimental product 129108: - _param_129108 - Experimental product 129109: - _param_129109 - Experimental product 129110: - _param_129110 - Experimental product 129111: - _param_129111 - Experimental product 129112: - _param_129112 - Experimental product 129113: - _param_129113 - Experimental product 129114: - _param_129114 - Experimental product 129115: - _param_129115 - Experimental product 129116: - _param_129116 - Experimental product 129117: - _param_129117 - Experimental product 129118: - _param_129118 - Experimental product 129119: - _param_129119 - Experimental product 129120: - _param_129120 - Experimental product 129121: - mx2t6grd - Maximum temperature at 2 metres gradient 129122: - mn2t6grd - Minimum temperature at 2 metres gradient 129123: - 10fg6grd - 10 metre wind gust in the last 6 hours gradient 129125: - _param_129125 - Vertically integrated total energy 129126: - _param_129126 - Generic parameter for sensitive area prediction 129127: - atgrd - Atmospheric tide gradient 129128: - bvgrd - Budget values gradient 129129: - zgrd - Geopotential gradient 129130: - tgrd - Temperature gradient 129131: - ugrd - U component of wind gradient 129132: - vgrd - V component of wind gradient 129133: - qgrd - Specific humidity gradient 129134: - spgrd - Surface pressure gradient 129135: - wgrd - vertical velocity (pressure) gradient 129136: - tcwgrd - Total column water gradient 129137: - tcwvgrd - Total column water vapour gradient 129138: - vogrd - Vorticity (relative) gradient 129139: - stl1grd - Soil temperature level 1 gradient 129140: - swl1grd - Soil wetness level 1 gradient 129141: - sdgrd - Snow depth gradient 129142: - lspgrd - Stratiform precipitation (Large-scale precipitation) gradient 129143: - cpgrd - Convective precipitation gradient 129144: - sfgrd - Snowfall (convective + stratiform) gradient 129145: - bldgrd - Boundary layer dissipation gradient 129146: - sshfgrd - Surface sensible heat flux gradient 129147: - slhfgrd - Surface latent heat flux gradient 129148: - chnkgrd - Charnock gradient 129149: - snrgrd - Surface net radiation gradient 129150: - tnrgrd - Top net radiation gradient 129151: - mslgrd - Mean sea level pressure gradient 129152: - lnspgrd - Logarithm of surface pressure gradient 129153: - swhrgrd - Short-wave heating rate gradient 129154: - lwhrgrd - Long-wave heating rate gradient 129155: - dgrd - Divergence gradient 129156: - ghgrd - Height gradient 129157: - rgrd - Relative humidity gradient 129158: - tspgrd - Tendency of surface pressure gradient 129159: - blhgrd - Boundary layer height gradient 129160: - sdorgrd - Standard deviation of orography gradient 129161: - isorgrd - Anisotropy of sub-gridscale orography gradient 129162: - anorgrd - Angle of sub-gridscale orography gradient 129163: - slorgrd - Slope of sub-gridscale orography gradient 129164: - tccgrd - Total cloud cover gradient 129165: - 10ugrd - 10 metre U wind component gradient 129166: - 10vgrd - 10 metre V wind component gradient 129167: - 2tgrd - 2 metre temperature gradient 129168: - 2dgrd - 2 metre dewpoint temperature gradient 129169: - ssrdgrd - Surface solar radiation downwards gradient 129170: - stl2grd - Soil temperature level 2 gradient 129171: - swl2grd - Soil wetness level 2 gradient 129172: - lsmgrd - Land-sea mask gradient 129173: - srgrd - Surface roughness gradient 129174: - algrd - Albedo gradient 129175: - strdgrd - Surface thermal radiation downwards gradient 129176: - ssrgrd - Surface net solar radiation gradient 129177: - strgrd - Surface net thermal radiation gradient 129178: - tsrgrd - Top net solar radiation gradient 129179: - ttrgrd - Top net thermal radiation gradient 129180: - ewssgrd - East-West surface stress gradient 129181: - nsssgrd - North-South surface stress gradient 129182: - egrd - Evaporation gradient 129183: - stl3grd - Soil temperature level 3 gradient 129184: - swl3grd - Soil wetness level 3 gradient 129185: - cccgrd - Convective cloud cover gradient 129186: - lccgrd - Low cloud cover gradient 129187: - mccgrd - Medium cloud cover gradient 129188: - hccgrd - High cloud cover gradient 129189: - sundgrd - Sunshine duration gradient 129190: - ewovgrd - East-West component of sub-gridscale orographic variance gradient 129191: - nsovgrd - North-South component of sub-gridscale orographic variance gradient 129192: - nwovgrd - North-West/South-East component of sub-gridscale orographic variance gradient 129193: - neovgrd - North-East/South-West component of sub-gridscale orographic variance gradient 129194: - btmpgrd - Brightness temperature gradient 129195: - lgwsgrd - Longitudinal component of gravity wave stress gradient 129196: - mgwsgrd - Meridional component of gravity wave stress gradient 129197: - gwdgrd - Gravity wave dissipation gradient 129198: - srcgrd - Skin reservoir content gradient 129199: - veggrd - Vegetation fraction gradient 129200: - vsogrd - Variance of sub-gridscale orography gradient 129201: - mx2tgrd - Maximum temperature at 2 metres since previous post-processing gradient 129202: - mn2tgrd - Minimum temperature at 2 metres since previous post-processing gradient 129203: - o3grd - Ozone mass mixing ratio gradient 129204: - pawgrd - Precipitation analysis weights gradient 129205: - rogrd - Runoff gradient 129206: - tco3grd - Total column ozone gradient 129207: - 10sigrd - 10 metre wind speed gradient 129208: - tsrcgrd - Top net solar radiation, clear sky gradient 129209: - ttrcgrd - Top net thermal radiation, clear sky gradient 129210: - ssrcgrd - Surface net solar radiation, clear sky gradient 129211: - strcgrd - Surface net thermal radiation, clear sky gradient 129212: - tisrgrd - TOA incident solar radiation gradient 129214: - dhrgrd - Diabatic heating by radiation gradient 129215: - dhvdgrd - Diabatic heating by vertical diffusion gradient 129216: - dhccgrd - Diabatic heating by cumulus convection gradient 129217: - dhlcgrd - Diabatic heating large-scale condensation gradient 129218: - vdzwgrd - Vertical diffusion of zonal wind gradient 129219: - vdmwgrd - Vertical diffusion of meridional wind gradient 129220: - ewgdgrd - East-West gravity wave drag tendency gradient 129221: - nsgdgrd - North-South gravity wave drag tendency gradient 129222: - ctzwgrd - Convective tendency of zonal wind gradient 129223: - ctmwgrd - Convective tendency of meridional wind gradient 129224: - vdhgrd - Vertical diffusion of humidity gradient 129225: - htccgrd - Humidity tendency by cumulus convection gradient 129226: - htlcgrd - Humidity tendency by large-scale condensation gradient 129227: - crnhgrd - Change from removal of negative humidity gradient 129228: - tpgrd - Total precipitation gradient 129229: - iewsgrd - Instantaneous X surface stress gradient 129230: - inssgrd - Instantaneous Y surface stress gradient 129231: - ishfgrd - Instantaneous surface heat flux gradient 129232: - iegrd - Instantaneous moisture flux gradient 129233: - asqgrd - Apparent surface humidity gradient 129234: - lsrhgrd - Logarithm of surface roughness length for heat gradient 129235: - sktgrd - Skin temperature gradient 129236: - stl4grd - Soil temperature level 4 gradient 129237: - swl4grd - Soil wetness level 4 gradient 129238: - tsngrd - Temperature of snow layer gradient 129239: - csfgrd - Convective snowfall gradient 129240: - lsfgrd - Large scale snowfall gradient 129241: - acfgrd - Accumulated cloud fraction tendency gradient 129242: - alwgrd - Accumulated liquid water tendency gradient 129243: - falgrd - Forecast albedo gradient 129244: - fsrgrd - Forecast surface roughness gradient 129245: - flsrgrd - Forecast logarithm of surface roughness for heat gradient 129246: - clwcgrd - Specific cloud liquid water content gradient 129247: - ciwcgrd - Specific cloud ice water content gradient 129248: - ccgrd - Cloud cover gradient 129249: - aiwgrd - Accumulated ice water tendency gradient 129250: - icegrd - Ice age gradient 129251: - attegrd - Adiabatic tendency of temperature gradient 129252: - athegrd - Adiabatic tendency of humidity gradient 129253: - atzegrd - Adiabatic tendency of zonal wind gradient 129254: - atmwgrd - Adiabatic tendency of meridional wind gradient 129255: - _param_129255 - Indicates a missing value 130208: - tsru - Top solar radiation upward 130209: - ttru - Top thermal radiation upward 130210: - tsuc - Top solar radiation upward, clear sky 130211: - ttuc - Top thermal radiation upward, clear sky 130212: - clw - Cloud liquid water 130213: - cf - Cloud fraction 130214: - dhr - Diabatic heating by radiation 130215: - dhvd - Diabatic heating by vertical diffusion 130216: - dhcc - Diabatic heating by cumulus convection 130217: - dhlc - Diabatic heating by large-scale condensation 130218: - vdzw - Vertical diffusion of zonal wind 130219: - vdmw - Vertical diffusion of meridional wind 130220: - ewgd - East-West gravity wave drag 130221: - nsgd - North-South gravity wave drag 130224: - vdh - Vertical diffusion of humidity 130225: - htcc - Humidity tendency by cumulus convection 130226: - htlc - Humidity tendency by large-scale condensation 130228: - att - Adiabatic tendency of temperature 130229: - ath - Adiabatic tendency of humidity 130230: - atzw - Adiabatic tendency of zonal wind 130231: - atmwax - Adiabatic tendency of meridional wind 130232: - mvv - Mean vertical velocity 131001: - 2tag2 - 2m temperature anomaly of at least +2K 131002: - 2tag1 - 2m temperature anomaly of at least +1K 131003: - 2tag0 - 2m temperature anomaly of at least 0K 131004: - 2talm1 - 2m temperature anomaly of at most -1K 131005: - 2talm2 - 2m temperature anomaly of at most -2K 131006: - tpag20 - Total precipitation anomaly of at least 20 mm 131007: - tpag10 - Total precipitation anomaly of at least 10 mm 131008: - tpag0 - Total precipitation anomaly of at least 0 mm 131009: - stag0 - Surface temperature anomaly of at least 0K 131010: - mslag0 - Mean sea level pressure anomaly of at least 0 Pa 131015: - h0dip - Height of 0 degree isotherm probability 131016: - hslp - Height of snowfall limit probability 131017: - saip - Showalter index probability 131018: - whip - Whiting index probability 131020: - talm2 - Temperature anomaly less than -2 K - temperature anomaly of at most -2 k 131021: - tag2 - Temperature anomaly of at least +2 K - temperature anomaly of at least 2 k 131022: - talm8 - Temperature anomaly less than -8 K - temperature anomaly of at most -8 k 131023: - talm4 - Temperature anomaly less than -4 K - temperature anomaly of at most -4 k 131024: - tag4 - Temperature anomaly greater than +4 K - temperature anomaly greater than 4 k - temperature anomaly of at least 4 k 131025: - tag8 - Temperature anomaly greater than +8 K - temperature anomaly greater than 8 k - temperature anomaly of at least 8 k 131049: - 10gp - 10 metre wind gust probability 131059: - capep - Convective available potential energy probability 131060: - tpg1 - Total precipitation of at least 1 mm 131061: - tpg5 - Total precipitation of at least 5 mm 131062: - tpg10 - Total precipitation of at least 10 mm 131063: - tpg20 - Total precipitation of at least 20 mm 131064: - tpl01 - Total precipitation less than 0.1 mm 131065: - tprl1 - Total precipitation rate less than 1 mm/day 131066: - tprg3 - Total precipitation rate of at least 3 mm/day 131067: - tprg5 - Total precipitation rate of at least 5 mm/day 131068: - 10spg10 - 10 metre Wind speed of at least 10 m/s 131069: - 10spg15 - 10 metre Wind speed of at least 15 m/s 131070: - 10fgg15 - 10 metre wind gust of at least 15 m/s 131071: - 10fgg20 - 10 metre wind gust of at least 20 m/s 131072: - 10fgg25 - 10 metre wind gust of at least 25 m/s 131073: - 2tl273 - 2 metre temperature less than 273.15 K 131074: - swhg2 - Significant wave height of at least 2 m 131075: - swhg4 - Significant wave height of at least 4 m 131076: - swhg6 - Significant wave height of at least 6 m 131077: - swhg8 - Significant wave height of at least 8 m 131078: - mwpg8 - Mean wave period of at least 8 s 131079: - mwpg10 - Mean wave period of at least 10 s 131080: - mwpg12 - Mean wave period of at least 12 s 131081: - mwpg15 - Mean wave period of at least 15 s 131082: - tpg40 - Total precipitation of at least 40 mm 131083: - tpg60 - Total precipitation of at least 60 mm 131084: - tpg80 - Total precipitation of at least 80 mm 131085: - tpg100 - Total precipitation of at least 100 mm 131086: - tpg150 - Total precipitation of at least 150 mm 131087: - tpg200 - Total precipitation of at least 200 mm 131088: - tpg300 - Total precipitation of at least 300 mm 131089: - pts - Probability of a tropical storm 131090: - ph - Probability of a hurricane 131091: - ptd - Probability of a tropical depression 131092: - cpts - Climatological probability of a tropical storm 131093: - cph - Climatological probability of a hurricane 131094: - cptd - Climatological probability of a tropical depression 131095: - pats - Probability anomaly of a tropical storm 131096: - pah - Probability anomaly of a hurricane 131097: - patd - Probability anomaly of a tropical depression 131098: - tpg25 - Total precipitation of at least 25 mm 131099: - tpg50 - Total precipitation of at least 50 mm 131100: - 10fgg10 - 10 metre wind gust of at least 10 m/s 131129: - zp - Geopotential probability 131130: - tap - Temperature anomaly probability 131139: - stl1p - Soil temperature level 1 probability 131144: - sfp - Snowfall (convective + stratiform) probability 131151: - mslpp - Mean sea level pressure probability 131164: - tccp - Total cloud cover probability 131165: - 10sp - 10 metre speed probability 131167: - 2tp - 2 metre temperature probability 131201: - mx2tp - Maximum 2 metre temperature probability 131202: - mn2tp - Minimum 2 metre temperature probability 131228: - tpp - Total precipitation probability 131229: - swhp - Significant wave height probability 131232: - mwpp - Mean wave period probability 131255: - _param_131255 - Indicates a missing value 131256: - 10cogug25 - 10 metre convective gust of at least 25 m/s 132044: - capesi - Convective available potential energy shear index 132045: - wvfi - Water vapour flux index 132049: - 10fgi - 10 metre wind gust index - 10gi 132059: - capei - Convective available potential energy index 132144: - sfi - Snowfall index 132165: - 10wsi - 10 metre speed index 132167: - 2ti - 2 metre temperature index 132201: - mx2ti - Maximum temperature at 2 metres index 132202: - mn2ti - Minimum temperature at 2 metres index 132216: - maxswhi - Maximum of significant wave height index 132228: - tpi - Total precipitation index 133001: - 2tplm10 - 2m temperature probability less than -10 C 133002: - 2tplm5 - 2m temperature probability less than -5 C 133003: - 2tpl0 - 2m temperature probability less than 0 C 133004: - 2tpl5 - 2m temperature probability less than 5 C 133005: - 2tpl10 - 2m temperature probability less than 10 C 133006: - 2tpg25 - 2m temperature probability greater than 25 C 133007: - 2tpg30 - 2m temperature probability greater than 30 C 133008: - 2tpg35 - 2m temperature probability greater than 35 C 133009: - 2tpg40 - 2m temperature probability greater than 40 C 133010: - 2tpg45 - 2m temperature probability greater than 45 C 133011: - mn2tplm10 - Minimum 2 metre temperature probability less than -10 C 133012: - mn2tplm5 - Minimum 2 metre temperature probability less than -5 C 133013: - mn2tpl0 - Minimum 2 metre temperature probability less than 0 C 133014: - mn2tpl5 - Minimum 2 metre temperature probability less than 5 C 133015: - mn2tpl10 - Minimum 2 metre temperature probability less than 10 C 133016: - mx2tpg25 - Maximum 2 metre temperature probability greater than 25 C 133017: - mx2tpg30 - Maximum 2 metre temperature probability greater than 30 C 133018: - mx2tpg35 - Maximum 2 metre temperature probability greater than 35 C 133019: - mx2tpg40 - Maximum 2 metre temperature probability greater than 40 C 133020: - mx2tpg45 - Maximum 2 metre temperature probability greater than 45 C 133021: - 10spg10 - 10 metre wind speed probability of at least 10 m/s 133022: - 10spg15 - 10 metre wind speed probability of at least 15 m/s 133023: - 10spg20 - 10 metre wind speed probability of at least 20 m/s 133024: - 10spg35 - 10 metre wind speed probability of at least 35 m/s 133025: - 10spg50 - 10 metre wind speed probability of at least 50 m/s 133026: - 10gpg20 - 10 metre wind gust probability of at least 20 m/s 133027: - 10gpg35 - 10 metre wind gust probability of at least 35 m/s 133028: - 10gpg50 - 10 metre wind gust probability of at least 50 m/s 133029: - 10gpg75 - 10 metre wind gust probability of at least 75 m/s 133030: - 10gpg100 - 10 metre wind gust probability of at least 100 m/s 133031: - tppg1 - Total precipitation probability of at least 1 mm 133032: - tppg5 - Total precipitation probability of at least 5 mm 133033: - tppg10 - Total precipitation probability of at least 10 mm 133034: - tppg20 - Total precipitation probability of at least 20 mm 133035: - tppg40 - Total precipitation probability of at least 40 mm 133036: - tppg60 - Total precipitation probability of at least 60 mm 133037: - tppg80 - Total precipitation probability of at least 80 mm 133038: - tppg100 - Total precipitation probability of at least 100 mm 133039: - tppg150 - Total precipitation probability of at least 150 mm 133040: - tppg200 - Total precipitation probability of at least 200 mm 133041: - tppg300 - Total precipitation probability of at least 300 mm 133042: - sfpg1 - Snowfall probability of at least 1 mm 133043: - sfpg5 - Snowfall probability of at least 5 mm 133044: - sfpg10 - Snowfall probability of at least 10 mm 133045: - sfpg20 - Snowfall probability of at least 20 mm 133046: - sfpg40 - Snowfall probability of at least 40 mm 133047: - sfpg60 - Snowfall probability of at least 60 mm 133048: - sfpg80 - Snowfall probability of at least 80 mm 133049: - sfpg100 - Snowfall probability of at least 100 mm 133050: - sfpg150 - Snowfall probability of at least 150 mm 133051: - sfpg200 - Snowfall probability of at least 200 mm 133052: - sfpg300 - Snowfall probability of at least 300 mm 133053: - tccpg10 - Total Cloud Cover probability greater than 10% 133054: - tccpg20 - Total Cloud Cover probability greater than 20% 133055: - tccpg30 - Total Cloud Cover probability greater than 30% 133056: - tccpg40 - Total Cloud Cover probability greater than 40% 133057: - tccpg50 - Total Cloud Cover probability greater than 50% 133058: - tccpg60 - Total Cloud Cover probability greater than 60% 133059: - tccpg70 - Total Cloud Cover probability greater than 70% 133060: - tccpg80 - Total Cloud Cover probability greater than 80% 133061: - tccpg90 - Total Cloud Cover probability greater than 90% 133062: - tccpg99 - Total Cloud Cover probability greater than 99% 133063: - hccpg10 - High Cloud Cover probability greater than 10% 133064: - hccpg20 - High Cloud Cover probability greater than 20% 133065: - hccpg30 - High Cloud Cover probability greater than 30% 133066: - hccpg40 - High Cloud Cover probability greater than 40% 133067: - hccpg50 - High Cloud Cover probability greater than 50% 133068: - hccpg60 - High Cloud Cover probability greater than 60% 133069: - hccpg70 - High Cloud Cover probability greater than 70% 133070: - hccpg80 - High Cloud Cover probability greater than 80% 133071: - hccpg90 - High Cloud Cover probability greater than 90% 133072: - hccpg99 - High Cloud Cover probability greater than 99% 133073: - mccpg10 - Medium Cloud Cover probability greater than 10% 133074: - mccpg20 - Medium Cloud Cover probability greater than 20% 133075: - mccpg30 - Medium Cloud Cover probability greater than 30% 133076: - mccpg40 - Medium Cloud Cover probability greater than 40% 133077: - mccpg50 - Medium Cloud Cover probability greater than 50% 133078: - mccpg60 - Medium Cloud Cover probability greater than 60% 133079: - mccpg70 - Medium Cloud Cover probability greater than 70% 133080: - mccpg80 - Medium Cloud Cover probability greater than 80% 133081: - mccpg90 - Medium Cloud Cover probability greater than 90% 133082: - mccpg99 - Medium Cloud Cover probability greater than 99% 133083: - lccpg10 - Low Cloud Cover probability greater than 10% 133084: - lccpg20 - Low Cloud Cover probability greater than 20% 133085: - lccpg30 - Low Cloud Cover probability greater than 30% 133086: - lccpg40 - Low Cloud Cover probability greater than 40% 133087: - lccpg50 - Low Cloud Cover probability greater than 50% 133088: - lccpg60 - Low Cloud Cover probability greater than 60% 133089: - lccpg70 - Low Cloud Cover probability greater than 70% 133090: - lccpg80 - Low Cloud Cover probability greater than 80% 133091: - lccpg90 - Low Cloud Cover probability greater than 90% 133092: - lccpg99 - Low Cloud Cover probability greater than 99% 133093: - ptsa_gt_1stdev - Probability of temperature standardized anomaly greater than 1 standard deviation 133094: - ptsa_gt_1p5stdev - Probability of temperature standardized anomaly greater than 1.5 standard deviation 133095: - ptsa_gt_2stdev - Probability of temperature standardized anomaly greater than 2 standard deviation 133096: - ptsa_lt_1stdev - Probability of temperature standardized anomaly less than -1 standard deviation 133097: - ptsa_lt_1p5stdev - Probability of temperature standardized anomaly less than -1.5 standard deviation 133098: - ptsa_lt_2stdev - Probability of temperature standardized anomaly less than -2 standard deviation 140080: - wx1 - Wave experimental parameter 1 140081: - wx2 - Wave experimental parameter 2 140082: - wx3 - Wave experimental parameter 3 140083: - wx4 - Wave experimental parameter 4 140084: - wx5 - Wave experimental parameter 5 140098: - weta - Wave induced mean sea level correction 140099: - wraf - Ratio of wave angular and frequency width 140100: - wnslc - Number of events in freak waves statistics 140101: - utaua - U-component of atmospheric surface momentum flux 140102: - vtaua - V-component of atmospheric surface momentum flux 140103: - utauo - U-component of surface momentum flux into ocean 140104: - vtauo - V-component of surface momentum flux into ocean 140105: - wphio - Wave turbulent energy flux into ocean 140106: - wdw1 - Wave directional width of first swell partition 140107: - wfw1 - Wave frequency width of first swell partition 140108: - wdw2 - Wave directional width of second swell partition 140109: - wfw2 - Wave frequency width of second swell partition 140110: - wdw3 - Wave directional width of third swell partition 140111: - wfw3 - Wave frequency width of third swell partition 140112: - wefxm - Wave energy flux magnitude 140113: - wefxd - Wave energy flux mean direction 140114: - h1012 - Significant wave height of all waves with periods within the inclusive range from 10 to 12 seconds 140115: - h1214 - Significant wave height of all waves with periods within the inclusive range from 12 to 14 seconds 140116: - h1417 - Significant wave height of all waves with periods within the inclusive range from 14 to 17 seconds 140117: - h1721 - Significant wave height of all waves with periods within the inclusive range from 17 to 21 seconds 140118: - h2125 - Significant wave height of all waves with periods within the inclusive range from 21 to 25 seconds 140119: - h2530 - Significant wave height of all waves with periods within the inclusive range from 25 to 30 seconds 140120: - sh10 - Significant wave height of all waves with period larger than 10s 140121: - swh1 - Significant wave height of first swell partition 140122: - mwd1 - Mean wave direction of first swell partition 140123: - mwp1 - Mean wave period of first swell partition 140124: - swh2 - Significant wave height of second swell partition 140125: - mwd2 - Mean wave direction of second swell partition 140126: - mwp2 - Mean wave period of second swell partition 140127: - swh3 - Significant wave height of third swell partition 140128: - mwd3 - Mean wave direction of third swell partition 140129: - mwp3 - Mean wave period of third swell partition 140131: - tdcmax - Time domain maximum individual crest height 140132: - tdhmax - Time domain maximum individual wave height 140133: - stcmax - Space time maximum individual crest height 140134: - sthmax - Space time maximum individual wave height 140135: - pp1dw - Peak wave period of wind waves 140136: - pp1ds - Peak wave period of total swell 140137: - pp1d1 - Peak wave period of first swell partition 140138: - pp1d2 - Peak wave period of second swell partition 140139: - pp1d3 - Peak wave period of third swell partition 140140: - pwd - Peak wave direction (total) 140141: - pwdw - Peak wave direction of wind waves 140142: - pwds - Peak wave direction of total swell 140143: - pwd1 - Peak wave direction of first swell partition 140144: - pwd2 - Peak wave direction of second swell partition 140145: - pwd3 - Peak wave direction of third swell partition 140146: - wcfr - Whitecap fraction 140147: - bfi2d - Benjamin-Feir index 2D 140148: - ctc - Crest-trough correlation 140149: - xwrs - X component of the wave radiative stress to sea ice 140150: - ywrs - Y component of the wave radiative stress to sea ice 140200: - maxswh - Maximum of significant wave height 140207: - wss - Wave Spectral Skewness 140208: - wstar - Free convective velocity over the oceans 140209: - rhoao - Air density over the oceans 140210: - mswsi - Mean square wave strain in sea ice 140211: - phiaw - Normalized energy flux into waves 140212: - phioc - Normalized energy flux into ocean 140213: - tla - Turbulent Langmuir number 140214: - tauoc - Normalized stress into ocean 140215: - ust - U-component surface stokes drift 140216: - vst - V-component surface stokes drift 140217: - tmax - Period corresponding to maximum individual wave height 140218: - hmax - Envelop-maximum individual wave height 140219: - wmb - Model bathymetry 140220: - mp1 - Mean wave period based on first moment 140221: - mp2 - Mean zero-crossing wave period 140222: - wdw - Wave spectral directional width 140223: - p1ww - Mean wave period based on first moment for wind waves 140224: - p2ww - Mean wave period based on second moment for wind waves 140225: - dwww - Wave spectral directional width for wind waves 140226: - p1ps - Mean wave period based on first moment for swell 140227: - p2ps - Mean wave period based on second moment for swell 140228: - dwps - Wave spectral directional width for swell 140229: - swh - Significant height of combined wind waves and swell 140230: - mwd - Mean wave direction 140231: - pp1d - Peak wave period 140232: - mwp - Mean wave period 140233: - cdww - Coefficient of drag with waves 140234: - shww - Significant height of wind waves 140235: - mdww - Mean direction of wind waves 140236: - mpww - Mean period of wind waves 140237: - shts - Significant height of total swell 140238: - mdts - Mean direction of total swell 140239: - mpts - Mean period of total swell 140240: - sdhs - Standard deviation wave height 140241: - mu10 - Mean of 10 metre wind speed 140242: - mdwi - Mean wind direction 140243: - sdu - Standard deviation of 10 metre wind speed 140244: - msqs - Mean square slope of waves 140245: - wind - 10 metre wind speed 140246: - awh - Altimeter wave height 140247: - acwh - Altimeter corrected wave height 140248: - arrc - Altimeter range relative correction 140249: - dwi - 10 metre wind direction 140250: - 2dsp - 2D wave spectra (multiple) 140251: - 2dfd - 2D wave spectra (single) 140252: - wsk - Wave spectral kurtosis 140253: - bfi - Benjamin-Feir index 140254: - wsp - Wave spectral peakedness 140255: - _param_140255 - Indicates a missing value 141098: - avg_weta - Time-mean wave induced mean sea level correction 141099: - avg_wraf - Time-mean ratio of wave angular and frequency width 141100: - avg_wnslc - Time-mean number of events in freak waves statistics 141101: - avg_utaua - Time-mean U-component of atmospheric surface momentum flux 141102: - avg_vtaua - Time-mean V-component of atmospheric surface momentum flux 141103: - avg_utauo - Time-mean U-component of surface momentum flux into ocean 141104: - avg_vtauo - Time-mean V-component of surface momentum flux into ocean 141105: - avg_wphio - Time-mean wave turbulent energy flux into ocean 141106: - avg_wdw1 - Time-mean wave directional width of first swell partition 141107: - avg_wfw1 - Time-mean wave frequency width of first swell partition 141108: - avg_wdw2 - Time-mean wave directional width of second swell partition 141109: - avg_wfw2 - Time-mean wave frequency width of second swell partition 141110: - avg_wdw3 - Time-mean wave directional width of third swell partition 141111: - avg_wfw3 - Time-mean wave frequency width of third swell partition 141112: - avg_wefxm - Time-mean wave energy flux magnitude 141113: - avg_wefxd - Time-mean wave energy flux mean direction 141114: - avg_h1012 - Time-mean significant wave height of all waves with periods within the inclusive range from 10 to 12 seconds 141115: - avg_h1214 - Time-mean significant wave height of all waves with periods within the inclusive range from 12 to 14 seconds 141116: - avg_h1417 - Time-mean significant wave height of all waves with periods within the inclusive range from 14 to 17 seconds 141117: - avg_h1721 - Time-mean significant wave height of all waves with periods within the inclusive range from 17 to 21 seconds 141118: - avg_h2125 - Time-mean significant wave height of all waves with periods within the inclusive range from 21 to 25 seconds 141119: - avg_h2530 - Time-mean significant wave height of all waves with periods within the inclusive range from 25 to 30 seconds 141120: - avg_sh10 - Time-mean significant wave height of all waves with period larger than 10s 141121: - avg_swh1 - Time-mean significant wave height of first swell partition 141122: - avg_mwd1 - Time-mean mean wave direction of first swell partition 141123: - avg_mwp1 - Time-mean mean wave period of first swell partition 141124: - avg_swh2 - Time-mean significant wave height of second swell partition 141125: - avg_mwd2 - Time-mean mean wave direction of second swell partition 141126: - avg_mwp2 - Time-mean mean wave period of second swell partition 141127: - avg_swh3 - Time-mean significant wave height of third swell partition 141128: - avg_mwd3 - Time-mean mean wave direction of third swell partition 141129: - avg_mwp3 - Time-mean mean wave period of third swell partition 141131: - avg_tdcmax - Time-mean time domain maximum individual crest height 141132: - avg_tdhmax - Time-mean time domain maximum individual wave height 141133: - avg_stcmax - Time-mean space time maximum individual crest height 141134: - avg_sthmax - Time-mean space time maximum individual wave height 141135: - avg_pp1dw - Time-mean peak wave period of wind waves 141136: - avg_pp1ds - Time-mean peak wave period of total swell 141137: - avg_pp1d1 - Time-mean peak wave period of first swell partition 141138: - avg_pp1d2 - Time-mean peak wave period of second swell partition 141139: - avg_pp1d3 - Time-mean peak wave period of third swell partition 141140: - avg_pwd - Time-mean peak wave direction (total) 141141: - avg_pwdw - Time-mean peak wave direction of wind waves 141142: - avg_pwds - Time-mean peak wave direction of total swell 141143: - avg_pwd1 - Time-mean peak wave direction of first swell partition 141144: - avg_pwd2 - Time-mean peak wave direction of second swell partition 141145: - avg_pwd3 - Time-mean peak wave direction of third swell partition 141146: - avg_wcfr - Time-mean whitecap fraction 141147: - avg_bfi2d - Time-mean Benjamin-Feir index 2D 141148: - avg_ctc - Time-mean crest-trough correlation 141149: - avg_xwrs - Time-mean X component of the wave radiative stress to sea ice 141150: - avg_ywrs - Time-mean Y component of the wave radiative stress to sea ice 141207: - avg_wss - Time-mean wave Spectral Skewness 141208: - avg_wstar - Time-mean free convective velocity over the oceans 141209: - avg_rhoao - Time-mean air density over the oceans 141211: - avg_phiaw - Time-mean normalized energy flux into waves 141212: - avg_phioc - Time-mean normalized energy flux into ocean 141214: - avg_tauoc - Time-mean normalized stress into ocean 141215: - avg_ust - Time-mean U-component surface stokes drift 141216: - avg_vst - Time-mean V-component surface stokes drift 141217: - avg_tmax - Time-mean period corresponding to maximum individual wave height 141218: - avg_hmax - Time-mean envelop-maximum individual wave height 141220: - avg_mp1 - Time-mean mean wave period based on first moment 141221: - avg_mp2 - Time-mean mean zero-crossing wave period 141222: - avg_wdw - Time-mean wave spectral directional width 141223: - avg_p1ww - Time-mean mean wave period based on first moment for wind waves 141224: - avg_p2ww - Time-mean mean wave period based on second moment for wind waves 141225: - avg_dwww - Time-mean wave spectral directional width for wind waves 141226: - avg_p1ps - Time-mean mean wave period based on first moment for swell 141227: - avg_p2ps - Time-mean mean wave period based on second moment for swell 141228: - avg_dwps - Time-mean wave spectral directional width for swell 141229: - avg_swh - Time-mean significant height of combined wind waves and swell 141230: - avg_mwd - Time-mean mean wave direction 141231: - avg_pp1d - Time-mean peak wave period 141232: - avg_mwp - Time-mean mean wave period 141233: - avg_cdww - Time-mean coefficient of drag with waves 141234: - avg_shww - Time-mean significant height of wind waves 141235: - avg_mdww - Time-mean mean direction of wind waves 141236: - avg_mpww - Time-mean mean period of wind waves 141237: - avg_shts - Time-mean significant height of total swell 141238: - avg_mdts - Time-mean mean direction of total swell 141239: - avg_mpts - Time-mean mean period of total swell 141244: - avg_msqs - Time-mean mean square slope of waves 141245: - avg_wind - Time-mean 10 metre wind speed 141249: - avg_dwi - Time-mean 10 metre wind direction 141252: - avg_wsk - Time-mean wave spectral kurtosis 141253: - avg_bfi - Time-mean benjamin-Feir index 141254: - avg_wsp - Time-mean wave spectral peakedness 143098: - max_weta - Time-maximum wave induced mean sea level correction 143099: - max_wraf - Time-maximum ratio of wave angular and frequency width 143100: - max_wnslc - Time-maximum number of events in freak waves statistics 143101: - max_utaua - Time-maximum U-component of atmospheric surface momentum flux 143102: - max_vtaua - Time-maximum V-component of atmospheric surface momentum flux 143103: - max_utauo - Time-maximum U-component of surface momentum flux into ocean 143104: - max_vtauo - Time-maximum V-component of surface momentum flux into ocean 143105: - max_wphio - Time-maximum wave turbulent energy flux into ocean 143106: - max_wdw1 - Time-maximum wave directional width of first swell partition 143107: - max_wfw1 - Time-maximum wave frequency width of first swell partition 143108: - max_wdw2 - Time-maximum wave directional width of second swell partition 143109: - max_wfw2 - Time-maximum wave frequency width of second swell partition 143110: - max_wdw3 - Time-maximum wave directional width of third swell partition 143111: - max_wfw3 - Time-maximum wave frequency width of third swell partition 143112: - max_wefxm - Time-maximum wave energy flux magnitude 143113: - max_wefxd - Time-maximum wave energy flux mean direction 143114: - max_h1012 - Time-maximum significant wave height of all waves with periods within the inclusive range from 10 to 12 seconds 143115: - max_h1214 - Time-maximum significant wave height of all waves with periods within the inclusive range from 12 to 14 seconds 143116: - max_h1417 - Time-maximum significant wave height of all waves with periods within the inclusive range from 14 to 17 seconds 143117: - max_h1721 - Time-maximum significant wave height of all waves with periods within the inclusive range from 17 to 21 seconds 143118: - max_h2125 - Time-maximum significant wave height of all waves with periods within the inclusive range from 21 to 25 seconds 143119: - max_h2530 - Time-maximum significant wave height of all waves with periods within the inclusive range from 25 to 30 seconds 143120: - max_sh10 - Time-maximum significant wave height of all waves with period larger than 10s 143121: - max_swh1 - Time-maximum significant wave height of first swell partition 143122: - max_mwd1 - Time-maximum mean wave direction of first swell partition 143123: - max_mwp1 - Time-maximum mean wave period of first swell partition 143124: - max_swh2 - Time-maximum significant wave height of second swell partition 143125: - max_mwd2 - Time-maximum mean wave direction of second swell partition 143126: - max_mwp2 - Time-maximum mean wave period of second swell partition 143127: - max_swh3 - Time-maximum significant wave height of third swell partition 143128: - max_mwd3 - Time-maximum mean wave direction of third swell partition 143129: - max_mwp3 - Time-maximum mean wave period of third swell partition 143131: - max_tdcmax - Time-maximum time domain maximum individual crest height 143132: - max_tdhmax - Time-maximum time domain maximum individual wave height 143133: - max_stcmax - Time-maximum space time maximum individual crest height 143134: - max_sthmax - Time-maximum space time maximum individual wave height 143135: - max_pp1dw - Time-maximum peak wave period of wind waves 143136: - max_pp1ds - Time-maximum peak wave period of total swell 143137: - max_pp1d1 - Time-maximum peak wave period of first swell partition 143138: - max_pp1d2 - Time-maximum peak wave period of second swell partition 143139: - max_pp1d3 - Time-maximum peak wave period of third swell partition 143140: - max_pwd - Time-maximum peak wave direction (total) 143141: - max_pwdw - Time-maximum peak wave direction of wind waves 143142: - max_pwds - Time-maximum peak wave direction of total swell 143143: - max_pwd1 - Time-maximum peak wave direction of first swell partition 143144: - max_pwd2 - Time-maximum peak wave direction of second swell partition 143145: - max_pwd3 - Time-maximum peak wave direction of third swell partition 143146: - max_wcfr - Time-maximum whitecap fraction 143147: - max_bfi2d - Time-maximum Benjamin-Feir index 2D 143148: - max_ctc - Time-maximum crest-trough correlation 143149: - max_xwrs - Time-maximum X component of the wave radiative stress to sea ice 143150: - max_ywrs - Time-maximum Y component of the wave radiative stress to sea ice 143207: - max_wss - Time-maximum wave Spectral Skewness 143208: - max_wstar - Time-maximum free convective velocity over the oceans 143209: - max_rhoao - Time-maximum air density over the oceans 143211: - max_phiaw - Time-maximum normalized energy flux into waves 143212: - max_phioc - Time-maximum normalized energy flux into ocean 143214: - max_tauoc - Time-maximum normalized stress into ocean 143215: - max_ust - Time-maximum U-component surface stokes drift 143216: - max_vst - Time-maximum V-component surface stokes drift 143217: - max_tmax - Time-maximum period corresponding to maximum individual wave height 143218: - max_hmax - Time-maximum envelop-maximum individual wave height 143220: - max_mp1 - Time-maximum mean wave period based on first moment 143221: - max_mp2 - Time-maximum mean zero-crossing wave period 143222: - max_wdw - Time-maximum wave spectral directional width 143223: - max_p1ww - Time-maximum mean wave period based on first moment for wind waves 143224: - max_p2ww - Time-maximum mean wave period based on second moment for wind waves 143225: - max_dwww - Time-maximum wave spectral directional width for wind waves 143226: - max_p1ps - Time-maximum mean wave period based on first moment for swell 143227: - max_p2ps - Time-maximum mean wave period based on second moment for swell 143228: - max_dwps - Time-maximum wave spectral directional width for swell 143229: - max_swh - Time-maximum significant height of combined wind waves and swell 143230: - max_mwd - Time-maximum mean wave direction 143231: - max_pp1d - Time-maximum peak wave period 143232: - max_mwp - Time-maximum mean wave period 143233: - max_cdww - Time-maximum coefficient of drag with waves 143234: - max_shww - Time-maximum significant height of wind waves 143235: - max_mdww - Time-maximum mean direction of wind waves 143236: - max_mpww - Time-maximum mean period of wind waves 143237: - max_shts - Time-maximum significant height of total swell 143238: - max_mdts - Time-maximum mean direction of total swell 143239: - max_mpts - Time-maximum mean period of total swell 143244: - max_msqs - Time-maximum mean square slope of waves 143245: - max_wind - Time-maximum 10 metre wind speed 143249: - max_dwi - Time-maximum 10 metre wind direction 143252: - max_wsk - Time-maximum wave spectral kurtosis 143253: - max_bfi - Time-maximum benjamin-Feir index 143254: - max_wsp - Time-maximum wave spectral peakedness 144098: - min_weta - Time-minimum wave induced mean sea level correction 144099: - min_wraf - Time-minimum ratio of wave angular and frequency width 144100: - min_wnslc - Time-minimum number of events in freak waves statistics 144101: - min_utaua - Time-minimum U-component of atmospheric surface momentum flux 144102: - min_vtaua - Time-minimum V-component of atmospheric surface momentum flux 144103: - min_utauo - Time-minimum U-component of surface momentum flux into ocean 144104: - min_vtauo - Time-minimum V-component of surface momentum flux into ocean 144105: - min_wphio - Time-minimum wave turbulent energy flux into ocean 144106: - min_wdw1 - Time-minimum wave directional width of first swell partition 144107: - min_wfw1 - Time-minimum wave frequency width of first swell partition 144108: - min_wdw2 - Time-minimum wave directional width of second swell partition 144109: - min_wfw2 - Time-minimum wave frequency width of second swell partition 144110: - min_wdw3 - Time-minimum wave directional width of third swell partition 144111: - min_wfw3 - Time-minimum wave frequency width of third swell partition 144112: - min_wefxm - Time-minimum wave energy flux magnitude 144113: - min_wefxd - Time-minimum wave energy flux mean direction 144114: - min_h1012 - Time-minimum significant wave height of all waves with periods within the inclusive range from 10 to 12 seconds 144115: - min_h1214 - Time-minimum significant wave height of all waves with periods within the inclusive range from 12 to 14 seconds 144116: - min_h1417 - Time-minimum significant wave height of all waves with periods within the inclusive range from 14 to 17 seconds 144117: - min_h1721 - Time-minimum significant wave height of all waves with periods within the inclusive range from 17 to 21 seconds 144118: - min_h2125 - Time-minimum significant wave height of all waves with periods within the inclusive range from 21 to 25 seconds 144119: - min_h2530 - Time-minimum significant wave height of all waves with periods within the inclusive range from 25 to 30 seconds 144120: - min_sh10 - Time-minimum significant wave height of all waves with period larger than 10s 144121: - min_swh1 - Time-minimum significant wave height of first swell partition 144122: - min_mwd1 - Time-minimum mean wave direction of first swell partition 144123: - min_mwp1 - Time-minimum mean wave period of first swell partition 144124: - min_swh2 - Time-minimum significant wave height of second swell partition 144125: - min_mwd2 - Time-minimum mean wave direction of second swell partition 144126: - min_mwp2 - Time-minimum mean wave period of second swell partition 144127: - min_swh3 - Time-minimum significant wave height of third swell partition 144128: - min_mwd3 - Time-minimum mean wave direction of third swell partition 144129: - min_mwp3 - Time-minimum mean wave period of third swell partition 144131: - min_tdcmax - Time-minimum time domain maximum individual crest height 144132: - min_tdhmax - Time-minimum time domain maximum individual wave height 144133: - min_stcmax - Time-minimum space time maximum individual crest height 144134: - min_sthmax - Time-minimum space time maximum individual wave height 144135: - min_pp1dw - Time-minimum peak wave period of wind waves 144136: - min_pp1ds - Time-minimum peak wave period of total swell 144137: - min_pp1d1 - Time-minimum peak wave period of first swell partition 144138: - min_pp1d2 - Time-minimum peak wave period of second swell partition 144139: - min_pp1d3 - Time-minimum peak wave period of third swell partition 144140: - min_pwd - Time-minimum peak wave direction (total) 144141: - min_pwdw - Time-minimum peak wave direction of wind waves 144142: - min_pwds - Time-minimum peak wave direction of total swell 144143: - min_pwd1 - Time-minimum peak wave direction of first swell partition 144144: - min_pwd2 - Time-minimum peak wave direction of second swell partition 144145: - min_pwd3 - Time-minimum peak wave direction of third swell partition 144146: - min_wcfr - Time-minimum whitecap fraction 144147: - min_bfi2d - Time-minimum Benjamin-Feir index 2D 144148: - min_ctc - Time-minimum crest-trough correlation 144149: - min_xwrs - Time-minimum X component of the wave radiative stress to sea ice 144150: - min_ywrs - Time-minimum Y component of the wave radiative stress to sea ice 144207: - min_wss - Time-minimum wave Spectral Skewness 144208: - min_wstar - Time-minimum free convective velocity over the oceans 144209: - min_rhoao - Time-minimum air density over the oceans 144211: - min_phiaw - Time-minimum normalized energy flux into waves 144212: - min_phioc - Time-minimum normalized energy flux into ocean 144214: - min_tauoc - Time-minimum normalized stress into ocean 144215: - min_ust - Time-minimum U-component surface stokes drift 144216: - min_vst - Time-minimum V-component surface stokes drift 144217: - min_tmax - Time-minimum period corresponding to maximum individual wave height 144218: - min_hmax - Time-minimum envelop-maximum individual wave height 144220: - min_mp1 - Time-minimum mean wave period based on first moment 144221: - min_mp2 - Time-minimum mean zero-crossing wave period 144222: - min_wdw - Time-minimum wave spectral directional width 144223: - min_p1ww - Time-minimum mean wave period based on first moment for wind waves 144224: - min_p2ww - Time-minimum mean wave period based on second moment for wind waves 144225: - min_dwww - Time-minimum wave spectral directional width for wind waves 144226: - min_p1ps - Time-minimum mean wave period based on first moment for swell 144227: - min_p2ps - Time-minimum mean wave period based on second moment for swell 144228: - min_dwps - Time-minimum wave spectral directional width for swell 144229: - min_swh - Time-minimum significant height of combined wind waves and swell 144230: - min_mwd - Time-minimum mean wave direction 144231: - min_pp1d - Time-minimum peak wave period 144232: - min_mwp - Time-minimum mean wave period 144233: - min_cdww - Time-minimum coefficient of drag with waves 144234: - min_shww - Time-minimum significant height of wind waves 144235: - min_mdww - Time-minimum mean direction of wind waves 144236: - min_mpww - Time-minimum mean period of wind waves 144237: - min_shts - Time-minimum significant height of total swell 144238: - min_mdts - Time-minimum mean direction of total swell 144239: - min_mpts - Time-minimum mean period of total swell 144244: - min_msqs - Time-minimum mean square slope of waves 144245: - min_wind - Time-minimum 10 metre wind speed 144249: - min_dwi - Time-minimum 10 metre wind direction 144252: - min_wsk - Time-minimum wave spectral kurtosis 144253: - min_bfi - Time-minimum benjamin-Feir index 144254: - min_wsp - Time-minimum wave spectral peakedness 145098: - std_weta - Time-standard-deviation wave induced mean sea level correction 145099: - std_wraf - Time-standard-deviation ratio of wave angular and frequency width 145100: - std_wnslc - Time-standard-deviation number of events in freak waves statistics 145101: - std_utaua - Time-standard-deviation U-component of atmospheric surface momentum flux 145102: - std_vtaua - Time-standard-deviation V-component of atmospheric surface momentum flux 145103: - std_utauo - Time-standard-deviation U-component of surface momentum flux into ocean 145104: - std_vtauo - Time-standard-deviation V-component of surface momentum flux into ocean 145105: - std_wphio - Time-standard-deviation wave turbulent energy flux into ocean 145106: - std_wdw1 - Time-standard-deviation wave directional width of first swell partition 145107: - std_wfw1 - Time-standard-deviation wave frequency width of first swell partition 145108: - std_wdw2 - Time-standard-deviation wave directional width of second swell partition 145109: - std_wfw2 - Time-standard-deviation wave frequency width of second swell partition 145110: - std_wdw3 - Time-standard-deviation wave directional width of third swell partition 145111: - std_wfw3 - Time-standard-deviation wave frequency width of third swell partition 145112: - std_wefxm - Time-standard-deviation wave energy flux magnitude 145113: - std_wefxd - Time-standard-deviation wave energy flux mean direction 145114: - std_h1012 - Time-standard-deviation significant wave height of all waves with periods within the inclusive range from 10 to 12 seconds 145115: - std_h1214 - Time-standard-deviation significant wave height of all waves with periods within the inclusive range from 12 to 14 seconds 145116: - std_h1417 - Time-standard-deviation significant wave height of all waves with periods within the inclusive range from 14 to 17 seconds 145117: - std_h1721 - Time-standard-deviation significant wave height of all waves with periods within the inclusive range from 17 to 21 seconds 145118: - std_h2125 - Time-standard-deviation significant wave height of all waves with periods within the inclusive range from 21 to 25 seconds 145119: - std_h2530 - Time-standard-deviation significant wave height of all waves with periods within the inclusive range from 25 to 30 seconds 145120: - std_sh10 - Time-standard-deviation significant wave height of all waves with period larger than 10s 145121: - std_swh1 - Time-standard-deviation significant wave height of first swell partition 145122: - std_mwd1 - Time-standard-deviation mean wave direction of first swell partition 145123: - std_mwp1 - Time-standard-deviation mean wave period of first swell partition 145124: - std_swh2 - Time-standard-deviation significant wave height of second swell partition 145125: - std_mwd2 - Time-standard-deviation mean wave direction of second swell partition 145126: - std_mwp2 - Time-standard-deviation mean wave period of second swell partition 145127: - std_swh3 - Time-standard-deviation significant wave height of third swell partition 145128: - std_mwd3 - Time-standard-deviation mean wave direction of third swell partition 145129: - std_mwp3 - Time-standard-deviation mean wave period of third swell partition 145131: - std_tdcmax - Time-standard-deviation time domain maximum individual crest height 145132: - std_tdhmax - Time-standard-deviation time domain maximum individual wave height 145133: - std_stcmax - Time-standard-deviation space time maximum individual crest height 145134: - std_sthmax - Time-standard-deviation space time maximum individual wave height 145135: - std_pp1dw - Time-standard-deviation peak wave period of wind waves 145136: - std_pp1ds - Time-standard-deviation peak wave period of total swell 145137: - std_pp1d1 - Time-standard-deviation peak wave period of first swell partition 145138: - std_pp1d2 - Time-standard-deviation peak wave period of second swell partition 145139: - std_pp1d3 - Time-standard-deviation peak wave period of third swell partition 145140: - std_pwd - Time-standard-deviation peak wave direction (total) 145141: - std_pwdw - Time-standard-deviation peak wave direction of wind waves 145142: - std_pwds - Time-standard-deviation peak wave direction of total swell 145143: - std_pwd1 - Time-standard-deviation peak wave direction of first swell partition 145144: - std_pwd2 - Time-standard-deviation peak wave direction of second swell partition 145145: - std_pwd3 - Time-standard-deviation peak wave direction of third swell partition 145146: - std_wcfr - Time-standard-deviation whitecap fraction 145147: - std_bfi2d - Time-standard-deviation Benjamin-Feir index 2D 145148: - std_ctc - Time-standard-deviation crest-trough correlation 145149: - std_xwrs - Time-standard-deviation X component of the wave radiative stress to sea ice 145150: - std_ywrs - Time-standard-deviation Y component of the wave radiative stress to sea ice 145207: - std_wss - Time-standard-deviation wave Spectral Skewness 145208: - std_wstar - Time-standard-deviation free convective velocity over the oceans 145209: - std_rhoao - Time-standard-deviation air density over the oceans 145211: - std_phiaw - Time-standard-deviation normalized energy flux into waves 145212: - std_phioc - Time-standard-deviation normalized energy flux into ocean 145214: - std_tauoc - Time-standard-deviation normalized stress into ocean 145215: - std_ust - Time-standard-deviation U-component surface stokes drift 145216: - std_vst - Time-standard-deviation V-component surface stokes drift 145217: - std_tmax - Time-standard-deviation period corresponding to maximum individual wave height 145218: - std_hmax - Time-standard-deviation envelop-maximum individual wave height 145220: - std_mp1 - Time-standard-deviation mean wave period based on first moment 145221: - std_mp2 - Time-standard-deviation mean zero-crossing wave period 145222: - std_wdw - Time-standard-deviation wave spectral directional width 145223: - std_p1ww - Time-standard-deviation mean wave period based on first moment for wind waves 145224: - std_p2ww - Time-standard-deviation mean wave period based on second moment for wind waves 145225: - std_dwww - Time-standard-deviation wave spectral directional width for wind waves 145226: - std_p1ps - Time-standard-deviation mean wave period based on first moment for swell 145227: - std_p2ps - Time-standard-deviation mean wave period based on second moment for swell 145228: - std_dwps - Time-standard-deviation wave spectral directional width for swell 145229: - std_swh - Time-standard-deviation significant height of combined wind waves and swell 145230: - std_mwd - Time-standard-deviation mean wave direction 145231: - std_pp1d - Time-standard-deviation peak wave period 145232: - std_mwp - Time-standard-deviation mean wave period 145233: - std_cdww - Time-standard-deviation coefficient of drag with waves 145234: - std_shww - Time-standard-deviation significant height of wind waves 145235: - std_mdww - Time-standard-deviation mean direction of wind waves 145236: - std_mpww - Time-standard-deviation mean period of wind waves 145237: - std_shts - Time-standard-deviation significant height of total swell 145238: - std_mdts - Time-standard-deviation mean direction of total swell 145239: - std_mpts - Time-standard-deviation mean period of total swell 145244: - std_msqs - Time-standard-deviation mean square slope of waves 145245: - std_wind - Time-standard-deviation 10 metre wind speed 145249: - std_dwi - Time-standard-deviation 10 metre wind direction 145252: - std_wsk - Time-standard-deviation wave spectral kurtosis 145253: - std_bfi - Time-standard-deviation benjamin-Feir index 145254: - std_wsp - Time-standard-deviation wave spectral peakedness 150129: - ocpt - Ocean potential temperature 150130: - ocs - Ocean salinity 150131: - ocpd - Ocean potential density 150133: - _param_150133 - Ocean U wind component 150134: - _param_150134 - Ocean V wind component 150135: - ocw - Ocean W wind component 150137: - rn - Richardson number 150139: - uv - U*V product 150140: - ut - U*T product 150141: - vt - V*T product 150142: - uu - U*U product 150143: - vv - V*V product 150144: - _param_150144 - UV - U~V~ 150145: - _param_150145 - UT - U~T~ 150146: - _param_150146 - VT - V~T~ 150147: - _param_150147 - UU - U~U~ 150148: - _param_150148 - VV - V~V~ 150152: - sl - Sea level 150153: - _param_150153 - Barotropic stream function 150154: - mld - Mixed layer depth 150155: - _param_150155 - Depth 150168: - _param_150168 - U stress 150169: - _param_150169 - V stress 150170: - _param_150170 - Turbulent kinetic energy input 150171: - nsf - Net surface heat flux 150172: - _param_150172 - Surface solar radiation 150173: - _param_150173 - P-E 150180: - _param_150180 - Diagnosed sea surface temperature error 150181: - _param_150181 - Heat flux correction 150182: - _param_150182 - Observed sea surface temperature 150183: - _param_150183 - Observed heat flux 150255: - _param_150255 - Indicates a missing value 151126: - mswpt300m - Mean sea water potential temperature in the upper 300 m 151127: - mswt300m - Mean sea water temperature in the upper 300 m 151128: - _param_151128 - In situ Temperature 151129: - thetao - Sea water potential temperature 151130: - so - Sea water practical salinity 151131: - ocu - Eastward surface sea water velocity 151132: - ocv - Northward surface sea water velocity 151133: - wo - Upward sea water velocity 151134: - mst - Modulus of strain rate tensor 151135: - vvs - Vertical viscosity 151136: - vdf - Vertical diffusivity 151137: - dep - Bottom level Depth 151138: - sigmat - Sea water sigma theta 151139: - rn - Richardson number 151140: - uv - UV product 151141: - ut - UT product 151142: - vt - VT product 151143: - uu - UU product 151144: - vv - VV product 151145: - zos - Sea surface height 151146: - sl_1 - Sea level previous timestep 151147: - stfbarot - Ocean barotropic stream function 151148: - mld - Mixed layer depth 151149: - btp - Bottom Pressure (equivalent height) 151150: - sh - Steric height 151151: - crl - Curl of Wind Stress 151152: - _param_151152 - Divergence of wind stress 151153: - taueo - Surface downward eastward stress 151154: - tauno - Surface downward northward stress 151155: - tki - Turbulent kinetic energy input 151156: - nsf - Net surface heat flux 151157: - asr - Absorbed solar radiation 151158: - pme - Precipitation - evaporation 151159: - sst - Specified sea surface temperature 151160: - shf - Specified surface heat flux 151161: - dte - Diagnosed sea surface temperature error 151162: - hfc - Heat flux correction 151163: - t20d - Depth of 20C isotherm 151164: - tav300 - Average potential temperature in the upper 300m 151165: - uba1 - Vertically integrated zonal velocity (previous time step) 151166: - vba1 - Vertically Integrated meridional velocity (previous time step) 151167: - ztr - Vertically integrated zonal volume transport 151168: - mtr - Vertically integrated meridional volume transport 151169: - zht - Vertically integrated zonal heat transport 151170: - mht - Vertically integrated meridional heat transport 151171: - umax - U velocity maximum 151172: - dumax - Depth of the velocity maximum 151173: - smax - Salinity maximum 151174: - dsmax - Depth of salinity maximum 151175: - sav300 - Average sea water practical salinity in the upper 300m 151176: - ldp - Layer Thickness at scalar points 151177: - ldu - Layer Thickness at vector points 151178: - pti - Potential temperature increment 151179: - ptae - Potential temperature analysis error 151180: - bpt - Background potential temperature 151181: - apt - Analysed potential temperature 151182: - ptbe - Potential temperature background error 151183: - as - Analysed salinity 151184: - sali - Salinity increment 151185: - ebt - Estimated Bias in Temperature 151186: - ebs - Estimated Bias in Salinity 151187: - uvi - Zonal Velocity increment (from balance operator) 151188: - vvi - Meridional Velocity increment (from balance operator) 151189: - tos - Sea surface temperature in degC 151190: - subi - Salinity increment (from salinity data) 151191: - sale - Salinity analysis error 151192: - bsal - Background Salinity 151193: - _param_151193 - Reserved 151194: - salbe - Salinity background error 151195: - t14d - Depth of 14C isotherm 151196: - t17d - Depth of 17C isotherm 151197: - t26d - Depth of 26C isotherm 151198: - t28d - Depth of 28C isotherm 151199: - ebta - Estimated temperature bias from assimilation 151200: - ebsa - Estimated salinity bias from assimilation 151201: - lti - Temperature increment from relaxation term 151202: - lsi - Salinity increment from relaxation term 151203: - bzpga - Bias in the zonal pressure gradient (applied) 151204: - bmpga - Bias in the meridional pressure gradient (applied) 151205: - ebtl - Estimated temperature bias from relaxation 151206: - ebsl - Estimated salinity bias from relaxation 151207: - fgbt - First guess bias in temperature 151208: - fgbs - First guess bias in salinity 151209: - bpa - Applied bias in pressure 151210: - fgbp - FG bias in pressure 151211: - pta - Bias in temperature(applied) 151212: - psa - Bias in salinity (applied) 151213: - hfds - Surface downward heat flux in sea water 151214: - hc300m - Ocean heat content 0-300m 151215: - hc700m - Ocean heat content 0-700m 151216: - hcbtm - Ocean heat content 0-bottom 151217: - wfcorr - Water flux correction 151219: - sos - Sea surface practical salinity 151222: - tauvo - Surface downward y stress 151223: - tauuo - Surface downward x stress 151224: - mlotdev - Ocean mixed layer thickness defined by vertical tracer diffusivity threshold 151225: - mlotst010 - Ocean mixed layer thickness defined by sigma theta 0.01 kg/m3 151226: - mlotst030 - Ocean mixed layer thickness defined by sigma theta 0.03 kg/m3 151227: - mlotst125 - Ocean mixed layer thickness defined by sigma theta 0.125 kg/m3 151228: - mlott02 - Ocean mixed layer thickness defined by temperature 0.2C 151229: - mlott05 - Ocean mixed layer thickness defined by temperature 0.5C 151230: - vsf - Virtual salt flux into sea water 151232: - sc700m - Integrated salinity 0-700m 151233: - sabtm - Integrated salinity 0-bottom 151234: - swmth - Sea water mass per unit area expressed as thickness 151235: - sc300m - Integrated salinity 0-300m 151236: - rss - Surface net downward shortwave flux into ocean 151250: - voy - Sea water y velocity 151251: - uox - Sea water x velocity 151255: - _param_151255 - Indicates a missing value 160049: - 10fgrea - 10 metre wind gust during averaging time 160135: - wrea - vertical velocity (pressure) 160137: - pwcrea - Precipitable water content 160140: - swl1rea - Soil wetness level 1 160141: - sdrea - Snow depth 160142: - lsprea - Large-scale precipitation 160143: - cprea - Convective precipitation 160144: - sfrea - Snowfall 160156: - ghrea - Height 160157: - rrea - Relative humidity 160171: - swl2rea - Soil wetness level 2 160180: - ewssrea - East-West surface stress 160181: - nsssrea - North-South surface stress 160182: - erea - Evaporation 160184: - swl3rea - Soil wetness level 3 160198: - srcon - Skin reservoir content 160199: - vegrea - Percentage of vegetation 160205: - rorea - Runoff 160207: - tzrea - Covariance of temperature and geopotential 160209: - qzrea - Covariance of specific humidity and geopotential 160210: - qtrea - Covariance of specific humidity and temperature 160212: - uzrea - Covariance of U component and geopotential 160213: - utrea - Covariance of U component and temperature 160214: - uqrea - Covariance of U component and specific humidity 160216: - vzrea - Covariance of V component and geopotential 160217: - vtrea - Covariance of V component and temperature 160218: - vqrea - Covariance of V component and specific humidity 160219: - vurea - Covariance of V component and U component 160221: - wzrea - Covariance of W component and geopotential 160222: - wtrea - Covariance of W component and temperature 160223: - wqrea - Covariance of W component and specific humidity 160224: - wurea - Covariance of W component and U component 160225: - wvrea - Covariance of W component and V component 160231: - ishfrea - Instantaneous surface heat flux 160239: - csfrea - Convective snowfall 160240: - lsfrea - Large scale snowfall 160241: - clwcerrea - Cloud liquid water content 160242: - ccrea - Cloud cover 160243: - falrea - Forecast albedo 160246: - 10wsrea - 10 metre wind speed 160247: - moflrea - Momentum flux 160249: - _param_160249 - Gravity wave dissipation flux 160254: - hsdrea - Heaviside beta function 162031: - viwgd - Total column vertically-integrated divergence of water geopotential flux 162032: - viee - Total column vertically-integrated eastward enthalpy flux 162033: - vien - Total column vertically-integrated northward enthalpy flux 162034: - vied - Total column vertically-integrated divergence of enthalpy flux 162035: - viwed - Total column vertically-integrated divergence of water enthalpy flux 162036: - viwvd - Total column vertically-integrated divergence of water vapour flux 162037: - viclwd - Total column vertically-integrated divergence of cloud liquid water flux 162038: - viciwd - Total column vertically-integrated divergence of cloud ice water flux 162039: - vird - Total column vertically-integrated divergence of rain flux 162040: - visd - Total column vertically-integrated divergence of snow flux 162041: - viclwe - Total column vertically-integrated eastward cloud liquid water flux 162042: - viclwn - Total column vertically-integrated northward cloud liquid water flux 162043: - viciwe - Total column vertically-integrated eastward cloud ice water flux 162044: - viciwn - Total column vertically-integrated northward cloud ice water flux 162045: - wvf - Water vapour flux 162046: - vire - Total column vertically-integrated eastward rain flux 162047: - virn - Total column vertically-integrated northward rain flux 162048: - vise - Total column vertically-integrated eastward snow flux 162049: - visn - Total column vertically-integrated northward snow flux 162050: - vions - Total column vertically-integrated net source of ozone 162051: - _param_162051 - Surface geopotential 162053: - vima - Total column vertically-integrated mass of atmosphere 162054: - vit - Total column vertically-integrated temperature 162055: - viwv - Total column vertically-integrated water vapour 162056: - vilw - Total column vertically-integrated cloud liquid water 162057: - viiw - Total column vertically-integrated cloud frozen water 162058: - vioz - Total column vertically-integrated ozone 162059: - vike - Total column vertically-integrated kinetic energy 162060: - vithe - Total column vertically-integrated enthalpy 162061: - vipie - Total column vertically-integrated potential + internal energy 162062: - vipile - Total column vertically-integrated potential+internal+latent energy 162063: - vitoe - Total column vertically-integrated total energy 162064: - viec - Total column vertically-integrated energy conversion 162065: - vimae - Total column vertically-integrated eastward mass flux 162066: - viman - Total column vertically-integrated northward mass flux 162067: - vikee - Total column vertically-integrated eastward kinetic energy flux 162068: - viken - Total column vertically-integrated northward kinetic energy flux 162069: - vithee - Total column vertically-integrated eastward heat flux 162070: - vithen - Total column vertically-integrated northward heat flux 162071: - viwve - Total column vertically-integrated eastward water vapour flux 162072: - viwvn - Total column vertically-integrated northward water vapour flux 162073: - vige - Total column vertically-integrated eastward geopotential flux 162074: - vign - Total column vertically-integrated northward geopotential flux 162075: - vitee - Total column vertically-integrated eastward total energy flux 162076: - viten - Total column vertically-integrated northward total energy flux 162077: - vioze - Total column vertically-integrated eastward ozone flux 162078: - viozn - Total column vertically-integrated northward ozone flux 162079: - vilwd - Total column vertically-integrated divergence of cloud liquid water flux 162080: - viiwd - Total column vertically-integrated divergence of cloud frozen water flux 162081: - vimad - Total column vertically-integrated divergence of mass flux 162082: - viked - Total column vertically-integrated divergence of kinetic energy flux 162083: - vithed - Total column vertically-integrated divergence of thermal energy flux 162084: - vimdf - Total column vertically-integrated moisture divergence flux 162085: - vigd - Total column vertically-integrated divergence of geopotential flux 162086: - vited - Total column vertically-integrated divergence of total energy flux 162087: - viozd - Total column vertically-integrated divergence of ozone flux 162088: - vilwe - Total column vertically-integrated eastward cloud liquid water flux 162089: - vilwn - Total column vertically-integrated northward cloud liquid water flux 162090: - viiwe - Total column vertically-integrated eastward cloud frozen water flux 162091: - viiwn - Total column vertically-integrated northward cloud frozen water flux 162092: - vimat - Total column vertically-integrated mass tendency 162093: - viwe - Total column vertically-integrated water enthalpy 162100: - srta - Time-integrated temperature tendency due to short-wave radiation 162101: - trta - Time-integrated temperature tendency due to long-wave radiation 162102: - srtca - Time-integrated temperature tendency due to short wave radiation, clear sky 162103: - trtca - Time-integrated temperature tendency due to long-wave radiation, clear sky 162104: - umfa - Time-integrated updraught mass flux 162105: - dmfa - Time-integrated downdraught mass flux 162106: - udra - Time-integrated updraught detrainment rate 162107: - ddra - Time-integrated downdraught detrainment rate 162108: - tpfa - Time-integrated total precipitation flux 162109: - tdcha - Time-integrated turbulent diffusion coefficient for heat 162110: - ttpha - Time-integrated temperature tendency due to parametrisations 162111: - qtpha - Time-integrated specific humidity tendency due to parametrisations 162112: - utpha - Time-integrated eastward wind tendency due to parametrisations 162113: - vtpha - Time-integrated northward wind tendency due to parametrisations 162114: - utendd - U-tendency from dynamics 162115: - vtendd - V-tendency from dynamics 162116: - ttendd - T-tendency from dynamics 162117: - qtendd - q-tendency from dynamics 162118: - ttendr - T-tendency from radiation 162119: - utendts - U-tendency from turbulent diffusion + subgrid orography 162120: - vtendts - V-tendency from turbulent diffusion + subgrid orography 162121: - ttendts - T-tendency from turbulent diffusion + subgrid orography 162122: - qtendt - q-tendency from turbulent diffusion 162123: - utends - U-tendency from subgrid orography 162124: - vtends - V-tendency from subgrid orography 162125: - ttends - T-tendency from subgrid orography 162126: - utendcds - U-tendency from convection (deep+shallow) 162127: - vtendcds - V-tendency from convection (deep+shallow) 162128: - ttendcds - T-tendency from convection (deep+shallow) 162129: - qtendcds - q-tendency from convection (deep+shallow) 162130: - lpc - Liquid Precipitation flux from convection 162131: - ipc - Ice Precipitation flux from convection 162132: - ttendcs - T-tendency from cloud scheme 162133: - qtendcs - q-tendency from cloud scheme 162134: - qltendcs - ql-tendency from cloud scheme 162135: - qitendcs - qi-tendency from cloud scheme 162136: - lpcs - Liquid Precip flux from cloud scheme (stratiform) 162137: - ipcs - Ice Precip flux from cloud scheme (stratiform) 162138: - utendcs - U-tendency from shallow convection 162139: - vtendcs - V-tendency from shallow convection 162140: - ttendsc - T-tendency from shallow convection 162141: - qtendsc - q-tendency from shallow convection 162150: - ttswr - Temperature tendency due to short-wave radiation 162151: - ttlwr - Temperature tendency due to long-wave radiation 162152: - ttswrcs - Temperature tendency due to short-wave radiation, clear sky 162153: - ttlwrcs - Temperature tendency due to long-wave radiation, clear sky 162154: - umf - Updraught mass flux 162155: - dmf - Downdraught mass flux 162156: - udr - Updraught detrainment rate 162157: - ddr - Downdraught detrainment rate 162158: - tpf - Total precipitation flux 162159: - tdch - Turbulent diffusion coefficient for heat 162160: - ttpm - Temperature tendency due to parametrisations 162161: - qtpm - Specific humidity tendency due to parametrisations 162162: - utpm - Eastward wind tendency due to parametrisations 162163: - vtpm - Northward wind tendency due to parametrisations 162206: - _param_162206 - Variance of geopotential 162207: - _param_162207 - Covariance of geopotential/temperature 162208: - _param_162208 - Variance of temperature 162209: - _param_162209 - Covariance of geopotential/specific humidity 162210: - _param_162210 - Covariance of temperature/specific humidity 162211: - _param_162211 - Variance of specific humidity 162212: - _param_162212 - Covariance of u component/geopotential 162213: - _param_162213 - Covariance of u component/temperature 162214: - _param_162214 - Covariance of u component/specific humidity 162215: - _param_162215 - Variance of u component 162216: - _param_162216 - Covariance of v component/geopotential 162217: - _param_162217 - Covariance of v component/temperature 162218: - _param_162218 - Covariance of v component/specific humidity 162219: - _param_162219 - Covariance of v component/u component 162220: - _param_162220 - Variance of v component 162221: - _param_162221 - Covariance of omega/geopotential 162222: - _param_162222 - Covariance of omega/temperature 162223: - _param_162223 - Covariance of omega/specific humidity 162224: - _param_162224 - Covariance of omega/u component 162225: - _param_162225 - Covariance of omega/v component 162226: - _param_162226 - Variance of omega 162227: - _param_162227 - Variance of surface pressure 162229: - _param_162229 - Variance of relative humidity 162230: - _param_162230 - Covariance of u component/ozone 162231: - _param_162231 - Covariance of v component/ozone 162232: - _param_162232 - Covariance of omega/ozone 162233: - _param_162233 - Variance of ozone 162255: - _param_162255 - Indicates a missing value 170001: - spi03 - Standardised precipitation index valid in the last 3 months 170002: - spi06 - Standardised precipitation index valid in the last 6 months 170003: - spi12 - Standardised precipitation index valid in the last 12 months 170149: - tsw - Total soil moisture 170171: - swl2 - Soil wetness level 2 170179: - ttr - Top net thermal radiation 171001: - strfa - Stream function anomaly 171002: - vpota - Velocity potential anomaly 171003: - pta - Potential temperature anomaly 171004: - epta - Equivalent potential temperature anomaly 171005: - septa - Saturated equivalent potential temperature anomaly 171006: - 100ua - 100 metre U wind component anomaly 171007: - 100va - 100 metre V wind component anomaly 171008: - 100sia - 100 metre wind speed anomaly 171011: - udwa - U component of divergent wind anomaly 171012: - vdwa - V component of divergent wind anomaly 171013: - urwa - U component of rotational wind anomaly 171014: - vrwa - V component of rotational wind anomaly 171021: - uctpa - Unbalanced component of temperature anomaly 171022: - uclna - Unbalanced component of logarithm of surface pressure anomaly 171023: - ucdva - Unbalanced component of divergence anomaly 171024: - lmlta - Lake mix-layer temperature anomaly 171025: - licda - Lake ice depth anomaly 171026: - cla - Lake cover anomaly 171027: - cvla - Low vegetation cover anomaly 171028: - cvha - High vegetation cover anomaly 171029: - tvla - Type of low vegetation anomaly 171030: - tvha - Type of high vegetation anomaly 171031: - sica - Sea-ice cover anomaly - ci 171032: - asna - Snow albedo anomaly 171033: - rsna - Snow density anomaly 171034: - ssta - Sea surface temperature anomaly 171035: - istal1 - Ice surface temperature anomaly layer 1 171036: - istal2 - Ice surface temperature anomaly layer 2 171037: - istal3 - Ice surface temperature anomaly layer 3 171038: - istal4 - Ice surface temperature anomaly layer 4 171039: - swval1 - Volumetric soil water anomaly layer 1 - swv1 - swvl1a 171040: - swval2 - Volumetric soil water anomaly layer 2 - swv2 - swvl2a 171041: - swval3 - Volumetric soil water anomaly layer 3 - swv3 - swvl3a 171042: - swval4 - Volumetric soil water anomaly layer 4 - swv4 - swvl4a 171043: - slta - Soil type anomaly 171044: - esa - Snow evaporation anomaly 171045: - smlta - Snowmelt anomaly 171046: - sdura - Solar duration anomaly 171047: - dsrpa - Direct solar radiation anomaly 171048: - magssa - Magnitude of turbulent surface stress anomaly 171049: - 10fga - 10 metre wind gust anomaly 171050: - lspfa - Large-scale precipitation fraction anomaly 171051: - mx2t24a - Maximum 2 metre temperature in the last 24 hours anomaly 171052: - mn2t24a - Minimum 2 metre temperature in the last 24 hours anomaly 171053: - monta - Montgomery potential anomaly 171054: - pa - Pressure anomaly 171055: - mean2t24a - Mean 2 metre temperature in the last 24 hours anomaly 171056: - mn2d24a - Mean 2 metre dewpoint temperature in the last 24 hours anomaly 171057: - uvba - Downward UV radiation at the surface anomaly 171058: - para - Photosynthetically active radiation at the surface anomaly 171059: - capea - Convective available potential energy anomaly 171060: - pva - Potential vorticity anomaly 171061: - tpoa - Total precipitation from observations anomaly 171062: - obcta - Observation count anomaly 171063: - stsktda - Start time for skin temperature difference anomaly 171064: - ftsktda - Finish time for skin temperature difference anomaly 171065: - sktda - Skin temperature difference anomaly 171078: - tclwa - Total column liquid water anomaly 171079: - tciwa - Total column ice water anomaly 171121: - mx2t6a - Maximum temperature at 2 metres in the last 6 hours anomaly 171122: - mn2t6a - Minimum temperature at 2 metres in the last 6 hours anomaly 171125: - vitea - Vertically integrated total energy anomaly 171126: - _param_171126 - Generic parameter for sensitive area prediction 171127: - ata - Atmospheric tide anomaly 171128: - bva - Budget values anomaly 171129: - za - Geopotential anomaly - z 171130: - ta - Temperature anomaly - t 171131: - ua - U component of wind anomaly - u 171132: - va - V component of wind anomaly - v 171133: - qa - Specific humidity anomaly 171134: - spa - Surface pressure anomaly 171135: - wa - Vertical velocity (pressure) anomaly 171136: - tcwa - Total column water anomaly 171137: - tcwva - Total column water vapour anomaly 171138: - voa - Relative vorticity anomaly - vo 171139: - stal1 - Soil temperature anomaly level 1 - stl1 - st 171140: - swal1 - Soil wetness anomaly level 1 - swl1 171141: - sda - Snow depth anomaly 171142: - lspa - Stratiform precipitation (Large-scale precipitation) anomaly 171143: - cpa - Convective precipitation anomaly 171144: - sfa - Snowfall (convective + stratiform) anomaly 171145: - blda - Boundary layer dissipation anomaly 171146: - sshfa - Surface sensible heat flux anomaly 171147: - slhfa - Surface latent heat flux anomaly 171148: - chnka - Charnock anomaly 171149: - snra - Surface net radiation anomaly 171150: - tnra - Top net radiation anomaly 171151: - msla - Mean sea level pressure anomaly - msl 171152: - lspa - Logarithm of surface pressure anomaly 171153: - swhra - Short-wave heating rate anomaly 171154: - lwhra - Long-wave heating rate anomaly 171155: - da - Relative divergence anomaly - d 171156: - gha - Height anomaly - gh 171157: - ra - Relative humidity anomaly - r 171158: - tspa - Tendency of surface pressure anomaly 171159: - blha - Boundary layer height anomaly 171160: - sdora - Standard deviation of orography anomaly 171161: - isora - Anisotropy of sub-gridscale orography anomaly 171162: - anora - Angle of sub-gridscale orography anomaly 171163: - slora - Slope of sub-gridscale orography anomaly 171164: - tcca - Total cloud cover anomaly 171165: - 10ua - 10 metre U wind component anomaly 171166: - 10va - 10 metre V wind component anomaly 171167: - 2ta - 2 metre temperature anomaly 171168: - 2da - 2 metre dewpoint temperature anomaly 171169: - ssrda - Surface solar radiation downwards anomaly 171170: - stal2 - Soil temperature anomaly level 2 - slal2 - stl2 171171: - swal2 - Soil wetness anomaly level 2 - swl2 171173: - sra - Surface roughness anomaly 171174: - ala - Albedo anomaly 171175: - strda - Surface thermal radiation downwards anomaly 171176: - ssra - Surface net solar radiation anomaly 171177: - stra - Surface net thermal radiation anomaly 171178: - tsra - Top net solar radiation anomaly 171179: - ttra - Top net thermal radiation anomaly 171180: - eqssa - East-West surface stress anomaly 171181: - nsssa - North-South surface stress anomaly 171182: - ea - Evaporation anomaly 171183: - stal3 - Soil temperature anomaly level 3 - stl3 171184: - swal3 - Soil wetness anomaly level 3 - swl3 171185: - ccca - Convective cloud cover anomaly 171186: - lcca - Low cloud cover anomaly 171187: - mcca - Medium cloud cover anomaly 171188: - hcca - High cloud cover anomaly 171189: - sunda - Sunshine duration anomaly 171190: - ewova - East-West component of sub-gridscale orographic variance anomaly 171191: - nsova - North-South component of sub-gridscale orographic variance anomaly 171192: - nwova - North-West/South-East component of sub-gridscale orographic variance anomaly 171193: - neova - North-East/South-West component of sub-gridscale orographic variance anomaly 171194: - btmpa - Brightness temperature anomaly 171195: - lgwsa - Longitudinal component of gravity wave stress anomaly 171196: - mgwsa - Meridional component of gravity wave stress anomaly 171197: - gwda - Gravity wave dissipation anomaly 171198: - srca - Skin reservoir content anomaly 171199: - vfa - Vegetation fraction anomaly 171200: - vsoa - Variance of sub-gridscale orography anomaly 171201: - mx2ta - Maximum temperature at 2 metres anomaly 171202: - mn2ta - Minimum temperature at 2 metres anomaly 171203: - o3a - Ozone mass mixing ratio anomaly 171204: - pawa - Precipitation analysis weights anomaly 171205: - roa - Runoff anomaly 171206: - tco3a - Total column ozone anomaly 171207: - 10sia - 10 metre wind speed anomaly - 10si 171208: - tsrca - Top net solar radiation clear sky anomaly 171209: - ttrca - Top net thermal radiation clear sky anomaly 171210: - ssrca - Surface net solar radiation clear sky anomaly 171211: - strca - Surface net thermal radiation, clear sky anomaly 171212: - sia - Solar insolation anomaly 171214: - dhra - Diabatic heating by radiation anomaly 171215: - dhvda - Diabatic heating by vertical diffusion anomaly 171216: - dhcca - Diabatic heating by cumulus convection anomaly 171217: - dhlca - Diabatic heating by large-scale condensation anomaly 171218: - vdzwa - Vertical diffusion of zonal wind anomaly 171219: - vdmwa - Vertical diffusion of meridional wind anomaly 171220: - ewgda - East-West gravity wave drag tendency anomaly 171221: - nsgda - North-South gravity wave drag tendency anomaly 171222: - ctzwa - Convective tendency of zonal wind anomaly 171223: - ctmwa - Convective tendency of meridional wind anomaly 171224: - vdha - Vertical diffusion of humidity anomaly 171225: - htcca - Humidity tendency by cumulus convection anomaly 171226: - htlca - Humidity tendency by large-scale condensation anomaly 171227: - crnha - Change from removal of negative humidity anomaly 171228: - tpa - Total precipitation anomaly 171229: - iewsa - Instantaneous X surface stress anomaly 171230: - inssa - Instantaneous Y surface stress anomaly 171231: - ishfa - Instantaneous surface heat flux anomaly 171232: - iea - Instantaneous moisture flux anomaly 171233: - asqa - Apparent surface humidity anomaly 171234: - lsrha - Logarithm of surface roughness length for heat anomaly 171235: - skta - Skin temperature anomaly 171236: - stal4 - Soil temperature level 4 anomaly 171237: - swal4 - Soil wetness level 4 anomaly - swl4 171238: - tsna - Temperature of snow layer anomaly 171239: - csfa - Convective snowfall anomaly 171240: - lsfa - Large scale snowfall anomaly 171241: - acfa - Accumulated cloud fraction tendency anomaly 171242: - alwa - Accumulated liquid water tendency anomaly 171243: - fala - Forecast albedo anomaly 171244: - fsra - Forecast surface roughness anomaly 171245: - flsra - Forecast logarithm of surface roughness for heat anomaly 171246: - clwca - Cloud liquid water content anomaly 171247: - ciwca - Cloud ice water content anomaly 171248: - cca - Cloud cover anomaly 171249: - aiwa - Accumulated ice water tendency anomaly 171250: - iaa - Ice age anomaly 171251: - attea - Adiabatic tendency of temperature anomaly 171252: - athea - Adiabatic tendency of humidity anomaly 171253: - atzea - Adiabatic tendency of zonal wind anomaly 171254: - atmwa - Adiabatic tendency of meridional wind anomaly 171255: - _param_171255 - Indicates a missing value 172008: - msror - Time-mean surface runoff rate 172009: - mssror - Time-mean sub-surface runoff rate 172044: - esrate - Time-mean snow evaporation rate - es 172045: - _param_172045 - Time-mean snowmelt rate 172050: - mlspfr - Mean large-scale precipitation fraction - lspf 172142: - mlsprt - Time-mean large-scale precipitation rate - lsp 172143: - cprate - Time-mean convective precipitation rate - cp 172144: - mtsfr - Time-mean total snowfall rate - sf 172145: - bldrate - Boundary layer dissipation 172146: - msshfl - Time-mean surface sensible heat flux - sshf - '146' 172147: - mslhfl - Time-mean surface latent heat flux - slhf - '147' 172149: - msnrf - Mean surface net radiation flux - snr 172153: - mswhr - Time-mean short-wave (solar) heating rate - swhr 172154: - mlwhr - Time-mean long-wave (thermal) heating rate - lwhr 172169: - msdsrf - Time-mean surface downward short-wave (solar) radiation flux - ssrd 172175: - msdtrf - Time-mean surface downward long-wave (thermal) radiation flux - strd 172176: - msnsrf - Time-mean surface net short-wave (solar) radiation flux - ssr 172177: - msntrf - Time-mean surface net long-wave (thermal) radiation flux - str 172178: - mtnsrf - Time-mean top net short-wave (solar) radiation flux - tsr 172179: - mtntrf - Time-mean top net long-wave (thermal) radiation flux - ttr 172180: - ewssra - Time-mean eastward turbulent surface stress - ewss 172181: - nsssra - Time-mean northward turbulent surface stress - nsss 172182: - erate - Time-mean evaporation rate - e 172189: - msdr - Mean sunshine duration rate - sund 172195: - _param_172195 - Longitudinal component of gravity wave stress 172196: - _param_172196 - Meridional component of gravity wave stress 172197: - gwdrate - Gravity wave dissipation 172205: - mrort - Time-mean runoff rate - ro 172208: - _param_172208 - Time-mean top net short-wave (solar) radiation flux, clear sky 172209: - _param_172209 - Time-mean top net long-wave (thermal) radiation flux, clear sky 172210: - _param_172210 - Time-mean surface net short-wave (solar) radiation flux, clear sky 172211: - _param_172211 - Time-mean surface net long-wave (thermal) radiation flux, clear sky 172212: - soira - Time-mean short-wave (solar) insolation rate 172228: - tprate - Time-mean total precipitation rate - tp 172239: - _param_172239 - Time-mean convective snowfall rate 172240: - _param_172240 - Time-mean large-scale snowfall rate 172255: - _param_172255 - Indicates a missing value 173008: - msrora - Mean surface runoff rate anomaly 173009: - mssrora - Mean sub-surface runoff rate anomaly 173044: - _param_173044 - Time-mean snow evaporation anomalous rate of accumulation 173045: - _param_173045 - Time-mean snowmelt anomalous rate of accumulation 173048: - _param_173048 - Magnitude of turbulent surface stress anomaly 173050: - _param_173050 - Large-scale precipitation fraction anomaly 173142: - lspara - Stratiform precipitation (Large-scale precipitation) anomalous rate of accumulation - lspa 173143: - mcpra - Mean convective precipitation rate anomaly - cpa 173144: - sfara - Snowfall (convective + stratiform) anomalous rate of accumulation 173145: - _param_173145 - Boundary layer dissipation anomaly 173146: - sshfara - Surface sensible heat flux anomalous rate of accumulation 173147: - slhfara - Surface latent heat flux anomalous rate of accumulation 173149: - _param_173149 - Surface net radiation anomaly 173153: - _param_173153 - Short-wave heating rate anomaly 173154: - _param_173154 - Long-wave heating rate anomaly 173169: - ssrdara - Surface solar radiation downwards anomalous rate of accumulation - ssrda - ssrd 173175: - strdara - Surface thermal radiation downwards anomalous rate of accumulation - strda 173176: - ssrara - Surface solar radiation anomalous rate of accumulation - ssra - ssr 173177: - strara - Surface thermal radiation anomalous rate of accumulation - stra - str 173178: - tsrara - Top solar radiation anomalous rate of accumulation - tsra - tsr 173179: - ttrara - Top thermal radiation anomalous rate of accumulation - ttra - ttr 173180: - ewssara - East-West surface stress anomalous rate of accumulation 173181: - nsssara - North-South surface stress anomalous rate of accumulation 173182: - evara - Time-mean evaporation anomalous rate of accumulation - ea - e 173189: - sundara - Sunshine duration anomalous rate of accumulation 173195: - _param_173195 - Longitudinal component of gravity wave stress anomaly 173196: - _param_173196 - Meridional component of gravity wave stress anomaly 173197: - _param_173197 - Gravity wave dissipation anomaly 173205: - roara - Runoff anomalous rate of accumulation 173208: - _param_173208 - Top net solar radiation, clear sky anomaly 173209: - _param_173209 - Top net thermal radiation, clear sky anomaly 173210: - _param_173210 - Surface net solar radiation, clear sky anomaly 173211: - _param_173211 - Surface net thermal radiation, clear sky anomaly 173212: - soiara - Solar insolation anomalous rate of accumulation 173228: - tpara - Total precipitation anomalous rate of accumulation 173239: - _param_173239 - Convective snowfall anomaly 173240: - _param_173240 - Large scale snowfall anomaly 173255: - _param_173255 - Indicates a missing value 174006: - _param_174006 - Total soil moisture 174008: - sro - Surface runoff 174010: - sswcsdown - Clear-sky (II) down surface sw flux 174013: - sswcsup - Clear-sky (II) up surface sw flux 174025: - vis15 - Visibility at 1.5m 174031: - _param_174031 - Fraction of sea-ice in sea 174034: - _param_174034 - Open-sea surface temperature 174039: - _param_174039 - Volumetric soil water layer 1 174040: - _param_174040 - Volumetric soil water layer 2 174041: - _param_174041 - Volumetric soil water layer 3 174042: - _param_174042 - Volumetric soil water layer 4 174049: - _param_174049 - 10 metre wind gust in the last 24 hours 174050: - mn15t - Minimum temperature at 1.5m since previous post-processing 174051: - mx15t - Maximum temperature at 1.5m since previous post-processing 174052: - rhum - Relative humidity at 1.5m 174055: - _param_174055 - 1.5m temperature - mean in the last 24 hours 174083: - _param_174083 - Net primary productivity 174085: - _param_174085 - 10m U wind over land 174086: - _param_174086 - 10m V wind over land 174087: - _param_174087 - 1.5m temperature over land 174088: - _param_174088 - 1.5m dewpoint temperature over land 174089: - _param_174089 - Top incoming solar radiation 174090: - _param_174090 - Top outgoing solar radiation 174092: - sialb - Sea ice albedo 174093: - sitemptop - Sea ice surface temperature 174094: - _param_174094 - Mean sea surface temperature 174095: - _param_174095 - 1.5m specific humidity 174096: - 2sh - 2 metre specific humidity 174097: - sisnthick - Sea ice snow thickness 174098: - sithick - Sea-ice thickness 174099: - _param_174099 - Liquid water potential temperature 174110: - _param_174110 - Ocean ice concentration 174111: - _param_174111 - Ocean mean ice depth 174112: - siue - Sea ice velocity along x 174113: - sivn - Sea ice velocity along y 174116: - swrsurf - Short wave radiation flux at surface 174117: - swrtop - Short wave radiation flux at top of atmosphere 174137: - tcwvap - Total column water vapour 174139: - _param_174139 - Soil temperature layer 1 174142: - lsrrate - Large scale rainfall rate 174143: - crfrate - Convective rainfall rate 174164: - _param_174164 - Average potential temperature in upper 293.4m 174167: - _param_174167 - 1.5m temperature 174168: - _param_174168 - 1.5m dewpoint temperature 174170: - _param_174170 - Soil temperature layer 2 174175: - _param_174175 - Average salinity in upper 293.4m 174183: - _param_174183 - Soil temperature layer 3 174186: - vlca - Very low cloud amount 174201: - _param_174201 - 1.5m temperature - maximum in the last 24 hours 174202: - _param_174202 - 1.5m temperature - minimum in the last 24 hours 174236: - _param_174236 - Soil temperature layer 4 174239: - csfrate - Convective snowfall rate 174240: - lsfrate - Large scale snowfall rate 174248: - tccro - Total cloud amount - random overlap 174249: - tcclwr - Total cloud amount in lw radiation 174255: - _param_174255 - Indicates a missing value 175006: - _param_175006 - Total soil moisture 175031: - _param_175031 - Fraction of sea-ice in sea 175034: - _param_175034 - Open-sea surface temperature 175039: - _param_175039 - Volumetric soil water layer 1 175040: - _param_175040 - Volumetric soil water layer 2 175041: - _param_175041 - Volumetric soil water layer 3 175042: - _param_175042 - Volumetric soil water layer 4 175049: - _param_175049 - 10m wind gust in the last 24 hours 175055: - _param_175055 - 1.5m temperature - mean in the last 24 hours 175083: - _param_175083 - Net primary productivity 175085: - _param_175085 - 10m U wind over land 175086: - _param_175086 - 10m V wind over land 175087: - _param_175087 - 1.5m temperature over land 175088: - _param_175088 - 1.5m dewpoint temperature over land 175089: - _param_175089 - Top incoming solar radiation 175090: - _param_175090 - Top outgoing solar radiation 175110: - _param_175110 - Ocean ice concentration 175111: - _param_175111 - Ocean mean ice depth 175139: - _param_175139 - Soil temperature layer 1 175164: - _param_175164 - Average potential temperature in upper 293.4m 175167: - _param_175167 - 1.5m temperature 175168: - _param_175168 - 1.5m dewpoint temperature 175170: - _param_175170 - Soil temperature layer 2 175175: - _param_175175 - Average salinity in upper 293.4m 175183: - _param_175183 - Soil temperature layer 3 175201: - _param_175201 - 1.5m temperature - maximum in the last 24 hours 175202: - _param_175202 - 1.5m temperature - minimum in the last 24 hours 175236: - _param_175236 - Soil temperature layer 4 175255: - _param_175255 - Indicates a missing value 180149: - tsw - Total soil wetness 180176: - ssr - Surface net solar radiation 180177: - str - Surface net thermal radiation 180178: - tsr - Top net solar radiation 180179: - ttr - Top net thermal radiation 190141: - sdsien - Snow depth 190170: - cap - Field capacity 190171: - wiltsien - Wilting point 190173: - sr - Roughness length 190229: - tsm - Total soil moisture 200001: - strfdiff - Stream function difference 200002: - vpotdiff - Velocity potential difference 200003: - ptdiff - Potential temperature difference 200004: - eqptdiff - Equivalent potential temperature difference 200005: - septdiff - Saturated equivalent potential temperature difference 200011: - udvwdiff - U component of divergent wind difference 200012: - vdvwdiff - V component of divergent wind difference 200013: - urtwdiff - U component of rotational wind difference 200014: - vrtwdiff - V component of rotational wind difference 200021: - uctpdiff - Unbalanced component of temperature difference 200022: - uclndiff - Unbalanced component of logarithm of surface pressure difference 200023: - ucdvdiff - Unbalanced component of divergence difference 200024: - _param_200024 - Reserved for future unbalanced components 200025: - _param_200025 - Reserved for future unbalanced components 200026: - cldiff - Lake cover difference 200027: - cvldiff - Low vegetation cover difference 200028: - cvhdiff - High vegetation cover difference 200029: - tvldiff - Type of low vegetation difference 200030: - tvhdiff - Type of high vegetation difference 200031: - sicdiff - Sea-ice cover difference 200032: - asndiff - Snow albedo difference 200033: - rsndiff - Snow density difference 200034: - sstdiff - Sea surface temperature difference 200035: - istl1diff - Ice surface temperature layer 1 difference 200036: - istl2diff - Ice surface temperature layer 2 difference 200037: - istl3diff - Ice surface temperature layer 3 difference 200038: - istl4diff - Ice surface temperature layer 4 difference 200039: - swvl1diff - Volumetric soil water layer 1 difference 200040: - swvl2diff - Volumetric soil water layer 2 difference 200041: - swvl3diff - Volumetric soil water layer 3 difference 200042: - swvl4diff - Volumetric soil water layer 4 difference 200043: - sltdiff - Soil type difference 200044: - esdiff - Snow evaporation difference 200045: - smltdiff - Snowmelt difference 200046: - sdurdiff - Solar duration difference 200047: - dsrpdiff - Direct solar radiation difference 200048: - magssdiff - Magnitude of turbulent surface stress difference 200049: - 10fgdiff - 10 metre wind gust difference 200050: - lspfdiff - Large-scale precipitation fraction difference 200051: - mx2t24diff - Maximum 2 metre temperature difference 200052: - mn2t24diff - Minimum 2 metre temperature difference 200053: - montdiff - Montgomery potential difference 200054: - presdiff - Pressure difference 200055: - mean2t24diff - Mean 2 metre temperature in the last 24 hours difference 200056: - mn2d24diff - Mean 2 metre dewpoint temperature in the last 24 hours difference 200057: - uvbdiff - Downward UV radiation at the surface difference 200058: - pardiff - Photosynthetically active radiation at the surface difference 200059: - capediff - Convective available potential energy difference 200060: - pvdiff - Potential vorticity difference 200061: - tpodiff - Total precipitation from observations difference 200062: - obctdiff - Observation count difference 200063: - _param_200063 - Start time for skin temperature difference 200064: - _param_200064 - Finish time for skin temperature difference 200065: - _param_200065 - Skin temperature difference 200066: - _param_200066 - Leaf area index, low vegetation 200067: - _param_200067 - Leaf area index, high vegetation 200070: - _param_200070 - Biome cover, low vegetation 200071: - _param_200071 - Biome cover, high vegetation 200078: - _param_200078 - Total column liquid water 200079: - _param_200079 - Total column ice water 200080: - _param_200080 - Experimental product 200081: - _param_200081 - Experimental product 200082: - _param_200082 - Experimental product 200083: - _param_200083 - Experimental product 200084: - _param_200084 - Experimental product 200085: - _param_200085 - Experimental product 200086: - _param_200086 - Experimental product 200087: - _param_200087 - Experimental product 200088: - _param_200088 - Experimental product 200089: - _param_200089 - Experimental product 200090: - _param_200090 - Experimental product 200091: - _param_200091 - Experimental product 200092: - _param_200092 - Experimental product 200093: - _param_200093 - Experimental product 200094: - _param_200094 - Experimental product 200095: - _param_200095 - Experimental product 200096: - _param_200096 - Experimental product 200097: - _param_200097 - Experimental product 200098: - _param_200098 - Experimental product 200099: - _param_200099 - Experimental product 200100: - _param_200100 - Experimental product 200101: - _param_200101 - Experimental product 200102: - _param_200102 - Experimental product 200103: - _param_200103 - Experimental product 200104: - _param_200104 - Experimental product 200105: - _param_200105 - Experimental product 200106: - _param_200106 - Experimental product 200107: - _param_200107 - Experimental product 200108: - _param_200108 - Experimental product 200109: - _param_200109 - Experimental product 200110: - _param_200110 - Experimental product 200111: - _param_200111 - Experimental product 200112: - _param_200112 - Experimental product 200113: - _param_200113 - Experimental product 200114: - _param_200114 - Experimental product 200115: - _param_200115 - Experimental product 200116: - _param_200116 - Experimental product 200117: - _param_200117 - Experimental product 200118: - _param_200118 - Experimental product 200119: - _param_200119 - Experimental product 200120: - _param_200120 - Experimental product 200121: - mx2t6diff - Maximum temperature at 2 metres difference 200122: - mn2t6diff - Minimum temperature at 2 metres difference 200123: - 10fg6diff - 10 metre wind gust in the last 6 hours difference 200125: - _param_200125 - Vertically integrated total energy 200126: - _param_200126 - Generic parameter for sensitive area prediction 200127: - atdiff - Atmospheric tide difference 200128: - bvdiff - Budget values difference 200129: - zdiff - Geopotential difference 200130: - tdiff - Temperature difference 200131: - udiff - U component of wind difference 200132: - vdiff - V component of wind difference 200133: - qdiff - Specific humidity difference 200134: - spdiff - Surface pressure difference 200135: - wdiff - Vertical velocity (pressure) difference 200136: - tcwdiff - Total column water difference 200137: - tcwvdiff - Total column water vapour difference 200138: - vodiff - Vorticity (relative) difference 200139: - stl1diff - Soil temperature level 1 difference 200140: - swl1diff - Soil wetness level 1 difference 200141: - sddiff - Snow depth difference 200142: - lspdiff - Stratiform precipitation (Large-scale precipitation) difference 200143: - cpdiff - Convective precipitation difference 200144: - sfdiff - Snowfall (convective + stratiform) difference 200145: - blddiff - Boundary layer dissipation difference 200146: - sshfdiff - Surface sensible heat flux difference 200147: - slhfdiff - Surface latent heat flux difference 200148: - chnkdiff - Charnock difference 200149: - snrdiff - Surface net radiation difference 200150: - tnrdiff - Top net radiation difference 200151: - msldiff - Mean sea level pressure difference 200152: - lnspdiff - Logarithm of surface pressure difference 200153: - swhrdiff - Short-wave heating rate difference 200154: - lwhrdiff - Long-wave heating rate difference 200155: - ddiff - Divergence difference 200156: - ghdiff - Height difference 200157: - rdiff - Relative humidity difference 200158: - tspdiff - Tendency of surface pressure difference 200159: - blhdiff - Boundary layer height difference 200160: - sdordiff - Standard deviation of orography difference 200161: - isordiff - Anisotropy of sub-gridscale orography difference 200162: - anordiff - Angle of sub-gridscale orography difference 200163: - slordiff - Slope of sub-gridscale orography difference 200164: - tccdiff - Total cloud cover difference 200165: - 10udiff - 10 metre U wind component difference 200166: - 10vdiff - 10 metre V wind component difference 200167: - 2tdiff - 2 metre temperature difference 200168: - 2ddiff - 2 metre dewpoint temperature difference 200169: - ssrddiff - Surface solar radiation downwards difference 200170: - stl2diff - Soil temperature level 2 difference 200171: - swl2diff - Soil wetness level 2 difference 200172: - lsmdiff - Land-sea mask difference 200173: - srdiff - Surface roughness difference 200174: - aldiff - Albedo difference 200175: - strddiff - Surface thermal radiation downwards difference 200176: - ssrdiff - Surface net solar radiation difference 200177: - strdiff - Surface net thermal radiation difference 200178: - tsrdiff - Top net solar radiation difference 200179: - ttrdiff - Top net thermal radiation difference 200180: - ewssdiff - East-West surface stress difference 200181: - nsssdiff - North-South surface stress difference 200182: - ediff - Evaporation difference 200183: - stl3diff - Soil temperature level 3 difference 200184: - swl3diff - Soil wetness level 3 difference 200185: - cccdiff - Convective cloud cover difference 200186: - lccdiff - Low cloud cover difference 200187: - mccdiff - Medium cloud cover difference 200188: - hccdiff - High cloud cover difference 200189: - sunddiff - Sunshine duration difference 200190: - ewovdiff - East-West component of sub-gridscale orographic variance difference 200191: - nsovdiff - North-South component of sub-gridscale orographic variance difference 200192: - nwovdiff - North-West/South-East component of sub-gridscale orographic variance difference 200193: - neovdiff - North-East/South-West component of sub-gridscale orographic variance difference 200194: - btmpdiff - Brightness temperature difference 200195: - lgwsdiff - Longitudinal component of gravity wave stress difference 200196: - mgwsdiff - Meridional component of gravity wave stress difference 200197: - gwddiff - Gravity wave dissipation difference 200198: - srcdiff - Skin reservoir content difference 200199: - vegdiff - Vegetation fraction difference 200200: - vsodiff - Variance of sub-gridscale orography difference 200201: - mx2tdiff - Maximum temperature at 2 metres since previous post-processing difference 200202: - mn2tdiff - Minimum temperature at 2 metres since previous post-processing difference 200203: - o3diff - Ozone mass mixing ratio difference 200204: - pawdiff - Precipitation analysis weights difference 200205: - rodiff - Runoff difference 200206: - tco3diff - Total column ozone difference 200207: - 10sidiff - 10 metre wind speed difference 200208: - tsrcdiff - Top net solar radiation, clear sky difference 200209: - ttrcdiff - Top net thermal radiation, clear sky difference 200210: - ssrcdiff - Surface net solar radiation, clear sky difference 200211: - strcdiff - Surface net thermal radiation, clear sky difference 200212: - tisrdiff - TOA incident solar radiation difference 200214: - dhrdiff - Diabatic heating by radiation difference 200215: - dhvddiff - Diabatic heating by vertical diffusion difference 200216: - dhccdiff - Diabatic heating by cumulus convection difference 200217: - dhlcdiff - Diabatic heating large-scale condensation difference 200218: - vdzwdiff - Vertical diffusion of zonal wind difference 200219: - vdmwdiff - Vertical diffusion of meridional wind difference 200220: - ewgddiff - East-West gravity wave drag tendency difference 200221: - nsgddiff - North-South gravity wave drag tendency difference 200222: - ctzwdiff - Convective tendency of zonal wind difference 200223: - ctmwdiff - Convective tendency of meridional wind difference 200224: - vdhdiff - Vertical diffusion of humidity difference 200225: - htccdiff - Humidity tendency by cumulus convection difference 200226: - htlcdiff - Humidity tendency by large-scale condensation difference 200227: - crnhdiff - Change from removal of negative humidity difference 200228: - tpdiff - Total precipitation difference 200229: - iewsdiff - Instantaneous X surface stress difference 200230: - inssdiff - Instantaneous Y surface stress difference 200231: - ishfdiff - Instantaneous surface heat flux difference 200232: - iediff - Instantaneous moisture flux difference 200233: - asqdiff - Apparent surface humidity difference 200234: - lsrhdiff - Logarithm of surface roughness length for heat difference 200235: - sktdiff - Skin temperature difference 200236: - stl4diff - Soil temperature level 4 difference 200237: - swl4diff - Soil wetness level 4 difference 200238: - tsndiff - Temperature of snow layer difference 200239: - csfdiff - Convective snowfall difference 200240: - lsfdiff - Large scale snowfall difference 200241: - acfdiff - Accumulated cloud fraction tendency difference 200242: - alwdiff - Accumulated liquid water tendency difference 200243: - faldiff - Forecast albedo difference 200244: - fsrdiff - Forecast surface roughness difference 200245: - flsrdiff - Forecast logarithm of surface roughness for heat difference 200246: - clwcdiff - Specific cloud liquid water content difference 200247: - ciwcdiff - Specific cloud ice water content difference 200248: - ccdiff - Cloud cover difference 200249: - aiwdiff - Accumulated ice water tendency difference 200250: - icediff - Ice age difference 200251: - attediff - Adiabatic tendency of temperature difference 200252: - athediff - Adiabatic tendency of humidity difference 200253: - atzediff - Adiabatic tendency of zonal wind difference 200254: - atmwdiff - Adiabatic tendency of meridional wind difference 200255: - _param_200255 - Indicates a missing value 201001: - _param_201001 - downward shortwave radiant flux density 201002: - _param_201002 - upward shortwave radiant flux density 201003: - _param_201003 - downward longwave radiant flux density 201004: - _param_201004 - upward longwave radiant flux density 201005: - apab_s - downwd photosynthetic active radiant flux density 201006: - _param_201006 - net shortwave flux 201007: - _param_201007 - net longwave flux 201008: - _param_201008 - total net radiative flux density 201009: - _param_201009 - downw shortw radiant flux density, cloudfree part 201010: - _param_201010 - upw shortw radiant flux density, cloudy part 201011: - _param_201011 - downw longw radiant flux density, cloudfree part 201012: - _param_201012 - upw longw radiant flux density, cloudy part 201013: - sohr_rad - shortwave radiative heating rate 201014: - thhr_rad - longwave radiative heating rate 201015: - _param_201015 - total radiative heating rate 201016: - _param_201016 - soil heat flux, surface 201017: - _param_201017 - soil heat flux, bottom of layer 201029: - clc - fractional cloud cover 201030: - _param_201030 - cloud cover, grid scale 201031: - qc - specific cloud water content 201032: - _param_201032 - cloud water content, grid scale, vert integrated 201033: - qi - specific cloud ice content, grid scale 201034: - _param_201034 - cloud ice content, grid scale, vert integrated 201035: - _param_201035 - specific rainwater content, grid scale 201036: - _param_201036 - specific snow content, grid scale 201037: - _param_201037 - specific rainwater content, gs, vert. integrated 201038: - _param_201038 - specific snow content, gs, vert. integrated 201041: - twater - total column water 201042: - _param_201042 - vert. integral of divergence of tot. water content 201050: - ch_cm_cl - cloud covers CH_CM_CL (000...888) 201051: - _param_201051 - cloud cover CH (0..8) 201052: - _param_201052 - cloud cover CM (0..8) 201053: - _param_201053 - cloud cover CL (0..8) 201054: - _param_201054 - total cloud cover (0..8) 201055: - _param_201055 - fog (0..8) 201056: - _param_201056 - fog 201060: - _param_201060 - cloud cover, convective cirrus 201061: - _param_201061 - specific cloud water content, convective clouds 201062: - _param_201062 - cloud water content, conv clouds, vert integrated 201063: - _param_201063 - specific cloud ice content, convective clouds 201064: - _param_201064 - cloud ice content, conv clouds, vert integrated 201065: - _param_201065 - convective mass flux 201066: - _param_201066 - Updraft velocity, convection 201067: - _param_201067 - entrainment parameter, convection 201068: - hbas_con - cloud base, convective clouds (above msl) 201069: - htop_con - cloud top, convective clouds (above msl) 201070: - _param_201070 - convective layers (00...77) (BKE) 201071: - _param_201071 - KO-index 201072: - bas_con - convection base index 201073: - top_con - convection top index 201074: - dt_con - convective temperature tendency 201075: - dqv_con - convective tendency of specific humidity 201076: - _param_201076 - convective tendency of total heat 201077: - _param_201077 - convective tendency of total water 201078: - du_con - convective momentum tendency (X-component) 201079: - dv_con - convective momentum tendency (Y-component) 201080: - _param_201080 - convective vorticity tendency 201081: - _param_201081 - convective divergence tendency 201082: - htop_dc - top of dry convection (above msl) 201083: - _param_201083 - dry convection top index 201084: - hzerocl - height of 0 degree Celsius isotherm above msl 201085: - snowlmt - height of snow-fall limit 201099: - qrs_gsp - spec. content of precip. particles 201100: - prr_gsp - surface precipitation rate, rain, grid scale 201101: - prs_gsp - surface precipitation rate, snow, grid scale 201102: - rain_gsp - surface precipitation amount, rain, grid scale 201111: - prr_con - surface precipitation rate, rain, convective 201112: - prs_con - surface precipitation rate, snow, convective 201113: - rain_con - surface precipitation amount, rain, convective 201150: - _param_201150 - coefficient of horizontal diffusion 201187: - vmax_10m - Maximum wind velocity 201200: - w_i - water content of interception store 201203: - t_snow - snow temperature 201215: - t_ice - ice surface temperature 201241: - cape_con - convective available potential energy 201255: - _param_201255 - Indicates a missing value 210001: - aermr01 - Sea Salt Aerosol (0.03 - 0.5 um) Mixing Ratio 210002: - aermr02 - Sea Salt Aerosol (0.5 - 5 um) Mixing Ratio 210003: - aermr03 - Sea Salt Aerosol (5 - 20 um) Mixing Ratio 210004: - aermr04 - Dust Aerosol (0.03 - 0.55 um) Mixing Ratio 210005: - aermr05 - Dust Aerosol (0.55 - 0.9 um) Mixing Ratio 210006: - aermr06 - Dust Aerosol (0.9 - 20 um) Mixing Ratio 210007: - aermr07 - Hydrophilic Organic Matter Aerosol Mixing Ratio 210008: - aermr08 - Hydrophobic Organic Matter Aerosol Mixing Ratio 210009: - aermr09 - Hydrophilic Black Carbon Aerosol Mixing Ratio 210010: - aermr10 - Hydrophobic Black Carbon Aerosol Mixing Ratio 210011: - aermr11 - Sulphate Aerosol Mixing Ratio 210012: - aermr12 - SO2 precursor mixing ratio 210013: - aermr13 - Volcanic ash aerosol mixing ratio 210014: - aermr14 - Volcanic sulphate aerosol mixing ratio 210015: - aermr15 - Volcanic SO2 precursor mixing ratio 210016: - aergn01 - Aerosol type 1 source/gain accumulated 210017: - aergn02 - Aerosol type 2 source/gain accumulated 210018: - aergn03 - Aerosol type 3 source/gain accumulated 210019: - aergn04 - Aerosol type 4 source/gain accumulated 210020: - aergn05 - Aerosol type 5 source/gain accumulated 210021: - aergn06 - Aerosol type 6 source/gain accumulated 210022: - aergn07 - Aerosol type 7 source/gain accumulated 210023: - aergn08 - Aerosol type 8 source/gain accumulated 210024: - aergn09 - Aerosol type 9 source/gain accumulated 210025: - aergn10 - Aerosol type 10 source/gain accumulated 210026: - aergn11 - Aerosol type 11 source/gain accumulated 210027: - aergn12 - Aerosol type 12 source/gain accumulated 210028: - aerpr03 - SO4 aerosol precursor mass mixing ratio 210029: - aerwv01 - Water vapour mixing ratio for hydrophilic aerosols in mode 1 210030: - aerwv02 - Water vapour mixing ratio for hydrophilic aerosols in mode 2 210031: - aerls01 - Aerosol type 1 sink/loss accumulated 210032: - aerls02 - Aerosol type 2 sink/loss accumulated 210033: - aerls03 - Aerosol type 3 sink/loss accumulated 210034: - aerls04 - Aerosol type 4 sink/loss accumulated 210035: - aerls05 - Aerosol type 5 sink/loss accumulated 210036: - aerls06 - Aerosol type 6 sink/loss accumulated 210037: - aerls07 - Aerosol type 7 sink/loss accumulated 210038: - aerls08 - Aerosol type 8 sink/loss accumulated 210039: - aerls09 - Aerosol type 9 sink/loss accumulated 210040: - aerls10 - Aerosol type 10 sink/loss accumulated 210041: - aerls11 - Aerosol type 11 sink/loss accumulated 210042: - aerls12 - Aerosol type 12 sink/loss accumulated 210043: - emdms - DMS surface emission 210044: - aerwv03 - Water vapour mixing ratio for hydrophilic aerosols in mode 3 210045: - aerwv04 - Water vapour mixing ratio for hydrophilic aerosols in mode 4 210046: - aerpr - Aerosol precursor mixing ratio 210047: - aersm - Aerosol small mode mixing ratio 210048: - aerlg - Aerosol large mode mixing ratio 210049: - aodpr - Aerosol precursor optical depth 210050: - aodsm - Aerosol small mode optical depth 210051: - aodlg - Aerosol large mode optical depth 210052: - aerdep - Dust emission potential 210053: - aerlts - Lifting threshold speed 210054: - aerscc - Soil clay content 210055: - _param_210055 - Experimental product 210056: - _param_210056 - Experimental product 210057: - ocnuc - Mixing ration of organic carbon aerosol, nucleation mode 210058: - monot - Monoterpene precursor mixing ratio 210059: - soapr - Secondary organic precursor mixing ratio 210060: - injh - Injection height (from IS4FIRES) 210061: - co2 - Carbon dioxide mass mixing ratio 210062: - ch4 - Methane 210063: - n2o - Nitrous oxide 210064: - tcco2 - CO2 column-mean molar fraction 210065: - tcch4 - CH4 column-mean molar fraction 210066: - tcn2o - Total column Nitrous oxide 210067: - co2of - Ocean flux of Carbon Dioxide 210068: - co2nbf - Natural biosphere flux of Carbon Dioxide 210069: - co2apf - Anthropogenic emissions of Carbon Dioxide 210070: - ch4f - Methane Surface Fluxes 210071: - kch4 - Methane loss rate due to radical hydroxyl (OH) 210072: - pm1 - Particulate matter d <= 1 um 210073: - pm2p5 - Particulate matter d <= 2.5 um 210074: - pm10 - Particulate matter d <= 10 um 210079: - vafire - Wildfire viewing angle of observation 210080: - co2fire - Wildfire flux of Carbon Dioxide 210081: - cofire - Wildfire flux of Carbon Monoxide 210082: - ch4fire - Wildfire flux of Methane 210083: - nmhcfire - Wildfire flux of Non-Methane Hydro-Carbons 210084: - h2fire - Wildfire flux of Hydrogen 210085: - noxfire - Wildfire flux of Nitrogen Oxides NOx 210086: - n2ofire - Wildfire flux of Nitrous Oxide 210087: - pm2p5fire - Wildfire flux of Particulate Matter PM2.5 210088: - tpmfire - Wildfire flux of Total Particulate Matter 210089: - tcfire - Wildfire flux of Total Carbon in Aerosols 210090: - ocfire - Wildfire flux of Organic Carbon 210091: - bcfire - Wildfire flux of Black Carbon 210092: - cfire - Wildfire overall flux of burnt Carbon 210093: - c4ffire - Wildfire fraction of C4 plants 210094: - vegfire - Wildfire vegetation map index 210095: - ccfire - Wildfire Combustion Completeness 210096: - flfire - 'Wildfire Fuel Load: Carbon per unit area' 210097: - offire - Wildfire fraction of area observed 210098: - nofrp - Number of positive FRP pixels per grid cell 210099: - frpfire - Wildfire radiative power 210100: - crfire - Wildfire combustion rate 210101: - maxfrpfire - Wildfire radiative power maximum 210102: - so2fire - Wildfire flux of Sulfur Dioxide 210103: - ch3ohfire - Wildfire Flux of Methanol (CH3OH) 210104: - c2h5ohfire - Wildfire Flux of Ethanol (C2H5OH) 210105: - c3h8fire - Wildfire Flux of Propane (C3H8) 210106: - c2h4fire - Wildfire Flux of Ethene (C2H4) 210107: - c3h6fire - Wildfire Flux of Propene (C3H6) 210108: - c5h8fire - Wildfire Flux of Isoprene (C5H8) 210109: - terpenesfire - Wildfire Flux of Terpenes (C5H8)n 210110: - toluenefire - Wildfire Flux of Toluene_lump (C7H8+ C6H6 + C8H10) 210111: - hialkenesfire - Wildfire Flux of Higher Alkenes (CnH2n, C>=4) 210112: - hialkanesfire - Wildfire Flux of Higher Alkanes (CnH2n+2, C>=4) 210113: - ch2ofire - Wildfire Flux of Formaldehyde (CH2O) 210114: - c2h4ofire - Wildfire Flux of Acetaldehyde (C2H4O) 210115: - c3h6ofire - Wildfire Flux of Acetone (C3H6O) 210116: - nh3fire - Wildfire Flux of Ammonia (NH3) 210117: - c2h6sfire - Wildfire Flux of Dimethyl Sulfide (DMS) (C2H6S) 210118: - c2h6fire - Wildfire Flux of Ethane (C2H6) 210119: - mami - Mean height of maximum injection 210120: - apt - Plume top height above surface 210121: - no2 - Nitrogen dioxide mass mixing ratio 210122: - so2 - Sulphur dioxide mass mixing ratio 210123: - co - Carbon monoxide mass mixing ratio 210124: - hcho - Formaldehyde 210125: - tcno2 - Total column Nitrogen dioxide 210126: - tcso2 - Total column Sulphur dioxide 210127: - tcco - Total column Carbon monoxide 210128: - tchcho - Total column Formaldehyde 210129: - nox - Nitrogen Oxides 210130: - tcnox - Total Column Nitrogen Oxides 210131: - grg1 - Reactive tracer 1 mass mixing ratio 210132: - tcgrg1 - Total column GRG tracer 1 210133: - grg2 - Reactive tracer 2 mass mixing ratio 210134: - tcgrg2 - Total column GRG tracer 2 210135: - grg3 - Reactive tracer 3 mass mixing ratio 210136: - tcgrg3 - Total column GRG tracer 3 210137: - grg4 - Reactive tracer 4 mass mixing ratio 210138: - tcgrg4 - Total column GRG tracer 4 210139: - grg5 - Reactive tracer 5 mass mixing ratio 210140: - tcgrg5 - Total column GRG tracer 5 210141: - grg6 - Reactive tracer 6 mass mixing ratio 210142: - tcgrg6 - Total column GRG tracer 6 210143: - grg7 - Reactive tracer 7 mass mixing ratio 210144: - tcgrg7 - Total column GRG tracer 7 210145: - grg8 - Reactive tracer 8 mass mixing ratio 210146: - tcgrg8 - Total column GRG tracer 8 210147: - grg9 - Reactive tracer 9 mass mixing ratio 210148: - tcgrg9 - Total column GRG tracer 9 210149: - grg10 - Reactive tracer 10 mass mixing ratio 210150: - tcgrg10 - Total column GRG tracer 10 210151: - sfnox - Surface flux Nitrogen oxides 210152: - sfno2 - Surface flux Nitrogen dioxide 210153: - sfso2 - Surface flux Sulphur dioxide 210154: - sfco2 - Surface flux Carbon monoxide 210155: - sfhcho - Surface flux Formaldehyde 210156: - sfgo3 - Surface flux GEMS Ozone 210157: - sfgr1 - Surface flux reactive tracer 1 210158: - sfgr2 - Surface flux reactive tracer 2 210159: - sfgr3 - Surface flux reactive tracer 3 210160: - sfgr4 - Surface flux reactive tracer 4 210161: - sfgr5 - Surface flux reactive tracer 5 210162: - sfgr6 - Surface flux reactive tracer 6 210163: - sfgr7 - Surface flux reactive tracer 7 210164: - sfgr8 - Surface flux reactive tracer 8 210165: - sfgr9 - Surface flux reactive tracer 9 210166: - sfgr10 - Surface flux reactive tracer 10 210167: - frpdayfire - Wildfire day-time radiative power 210169: - frpngtfire - Wildfire night-time radiative power 210170: - vso2 - Volcanic sulfur dioxide mass mixing ratio 210177: - frpdayivar - Wildfire day-time inverse variance of radiative power 210179: - frpngtivar - Wildfire night-time inverse variance of radiative power 210181: - ra - Radon 210182: - sf6 - Sulphur Hexafluoride 210183: - tcra - Total column Radon 210184: - tcsf6 - Total column Sulphur Hexafluoride 210185: - sf6apf - Anthropogenic Emissions of Sulphur Hexafluoride 210186: - aluvpi - UV visible albedo for direct radiation, isotropic component (climatological) 210187: - aluvpv - UV visible albedo for direct radiation, volumetric component (climatological) 210188: - aluvpg - UV visible albedo for direct radiation, geometric component (climatological) 210189: - alnipi - Near IR albedo for direct radiation, isotropic component (climatological) 210190: - alnipv - Near IR albedo for direct radiation, volumetric component (climatological) 210191: - alnipg - Near IR albedo for direct radiation, geometric component (climatological) 210192: - aluvdi - UV visible albedo for diffuse radiation, isotropic component (climatological) 210193: - aluvdv - UV visible albedo for diffuse radiation, volumetric component (climatological) 210194: - aluvdg - UV visible albedo for diffuse radiation, geometric component (climatological) 210195: - alnidi - Near IR albedo for diffuse radiation, isotropic component (climatological) 210196: - alnidv - Near IR albedo for diffuse radiation, volumetric component (climatological) 210197: - alnidg - Near IR albedo for diffuse radiation, geometric component (climatological) 210198: - aluvd_p - UV visible albedo for diffuse radiation (climatological) 210199: - aluvp_p - UV visible albedo for direct radiation (climatological) 210200: - aluvpg_p - UV visible albedo for direct radiation, geometric component (climatological) 210201: - aluvpi_p - UV visible albedo for direct radiation, isotropic component (climatological) 210202: - aluvpv_p - UV visible albedo for direct radiation, volumetric component (climatological) 210203: - go3 - Ozone mass mixing ratio (full chemistry scheme) 210206: - gtco3 - GEMS Total column ozone 210207: - aod550 - Total Aerosol Optical Depth at 550nm 210208: - ssaod550 - Sea Salt Aerosol Optical Depth at 550nm 210209: - duaod550 - Dust Aerosol Optical Depth at 550nm 210210: - omaod550 - Organic Matter Aerosol Optical Depth at 550nm 210211: - bcaod550 - Black Carbon Aerosol Optical Depth at 550nm 210212: - suaod550 - Sulphate Aerosol Optical Depth at 550nm 210213: - aod469 - Total Aerosol Optical Depth at 469nm 210214: - aod670 - Total Aerosol Optical Depth at 670nm 210215: - aod865 - Total Aerosol Optical Depth at 865nm 210216: - aod1240 - Total Aerosol Optical Depth at 1240nm 210217: - aod340 - Total aerosol optical depth at 340 nm 210218: - aod355 - Total aerosol optical depth at 355 nm 210219: - aod380 - Total aerosol optical depth at 380 nm 210220: - aod400 - Total aerosol optical depth at 400 nm 210221: - aod440 - Total aerosol optical depth at 440 nm 210222: - aod500 - Total aerosol optical depth at 500 nm 210223: - aod532 - Total aerosol optical depth at 532 nm 210224: - aod645 - Total aerosol optical depth at 645 nm 210225: - aod800 - Total aerosol optical depth at 800 nm 210226: - aod858 - Total aerosol optical depth at 858 nm 210227: - aod1020 - Total aerosol optical depth at 1020 nm 210228: - aod1064 - Total aerosol optical depth at 1064 nm 210229: - aod1640 - Total aerosol optical depth at 1640 nm 210230: - aod2130 - Total aerosol optical depth at 2130 nm 210231: - c7h8fire - Wildfire Flux of Toluene (C7H8) 210232: - c6h6fire - Wildfire Flux of Benzene (C6H6) 210233: - c8h10fire - Wildfire Flux of Xylene (C8H10) 210234: - c4h8fire - Wildfire Flux of Butenes (C4H8) 210235: - c5h10fire - Wildfire Flux of Pentenes (C5H10) 210236: - c6h12fire - Wildfire Flux of Hexene (C6H12) 210237: - c8h16fire - Wildfire Flux of Octene (C8H16) 210238: - c4h10fire - Wildfire Flux of Butanes (C4H10) 210239: - c5h12fire - Wildfire Flux of Pentanes (C5H12) 210240: - c6h14fire - Wildfire Flux of Hexanes (C6H14) 210241: - c7h16fire - Wildfire Flux of Heptane (C7H16) 210242: - apb - Plume bottom height above surface 210243: - vsuaod550 - Volcanic sulphate aerosol optical depth at 550 nm 210244: - vashaod550 - Volcanic ash optical depth at 550 nm 210245: - taedec550 - Profile of total aerosol dry extinction coefficient 210246: - taedab550 - Profile of total aerosol dry absorption coefficient 210247: - aermr16 - Nitrate fine mode aerosol mass mixing ratio 210248: - aermr17 - Nitrate coarse mode aerosol mass mixing ratio 210249: - aermr18 - Ammonium aerosol mass mixing ratio 210250: - niaod550 - Nitrate aerosol optical depth at 550 nm 210251: - amaod550 - Ammonium aerosol optical depth at 550 nm 210252: - aermr19 - Biogenic secondary organic aerosol mass mixing ratio 210253: - aermr20 - Anthropogenic secondary organic aerosol mass mixing ratio 210260: - alnid_p - Near IR albedo for diffuse radiation (climatological) 210261: - alnip_p - Near IR albedo for direct radiation (climatological) 210262: - alnipg_p - Near IR albedo for direct radiation, geometric component (climatological) 210263: - alnipi_p - Near IR albedo for direct radiation, isotropic component (climatological) 210264: - alnipv_p - Near IR albedo for direct radiation, volumetric component (climatological) 211001: - aermr01diff - Sea Salt Aerosol (0.03 - 0.5 um) Mixing Ratio 211002: - aermr02diff - Sea Salt Aerosol (0.5 - 5 um) Mixing Ratio 211003: - aermr03diff - Sea Salt Aerosol (5 - 20 um) Mixing Ratio 211004: - aermr04diff - Dust Aerosol (0.03 - 0.55 um) Mixing Ratio 211005: - aermr05diff - Dust Aerosol (0.55 - 0.9 um) Mixing Ratio 211006: - aermr06diff - Dust Aerosol (0.9 - 20 um) Mixing Ratio 211007: - aermr07diff - Hydrophilic Organic Matter Aerosol Mixing Ratio 211008: - aermr08diff - Hydrophobic Organic Matter Aerosol Mixing Ratio 211009: - aermr09diff - Hydrophilic Black Carbon Aerosol Mixing Ratio 211010: - aermr10diff - Hydrophobic Black Carbon Aerosol Mixing Ratio 211011: - aermr11diff - Sulphate Aerosol Mixing Ratio 211012: - aermr12diff - Aerosol type 12 mixing ratio 211013: - aermr13diff - Aerosol type 13 mass mixing ratio 211014: - aermr14diff - Aerosol type 14 mass mixing ratio 211015: - aermr15diff - Aerosol type 15 mass mixing ratio 211016: - aergn01diff - Aerosol type 1 source/gain accumulated 211017: - aergn02diff - Aerosol type 2 source/gain accumulated 211018: - aergn03diff - Aerosol type 3 source/gain accumulated 211019: - aergn04diff - Aerosol type 4 source/gain accumulated 211020: - aergn05diff - Aerosol type 5 source/gain accumulated 211021: - aergn06diff - Aerosol type 6 source/gain accumulated 211022: - aergn07diff - Aerosol type 7 source/gain accumulated 211023: - aergn08diff - Aerosol type 8 source/gain accumulated 211024: - aergn09diff - Aerosol type 9 source/gain accumulated 211025: - aergn10diff - Aerosol type 10 source/gain accumulated 211026: - aergn11diff - Aerosol type 11 source/gain accumulated 211027: - aergn12diff - Aerosol type 12 source/gain accumulated 211028: - aerpr03diff - SO4 aerosol precursor mass mixing ratio 211029: - aerwv01diff - Water vapour mixing ratio for hydrophilic aerosols in mode 1 211030: - aerwv02diff - Water vapour mixing ratio for hydrophilic aerosols in mode 2 211031: - aerls01diff - Aerosol type 1 sink/loss accumulated 211032: - aerls02diff - Aerosol type 2 sink/loss accumulated 211033: - aerls03diff - Aerosol type 3 sink/loss accumulated 211034: - aerls04diff - Aerosol type 4 sink/loss accumulated 211035: - aerls05diff - Aerosol type 5 sink/loss accumulated 211036: - aerls06diff - Aerosol type 6 sink/loss accumulated 211037: - aerls07diff - Aerosol type 7 sink/loss accumulated 211038: - aerls08diff - Aerosol type 8 sink/loss accumulated 211039: - aerls09diff - Aerosol type 9 sink/loss accumulated 211040: - aerls10diff - Aerosol type 10 sink/loss accumulated 211041: - aerls11diff - Aerosol type 11 sink/loss accumulated 211042: - aerls12diff - Aerosol type 12 sink/loss accumulated 211043: - emdmsdiff - DMS surface emission 211044: - aerwv03diff - Water vapour mixing ratio for hydrophilic aerosols in mode 3 211045: - aerwv04diff - Water vapour mixing ratio for hydrophilic aerosols in mode 4 211046: - aerprdiff - Aerosol precursor mixing ratio 211047: - aersmdiff - Aerosol small mode mixing ratio 211048: - aerlgdiff - Aerosol large mode mixing ratio 211049: - aodprdiff - Aerosol precursor optical depth 211050: - aodsmdiff - Aerosol small mode optical depth 211051: - aodlgdiff - Aerosol large mode optical depth 211052: - aerdepdiff - Dust emission potential 211053: - aerltsdiff - Lifting threshold speed 211054: - aersccdiff - Soil clay content 211055: - _param_211055 - Experimental product 211056: - _param_211056 - Experimental product 211059: - h2odiff - Water vapour (chemistry) difference 211061: - co2diff - Carbon Dioxide 211062: - ch4diff - Methane 211063: - n2odiff - Nitrous oxide 211064: - tcco2diff - Total column Carbon Dioxide 211065: - tcch4diff - Total column Methane 211066: - tcn2odiff - Total column Nitrous oxide 211067: - co2ofdiff - Ocean flux of Carbon Dioxide 211068: - co2nbfdiff - Natural biosphere flux of Carbon Dioxide 211069: - co2apfdiff - Anthropogenic emissions of Carbon Dioxide 211070: - ch4fdiff - Methane Surface Fluxes 211071: - kch4diff - Methane loss rate due to radical hydroxyl (OH) 211080: - co2firediff - Wildfire flux of Carbon Dioxide 211081: - cofirediff - Wildfire flux of Carbon Monoxide 211082: - ch4firediff - Wildfire flux of Methane 211083: - nmhcfirediff - Wildfire flux of Non-Methane Hydro-Carbons 211084: - h2firediff - Wildfire flux of Hydrogen 211085: - noxfirediff - Wildfire flux of Nitrogen Oxides NOx 211086: - n2ofirediff - Wildfire flux of Nitrous Oxide 211087: - pm2p5firediff - Wildfire flux of Particulate Matter PM2.5 211088: - tpmfirediff - Wildfire flux of Total Particulate Matter 211089: - tcfirediff - Wildfire flux of Total Carbon in Aerosols 211090: - ocfirediff - Wildfire flux of Organic Carbon 211091: - bcfirediff - Wildfire flux of Black Carbon 211092: - cfirediff - Wildfire overall flux of burnt Carbon 211093: - c4ffirediff - Wildfire fraction of C4 plants 211094: - vegfirediff - Wildfire vegetation map index 211095: - ccfirediff - Wildfire Combustion Completeness 211096: - flfirediff - 'Wildfire Fuel Load: Carbon per unit area' 211097: - offirediff - Wildfire fraction of area observed 211098: - oafirediff - Wildfire observed area 211099: - frpfirediff - Wildfire radiative power 211100: - crfirediff - Wildfire combustion rate 211101: - maxfrpfirediff - Wildfire radiative power maximum 211102: - so2firediff - Wildfire flux of Sulfur Dioxide 211103: - ch3ohfirediff - Wildfire Flux of Methanol (CH3OH) 211104: - c2h5ohfirediff - Wildfire Flux of Ethanol (C2H5OH) 211105: - c3h8firediff - Wildfire Flux of Propane (C3H8) 211106: - c2h4firediff - Wildfire Flux of Ethene (C2H4) 211107: - c3h6firediff - Wildfire Flux of Propene (C3H6) 211108: - c5h8firediff - Wildfire Flux of Isoprene (C5H8) 211109: - terpenesfirediff - Wildfire Flux of Terpenes (C5H8)n 211110: - toluenefirediff - Wildfire Flux of Toluene_lump (C7H8+ C6H6 + C8H10) 211111: - hialkenesfirediff - Wildfire Flux of Higher Alkenes (CnH2n, C>=4) 211112: - hialkanesfirediff - Wildfire Flux of Higher Alkanes (CnH2n+2, C>=4) 211113: - ch2ofirediff - Wildfire Flux of Formaldehyde (CH2O) 211114: - c2h4ofirediff - Wildfire Flux of Acetaldehyde (C2H4O) 211115: - c3h6ofirediff - Wildfire Flux of Acetone (C3H6O) 211116: - nh3firediff - Wildfire Flux of Ammonia (NH3) 211117: - c2h6sfirediff - Wildfire Flux of Dimethyl Sulfide (DMS) (C2H6S) 211118: - c2h6firediff - Wildfire Flux of Ethane (C2H6) 211119: - alediff - Altitude of emitter 211120: - aptdiff - Altitude of plume top 211121: - no2diff - Nitrogen dioxide mass mixing ratio difference 211122: - so2diff - Sulphur dioxide mass mixing ratio difference 211123: - codiff - Carbon monoxide mass mixing ratio difference 211124: - hchodiff - Formaldehyde 211125: - tcno2diff - Total column Nitrogen dioxide 211126: - tcso2diff - Total column Sulphur dioxide 211127: - tccodiff - Total column Carbon monoxide 211128: - tchchodiff - Total column Formaldehyde 211129: - noxdiff - Nitrogen Oxides 211130: - tcnoxdiff - Total Column Nitrogen Oxides 211131: - grg1diff - Reactive tracer 1 mass mixing ratio 211132: - tcgrg1diff - Total column GRG tracer 1 211133: - grg2diff - Reactive tracer 2 mass mixing ratio 211134: - tcgrg2diff - Total column GRG tracer 2 211135: - grg3diff - Reactive tracer 3 mass mixing ratio 211136: - tcgrg3diff - Total column GRG tracer 3 211137: - grg4diff - Reactive tracer 4 mass mixing ratio 211138: - tcgrg4diff - Total column GRG tracer 4 211139: - grg5diff - Reactive tracer 5 mass mixing ratio 211140: - tcgrg5diff - Total column GRG tracer 5 211141: - grg6diff - Reactive tracer 6 mass mixing ratio 211142: - tcgrg6diff - Total column GRG tracer 6 211143: - grg7diff - Reactive tracer 7 mass mixing ratio 211144: - tcgrg7diff - Total column GRG tracer 7 211145: - grg8diff - Reactive tracer 8 mass mixing ratio 211146: - tcgrg8diff - Total column GRG tracer 8 211147: - grg9diff - Reactive tracer 9 mass mixing ratio 211148: - tcgrg9diff - Total column GRG tracer 9 211149: - grg10diff - Reactive tracer 10 mass mixing ratio 211150: - tcgrg10diff - Total column GRG tracer 10 211151: - sfnoxdiff - Surface flux Nitrogen oxides 211152: - sfno2diff - Surface flux Nitrogen dioxide 211153: - sfso2diff - Surface flux Sulphur dioxide 211154: - sfco2diff - Surface flux Carbon monoxide 211155: - sfhchodiff - Surface flux Formaldehyde 211156: - sfgo3diff - Surface flux GEMS Ozone 211157: - sfgr1diff - Surface flux reactive tracer 1 211158: - sfgr2diff - Surface flux reactive tracer 2 211159: - sfgr3diff - Surface flux reactive tracer 3 211160: - sfgr4diff - Surface flux reactive tracer 4 211161: - sfgr5diff - Surface flux reactive tracer 5 211162: - sfgr6diff - Surface flux reactive tracer 6 211163: - sfgr7diff - Surface flux reactive tracer 7 211164: - sfgr8diff - Surface flux reactive tracer 8 211165: - sfgr9diff - Surface flux reactive tracer 9 211166: - sfgr10diff - Surface flux reactive tracer 10 211170: - vso2diff - Volcanic sulfur dioxide mass mixing ratio increment 211181: - radiff - Radon 211182: - sf6diff - Sulphur Hexafluoride 211183: - tcradiff - Total column Radon 211184: - tcsf6diff - Total column Sulphur Hexafluoride 211185: - sf6apfdiff - Anthropogenic Emissions of Sulphur Hexafluoride 211203: - go3diff - Ozone mass mixing ratio difference (full chemistry scheme) 211206: - gtco3diff - GEMS Total column ozone 211207: - aod550diff - Total Aerosol Optical Depth at 550nm 211208: - ssaod550diff - Sea Salt Aerosol Optical Depth at 550nm 211209: - duaod550diff - Dust Aerosol Optical Depth at 550nm 211210: - omaod550diff - Organic Matter Aerosol Optical Depth at 550nm 211211: - bcaod550diff - Black Carbon Aerosol Optical Depth at 550nm 211212: - suaod550diff - Sulphate Aerosol Optical Depth at 550nm 211213: - aod469diff - Total Aerosol Optical Depth at 469nm 211214: - aod670diff - Total Aerosol Optical Depth at 670nm 211215: - aod865diff - Total Aerosol Optical Depth at 865nm 211216: - aod1240diff - Total Aerosol Optical Depth at 1240nm 211247: - aermr16diff - Nitrate fine mode aerosol mass mixing ratio 211248: - aermr17diff - Nitrate coarse mode aerosol mass mixing ratio 211249: - aermr18diff - Ammonium aerosol mass mixing ratio 211252: - aermr19diff - Biogenic secondary organic aerosol mass mixing ratio increment 211253: - aermr20diff - Anthropogenic secondary organic aerosol mass mixing ratio increment 212001: - _param_212001 - Experimental product 212002: - _param_212002 - Experimental product 212003: - _param_212003 - Experimental product 212004: - _param_212004 - Experimental product 212005: - _param_212005 - Experimental product 212006: - _param_212006 - Experimental product 212007: - _param_212007 - Experimental product 212008: - _param_212008 - Experimental product 212009: - _param_212009 - Experimental product 212010: - _param_212010 - Experimental product 212011: - _param_212011 - Experimental product 212012: - _param_212012 - Experimental product 212013: - _param_212013 - Experimental product 212014: - _param_212014 - Experimental product 212015: - _param_212015 - Experimental product 212016: - _param_212016 - Experimental product 212017: - _param_212017 - Experimental product 212018: - _param_212018 - Experimental product 212019: - _param_212019 - Experimental product 212020: - _param_212020 - Experimental product 212021: - _param_212021 - Experimental product 212022: - _param_212022 - Experimental product 212023: - _param_212023 - Experimental product 212024: - _param_212024 - Experimental product 212025: - _param_212025 - Experimental product 212026: - _param_212026 - Experimental product 212027: - _param_212027 - Experimental product 212028: - _param_212028 - Experimental product 212029: - _param_212029 - Experimental product 212030: - _param_212030 - Experimental product 212031: - _param_212031 - Experimental product 212032: - _param_212032 - Experimental product 212033: - _param_212033 - Experimental product 212034: - _param_212034 - Experimental product 212035: - _param_212035 - Experimental product 212036: - _param_212036 - Experimental product 212037: - _param_212037 - Experimental product 212038: - _param_212038 - Experimental product 212039: - _param_212039 - Experimental product 212040: - _param_212040 - Experimental product 212041: - _param_212041 - Experimental product 212042: - _param_212042 - Experimental product 212043: - _param_212043 - Experimental product 212044: - _param_212044 - Experimental product 212045: - _param_212045 - Experimental product 212046: - _param_212046 - Experimental product 212047: - _param_212047 - Experimental product 212048: - _param_212048 - Experimental product 212049: - _param_212049 - Experimental product 212050: - _param_212050 - Experimental product 212051: - _param_212051 - Experimental product 212052: - _param_212052 - Experimental product 212053: - _param_212053 - Experimental product 212054: - _param_212054 - Experimental product 212055: - _param_212055 - Experimental product 212056: - _param_212056 - Experimental product 212057: - _param_212057 - Experimental product 212058: - _param_212058 - Experimental product 212059: - _param_212059 - Experimental product 212060: - _param_212060 - Experimental product 212061: - _param_212061 - Experimental product 212062: - _param_212062 - Experimental product 212063: - _param_212063 - Experimental product 212064: - _param_212064 - Experimental product 212065: - _param_212065 - Experimental product 212066: - _param_212066 - Experimental product 212067: - _param_212067 - Experimental product 212068: - _param_212068 - Experimental product 212069: - _param_212069 - Experimental product 212070: - _param_212070 - Experimental product 212071: - _param_212071 - Experimental product 212072: - _param_212072 - Experimental product 212073: - _param_212073 - Experimental product 212074: - _param_212074 - Experimental product 212075: - _param_212075 - Experimental product 212076: - _param_212076 - Experimental product 212077: - _param_212077 - Experimental product 212078: - _param_212078 - Experimental product 212079: - _param_212079 - Experimental product 212080: - _param_212080 - Experimental product 212081: - _param_212081 - Experimental product 212082: - _param_212082 - Experimental product 212083: - _param_212083 - Experimental product 212084: - _param_212084 - Experimental product 212085: - _param_212085 - Experimental product 212086: - _param_212086 - Experimental product 212087: - _param_212087 - Experimental product 212088: - _param_212088 - Experimental product 212089: - _param_212089 - Experimental product 212090: - _param_212090 - Experimental product 212091: - _param_212091 - Experimental product 212092: - _param_212092 - Experimental product 212093: - _param_212093 - Experimental product 212094: - _param_212094 - Experimental product 212095: - _param_212095 - Experimental product 212096: - _param_212096 - Experimental product 212097: - _param_212097 - Experimental product 212098: - _param_212098 - Experimental product 212099: - _param_212099 - Experimental product 212100: - _param_212100 - Experimental product 212101: - _param_212101 - Experimental product 212102: - _param_212102 - Experimental product 212103: - _param_212103 - Experimental product 212104: - _param_212104 - Experimental product 212105: - _param_212105 - Experimental product 212106: - _param_212106 - Experimental product 212107: - _param_212107 - Experimental product 212108: - _param_212108 - Experimental product 212109: - _param_212109 - Experimental product 212110: - _param_212110 - Experimental product 212111: - _param_212111 - Experimental product 212112: - _param_212112 - Experimental product 212113: - _param_212113 - Experimental product 212114: - _param_212114 - Experimental product 212115: - _param_212115 - Experimental product 212116: - _param_212116 - Experimental product 212117: - _param_212117 - Experimental product 212118: - _param_212118 - Experimental product 212119: - _param_212119 - Experimental product 212120: - _param_212120 - Experimental product 212121: - _param_212121 - Experimental product 212122: - _param_212122 - Experimental product 212123: - _param_212123 - Experimental product 212124: - _param_212124 - Experimental product 212125: - _param_212125 - Experimental product 212126: - _param_212126 - Experimental product 212127: - _param_212127 - Experimental product 212128: - _param_212128 - Experimental product 212129: - _param_212129 - Experimental product 212130: - _param_212130 - Experimental product 212131: - _param_212131 - Experimental product 212132: - _param_212132 - Experimental product 212133: - _param_212133 - Experimental product 212134: - _param_212134 - Experimental product 212135: - _param_212135 - Experimental product 212136: - _param_212136 - Experimental product 212137: - _param_212137 - Experimental product 212138: - _param_212138 - Experimental product 212139: - _param_212139 - Experimental product 212140: - _param_212140 - Experimental product 212141: - _param_212141 - Experimental product 212142: - _param_212142 - Experimental product 212143: - _param_212143 - Experimental product 212144: - _param_212144 - Experimental product 212145: - _param_212145 - Experimental product 212146: - _param_212146 - Experimental product 212147: - _param_212147 - Experimental product 212148: - _param_212148 - Experimental product 212149: - _param_212149 - Experimental product 212150: - _param_212150 - Experimental product 212151: - _param_212151 - Experimental product 212152: - _param_212152 - Experimental product 212153: - _param_212153 - Experimental product 212154: - _param_212154 - Experimental product 212155: - _param_212155 - Experimental product 212156: - _param_212156 - Experimental product 212157: - _param_212157 - Experimental product 212158: - _param_212158 - Experimental product 212159: - _param_212159 - Experimental product 212160: - _param_212160 - Experimental product 212161: - _param_212161 - Experimental product 212162: - _param_212162 - Experimental product 212163: - _param_212163 - Experimental product 212164: - _param_212164 - Experimental product 212165: - _param_212165 - Experimental product 212166: - _param_212166 - Experimental product 212167: - _param_212167 - Experimental product 212168: - _param_212168 - Experimental product 212169: - _param_212169 - Experimental product 212170: - _param_212170 - Experimental product 212171: - _param_212171 - Experimental product 212172: - _param_212172 - Experimental product 212173: - _param_212173 - Experimental product 212174: - _param_212174 - Experimental product 212175: - _param_212175 - Experimental product 212176: - _param_212176 - Experimental product 212177: - _param_212177 - Experimental product 212178: - _param_212178 - Experimental product 212179: - _param_212179 - Experimental product 212180: - _param_212180 - Experimental product 212181: - _param_212181 - Experimental product 212182: - _param_212182 - Experimental product 212183: - _param_212183 - Experimental product 212184: - _param_212184 - Experimental product 212185: - _param_212185 - Experimental product 212186: - _param_212186 - Experimental product 212187: - _param_212187 - Experimental product 212188: - _param_212188 - Experimental product 212189: - _param_212189 - Experimental product 212190: - _param_212190 - Experimental product 212191: - _param_212191 - Experimental product 212192: - _param_212192 - Experimental product 212193: - _param_212193 - Experimental product 212194: - _param_212194 - Experimental product 212195: - _param_212195 - Experimental product 212196: - _param_212196 - Experimental product 212197: - _param_212197 - Experimental product 212198: - _param_212198 - Experimental product 212199: - _param_212199 - Experimental product 212200: - _param_212200 - Experimental product 212201: - _param_212201 - Experimental product 212202: - _param_212202 - Experimental product 212203: - _param_212203 - Experimental product 212204: - _param_212204 - Experimental product 212205: - _param_212205 - Experimental product 212206: - _param_212206 - Experimental product 212207: - _param_212207 - Experimental product 212208: - _param_212208 - Experimental product 212209: - _param_212209 - Experimental product 212210: - _param_212210 - Experimental product 212211: - _param_212211 - Experimental product 212212: - _param_212212 - Experimental product 212213: - _param_212213 - Experimental product 212214: - _param_212214 - Experimental product 212215: - _param_212215 - Experimental product 212216: - _param_212216 - Experimental product 212217: - _param_212217 - Experimental product 212218: - _param_212218 - Experimental product 212219: - _param_212219 - Experimental product 212220: - _param_212220 - Experimental product 212221: - _param_212221 - Experimental product 212222: - _param_212222 - Experimental product 212223: - _param_212223 - Experimental product 212224: - _param_212224 - Experimental product 212225: - _param_212225 - Experimental product 212226: - _param_212226 - Experimental product 212227: - _param_212227 - Experimental product 212228: - _param_212228 - Experimental product 212229: - _param_212229 - Experimental product 212230: - _param_212230 - Experimental product 212231: - _param_212231 - Experimental product 212232: - _param_212232 - Experimental product 212233: - _param_212233 - Experimental product 212234: - _param_212234 - Experimental product 212235: - _param_212235 - Experimental product 212236: - _param_212236 - Experimental product 212237: - _param_212237 - Experimental product 212238: - _param_212238 - Experimental product 212239: - _param_212239 - Experimental product 212240: - _param_212240 - Experimental product 212241: - _param_212241 - Experimental product 212242: - _param_212242 - Experimental product 212243: - _param_212243 - Experimental product 212244: - _param_212244 - Experimental product 212245: - _param_212245 - Experimental product 212246: - _param_212246 - Experimental product 212247: - _param_212247 - Experimental product 212248: - _param_212248 - Experimental product 212249: - _param_212249 - Experimental product 212250: - _param_212250 - Experimental product 212251: - _param_212251 - Experimental product 212252: - _param_212252 - Experimental product 212253: - _param_212253 - Experimental product 212254: - _param_212254 - Experimental product 212255: - _param_212255 - Experimental product 213001: - sppt1 - Random pattern 1 for sppt 213002: - sppt2 - Random pattern 2 for sppt 213003: - sppt3 - Random pattern 3 for sppt 213004: - sppt4 - Random pattern 4 for sppt 213005: - sppt5 - Random pattern 5 for sppt 213101: - spp1 - Random pattern 1 for SPP scheme 213102: - spp2 - Random pattern 2 for SPP scheme 213103: - spp3 - Random pattern 3 for SPP scheme 213104: - spp4 - Random pattern 4 for SPP scheme 213105: - spp5 - Random pattern 5 for SPP scheme 213106: - spp6 - Random pattern 6 for SPP scheme 213107: - spp7 - Random pattern 7 for SPP scheme 213108: - spp8 - Random pattern 8 for SPP scheme 213109: - spp9 - Random pattern 9 for SPP scheme 213110: - spp10 - Random pattern 10 for SPP scheme 213111: - spp11 - Random pattern 11 for SPP scheme 213112: - spp12 - Random pattern 12 for SPP scheme 213113: - spp13 - Random pattern 13 for SPP scheme 213114: - spp14 - Random pattern 14 for SPP scheme 213115: - spp15 - Random pattern 15 for SPP scheme 213116: - spp16 - Random pattern 16 for SPP scheme 213117: - spp17 - Random pattern 17 for SPP scheme 213118: - spp18 - Random pattern 18 for SPP scheme 213119: - spp19 - Random pattern 19 for SPP scheme 213120: - spp20 - Random pattern 20 for SPP scheme 213121: - spp21 - Random pattern 21 for SPP scheme 213122: - spp22 - Random pattern 22 for SPP scheme 213123: - spp23 - Random pattern 23 for SPP scheme 213124: - spp24 - Random pattern 24 for SPP scheme 213125: - spp25 - Random pattern 25 for SPP scheme 213126: - spp26 - Random pattern 26 for SPP scheme 213127: - spp27 - Random pattern 27 for SPP scheme 213128: - spp28 - Random pattern 28 for SPP scheme 213129: - spp29 - Random pattern 29 for SPP scheme 213130: - spp30 - Random pattern 30 for SPP scheme 213131: - spp31 - Random pattern 31 for SPP scheme 213132: - spp32 - Random pattern 32 for SPP scheme 213133: - spp33 - Random pattern 33 for SPP scheme 213134: - spp34 - Random pattern 34 for SPP scheme 213135: - spp35 - Random pattern 35 for SPP scheme 213136: - spp36 - Random pattern 36 for SPP scheme 213137: - spp37 - Random pattern 37 for SPP scheme 213138: - spp38 - Random pattern 38 for SPP scheme 213139: - spp39 - Random pattern 39 for SPP scheme 213140: - spp40 - Random pattern 40 for SPP scheme 213141: - spp41 - Random pattern 41 for SPP scheme 213142: - spp42 - Random pattern 42 for SPP scheme 213143: - spp43 - Random pattern 43 for SPP scheme 213144: - spp44 - Random pattern 44 for SPP scheme 213145: - spp45 - Random pattern 45 for SPP scheme 213146: - spp46 - Random pattern 46 for SPP scheme 213147: - spp47 - Random pattern 47 for SPP scheme 213148: - spp48 - Random pattern 48 for SPP scheme 213149: - spp49 - Random pattern 49 for SPP scheme 213150: - spp50 - Random pattern 50 for SPP scheme 213151: - spp51 - Random pattern 51 for SPP scheme 213152: - spp52 - Random pattern 52 for SPP scheme 213153: - spp53 - Random pattern 53 for SPP scheme 213154: - spp54 - Random pattern 54 for SPP scheme 213155: - spp55 - Random pattern 55 for SPP scheme 213156: - spp56 - Random pattern 56 for SPP scheme 213157: - spp57 - Random pattern 57 for SPP scheme 213158: - spp58 - Random pattern 58 for SPP scheme 213159: - spp59 - Random pattern 59 for SPP scheme 213160: - spp60 - Random pattern 60 for SPP scheme 213161: - spp61 - Random pattern 61 for SPP scheme 213162: - spp62 - Random pattern 62 for SPP scheme 213163: - spp63 - Random pattern 63 for SPP scheme 213164: - spp64 - Random pattern 64 for SPP scheme 213165: - spp65 - Random pattern 65 for SPP scheme 213166: - spp66 - Random pattern 66 for SPP scheme 213167: - spp67 - Random pattern 67 for SPP scheme 213168: - spp68 - Random pattern 68 for SPP scheme 213169: - spp69 - Random pattern 69 for SPP scheme 213170: - spp70 - Random pattern 70 for SPP scheme 213171: - spp71 - Random pattern 71 for SPP scheme 213172: - spp72 - Random pattern 72 for SPP scheme 213173: - spp73 - Random pattern 73 for SPP scheme 213174: - spp74 - Random pattern 74 for SPP scheme 213175: - spp75 - Random pattern 75 for SPP scheme 213176: - spp76 - Random pattern 76 for SPP scheme 213177: - spp77 - Random pattern 77 for SPP scheme 213178: - spp78 - Random pattern 78 for SPP scheme 213179: - spp79 - Random pattern 79 for SPP scheme 213180: - spp80 - Random pattern 80 for SPP scheme 213181: - spp81 - Random pattern 81 for SPP scheme 213182: - spp82 - Random pattern 82 for SPP scheme 213183: - spp83 - Random pattern 83 for SPP scheme 213184: - spp84 - Random pattern 84 for SPP scheme 213185: - spp85 - Random pattern 85 for SPP scheme 213186: - spp86 - Random pattern 86 for SPP scheme 213187: - spp87 - Random pattern 87 for SPP scheme 213188: - spp88 - Random pattern 88 for SPP scheme 213189: - spp89 - Random pattern 89 for SPP scheme 213190: - spp90 - Random pattern 90 for SPP scheme 213191: - spp91 - Random pattern 91 for SPP scheme 213192: - spp92 - Random pattern 92 for SPP scheme 213193: - spp93 - Random pattern 93 for SPP scheme 213194: - spp94 - Random pattern 94 for SPP scheme 213195: - spp95 - Random pattern 95 for SPP scheme 213196: - spp96 - Random pattern 96 for SPP scheme 213197: - spp97 - Random pattern 97 for SPP scheme 213198: - spp98 - Random pattern 98 for SPP scheme 213199: - spp99 - Random pattern 99 for SPP scheme 213200: - spp100 - Random pattern 100 for SPP scheme 213201: - spp101 - Random pattern 101 for SPP scheme 213202: - spp102 - Random pattern 102 for SPP scheme 213203: - spp103 - Random pattern 103 for SPP scheme 213204: - spp104 - Random pattern 104 for SPP scheme 213205: - spp105 - Random pattern 105 for SPP scheme 213206: - spp106 - Random pattern 106 for SPP scheme 213207: - spp107 - Random pattern 107 for SPP scheme 213208: - spp108 - Random pattern 108 for SPP scheme 213209: - spp109 - Random pattern 109 for SPP scheme 213210: - spp110 - Random pattern 110 for SPP scheme 213211: - spp111 - Random pattern 111 for SPP scheme 213212: - spp112 - Random pattern 112 for SPP scheme 213213: - spp113 - Random pattern 113 for SPP scheme 213214: - spp114 - Random pattern 114 for SPP scheme 213215: - spp115 - Random pattern 115 for SPP scheme 213216: - spp116 - Random pattern 116 for SPP scheme 213217: - spp117 - Random pattern 117 for SPP scheme 213218: - spp118 - Random pattern 118 for SPP scheme 213219: - spp119 - Random pattern 119 for SPP scheme 213220: - spp120 - Random pattern 120 for SPP scheme 213221: - spp121 - Random pattern 121 for SPP scheme 214001: - cossza - Cosine of solar zenith angle 214002: - uvbed - UV biologically effective dose 214003: - uvbedcs - UV biologically effective dose clear-sky 214004: - uvsflxt280285 - Total surface UV spectral flux (280-285 nm) 214005: - uvsflxt285290 - Total surface UV spectral flux (285-290 nm) 214006: - uvsflxt290295 - Total surface UV spectral flux (290-295 nm) 214007: - uvsflxt295300 - Total surface UV spectral flux (295-300 nm) 214008: - uvsflxt300305 - Total surface UV spectral flux (300-305 nm) 214009: - uvsflxt305310 - Total surface UV spectral flux (305-310 nm) 214010: - uvsflxt310315 - Total surface UV spectral flux (310-315 nm) 214011: - uvsflxt315320 - Total surface UV spectral flux (315-320 nm) 214012: - uvsflxt320325 - Total surface UV spectral flux (320-325 nm) 214013: - uvsflxt325330 - Total surface UV spectral flux (325-330 nm) 214014: - uvsflxt330335 - Total surface UV spectral flux (330-335 nm) 214015: - uvsflxt335340 - Total surface UV spectral flux (335-340 nm) 214016: - uvsflxt340345 - Total surface UV spectral flux (340-345 nm) 214017: - uvsflxt345350 - Total surface UV spectral flux (345-350 nm) 214018: - uvsflxt350355 - Total surface UV spectral flux (350-355 nm) 214019: - uvsflxt355360 - Total surface UV spectral flux (355-360 nm) 214020: - uvsflxt360365 - Total surface UV spectral flux (360-365 nm) 214021: - uvsflxt365370 - Total surface UV spectral flux (365-370 nm) 214022: - uvsflxt370375 - Total surface UV spectral flux (370-375 nm) 214023: - uvsflxt375380 - Total surface UV spectral flux (375-380 nm) 214024: - uvsflxt380385 - Total surface UV spectral flux (380-385 nm) 214025: - uvsflxt385390 - Total surface UV spectral flux (385-390 nm) 214026: - uvsflxt390395 - Total surface UV spectral flux (390-395 nm) 214027: - uvsflxt395400 - Total surface UV spectral flux (395-400 nm) 214028: - uvsflxcs280285 - Clear-sky surface UV spectral flux (280-285 nm) 214029: - uvsflxcs285290 - Clear-sky surface UV spectral flux (285-290 nm) 214030: - uvsflxcs290295 - Clear-sky surface UV spectral flux (290-295 nm) 214031: - uvsflxcs295300 - Clear-sky surface UV spectral flux (295-300 nm) 214032: - uvsflxcs300305 - Clear-sky surface UV spectral flux (300-305 nm) 214033: - uvsflxcs305310 - Clear-sky surface UV spectral flux (305-310 nm) 214034: - uvsflxcs310315 - Clear-sky surface UV spectral flux (310-315 nm) 214035: - uvsflxcs315320 - Clear-sky surface UV spectral flux (315-320 nm) 214036: - uvsflxcs320325 - Clear-sky surface UV spectral flux (320-325 nm) 214037: - uvsflxcs325330 - Clear-sky surface UV spectral flux (325-330 nm) 214038: - uvsflxcs330335 - Clear-sky surface UV spectral flux (330-335 nm) 214039: - uvsflxcs335340 - Clear-sky surface UV spectral flux (335-340 nm) 214040: - uvsflxcs340345 - Clear-sky surface UV spectral flux (340-345 nm) 214041: - uvsflxcs345350 - Clear-sky surface UV spectral flux (345-350 nm) 214042: - uvsflxcs350355 - Clear-sky surface UV spectral flux (350-355 nm) 214043: - uvsflxcs355360 - Clear-sky surface UV spectral flux (355-360 nm) 214044: - uvsflxcs360365 - Clear-sky surface UV spectral flux (360-365 nm) 214045: - uvsflxcs365370 - Clear-sky surface UV spectral flux (365-370 nm) 214046: - uvsflxcs370375 - Clear-sky surface UV spectral flux (370-375 nm) 214047: - uvsflxcs375380 - Clear-sky surface UV spectral flux (375-380 nm) 214048: - uvsflxcs380385 - Clear-sky surface UV spectral flux (380-385 nm) 214049: - uvsflxcs385390 - Clear-sky surface UV spectral flux (385-390 nm) 214050: - uvsflxcs390395 - Clear-sky surface UV spectral flux (390-395 nm) 214051: - uvsflxcs395400 - Clear-sky surface UV spectral flux (395-400 nm) 214052: - aot340 - Profile of optical thickness at 340 nm 215001: - aersrcsss - Source/gain of sea salt aerosol (0.03 - 0.5 um) 215002: - aersrcssm - Source/gain of sea salt aerosol (0.5 - 5 um) 215003: - aersrcssl - Source/gain of sea salt aerosol (5 - 20 um) 215004: - aerddpsss - Dry deposition of sea salt aerosol (0.03 - 0.5 um) 215005: - aerddpssm - Dry deposition of sea salt aerosol (0.5 - 5 um) 215006: - aerddpssl - Dry deposition of sea salt aerosol (5 - 20 um) 215007: - aersdmsss - Sedimentation of sea salt aerosol (0.03 - 0.5 um) 215008: - aersdmssm - Sedimentation of sea salt aerosol (0.5 - 5 um) 215009: - aersdmssl - Sedimentation of sea salt aerosol (5 - 20 um) 215010: - aerwdlssss - Wet deposition of sea salt aerosol (0.03 - 0.5 um) by large-scale precipitation 215011: - aerwdlsssm - Wet deposition of sea salt aerosol (0.5 - 5 um) by large-scale precipitation 215012: - aerwdlsssl - Wet deposition of sea salt aerosol (5 - 20 um) by large-scale precipitation 215013: - aerwdccsss - Wet deposition of sea salt aerosol (0.03 - 0.5 um) by convective precipitation 215014: - aerwdccssm - Wet deposition of sea salt aerosol (0.5 - 5 um) by convective precipitation 215015: - aerwdccssl - Wet deposition of sea salt aerosol (5 - 20 um) by convective precipitation 215016: - aerngtsss - Negative fixer of sea salt aerosol (0.03 - 0.5 um) 215017: - aerngtssm - Negative fixer of sea salt aerosol (0.5 - 5 um) 215018: - aerngtssl - Negative fixer of sea salt aerosol (5 - 20 um) 215019: - aermsssss - Vertically integrated mass of sea salt aerosol (0.03 - 0.5 um) 215020: - aermssssm - Vertically integrated mass of sea salt aerosol (0.5 - 5 um) 215021: - aermssssl - Vertically integrated mass of sea salt aerosol (5 - 20 um) 215022: - aerodsss - Sea salt aerosol (0.03 - 0.5 um) optical depth 215023: - aerodssm - Sea salt aerosol (0.5 - 5 um) optical depth 215024: - aerodssl - Sea salt aerosol (5 - 20 um) optical depth 215025: - aersrcdus - Source/gain of dust aerosol (0.03 - 0.55 um) 215026: - aersrcdum - Source/gain of dust aerosol (0.55 - 0.9 um) 215027: - aersrcdul - Source/gain of dust aerosol (0.9 - 20 um) 215028: - aerddpdus - Dry deposition of dust aerosol (0.03 - 0.55 um) 215029: - aerddpdum - Dry deposition of dust aerosol (0.55 - 0.9 um) 215030: - aerddpdul - Dry deposition of dust aerosol (0.9 - 20 um) 215031: - aersdmdus - Sedimentation of dust aerosol (0.03 - 0.55 um) 215032: - aersdmdum - Sedimentation of dust aerosol (0.55 - 0.9 um) 215033: - aersdmdul - Sedimentation of dust aerosol (0.9 - 20 um) 215034: - aerwdlsdus - Wet deposition of dust aerosol (0.03 - 0.55 um) by large-scale precipitation 215035: - aerwdlsdum - Wet deposition of dust aerosol (0.55 - 0.9 um) by large-scale precipitation 215036: - aerwdlsdul - Wet deposition of dust aerosol (0.9 - 20 um) by large-scale precipitation 215037: - aerwdccdus - Wet deposition of dust aerosol (0.03 - 0.55 um) by convective precipitation 215038: - aerwdccdum - Wet deposition of dust aerosol (0.55 - 0.9 um) by convective precipitation 215039: - aerwdccdul - Wet deposition of dust aerosol (0.9 - 20 um) by convective precipitation 215040: - aerngtdus - Negative fixer of dust aerosol (0.03 - 0.55 um) 215041: - aerngtdum - Negative fixer of dust aerosol (0.55 - 0.9 um) 215042: - aerngtdul - Negative fixer of dust aerosol (0.9 - 20 um) 215043: - aermssdus - Vertically integrated mass of dust aerosol (0.03 - 0.55 um) 215044: - aermssdum - Vertically integrated mass of dust aerosol (0.55 - 0.9 um) 215045: - aermssdul - Vertically integrated mass of dust aerosol (0.9 - 20 um) 215046: - aeroddus - Dust aerosol (0.03 - 0.55 um) optical depth 215047: - aeroddum - Dust aerosol (0.55 - 0.9 um) optical depth 215048: - aeroddul - Dust aerosol (0.9 - 20 um) optical depth 215049: - aersrcomhphob - Source/gain of hydrophobic organic matter aerosol 215050: - aersrcomhphil - Source/gain of hydrophilic organic matter aerosol 215051: - aerddpomhphob - Dry deposition of hydrophobic organic matter aerosol 215052: - aerddpomhphil - Dry deposition of hydrophilic organic matter aerosol 215053: - aersdmomhphob - Sedimentation of hydrophobic organic matter aerosol 215054: - aersdmomhphil - Sedimentation of hydrophilic organic matter aerosol 215055: - aerwdlsomhphob - Wet deposition of hydrophobic organic matter aerosol by large-scale precipitation 215056: - aerwdlsomhphil - Wet deposition of hydrophilic organic matter aerosol by large-scale precipitation 215057: - aerwdccomhphob - Wet deposition of hydrophobic organic matter aerosol by convective precipitation 215058: - aerwdccomhphil - Wet deposition of hydrophilic organic matter aerosol by convective precipitation 215059: - aerngtomhphob - Negative fixer of hydrophobic organic matter aerosol 215060: - aerngtomhphil - Negative fixer of hydrophilic organic matter aerosol 215061: - aermssomhphob - Vertically integrated mass of hydrophobic organic matter aerosol 215062: - aermssomhphil - Vertically integrated mass of hydrophilic organic matter aerosol 215063: - aerodomhphob - Hydrophobic organic matter aerosol optical depth 215064: - aerodomhphil - Hydrophilic organic matter aerosol optical depth 215065: - aersrcbchphob - Source/gain of hydrophobic black carbon aerosol 215066: - aersrcbchphil - Source/gain of hydrophilic black carbon aerosol 215067: - aerddpbchphob - Dry deposition of hydrophobic black carbon aerosol 215068: - aerddpbchphil - Dry deposition of hydrophilic black carbon aerosol 215069: - aersdmbchphob - Sedimentation of hydrophobic black carbon aerosol 215070: - aersdmbchphil - Sedimentation of hydrophilic black carbon aerosol 215071: - aerwdlsbchphob - Wet deposition of hydrophobic black carbon aerosol by large-scale precipitation 215072: - aerwdlsbchphil - Wet deposition of hydrophilic black carbon aerosol by large-scale precipitation 215073: - aerwdccbchphob - Wet deposition of hydrophobic black carbon aerosol by convective precipitation 215074: - aerwdccbchphil - Wet deposition of hydrophilic black carbon aerosol by convective precipitation 215075: - aerngtbchphob - Negative fixer of hydrophobic black carbon aerosol 215076: - aerngtbchphil - Negative fixer of hydrophilic black carbon aerosol 215077: - aermssbchphob - Vertically integrated mass of hydrophobic black carbon aerosol 215078: - aermssbchphil - Vertically integrated mass of hydrophilic black carbon aerosol 215079: - aerodbchphob - Hydrophobic black carbon aerosol optical depth 215080: - aerodbchphil - Hydrophilic black carbon aerosol optical depth 215081: - aersrcsu - Source/gain of sulphate aerosol 215082: - aerddpsu - Dry deposition of sulphate aerosol 215083: - aersdmsu - Sedimentation of sulphate aerosol 215084: - aerwdlssu - Wet deposition of sulphate aerosol by large-scale precipitation 215085: - aerwdccsu - Wet deposition of sulphate aerosol by convective precipitation 215086: - aerngtsu - Negative fixer of sulphate aerosol 215087: - aermsssu - Vertically integrated mass of sulphate aerosol 215088: - aerodsu - Sulphate aerosol optical depth 215089: - accaod550 - Accumulated total aerosol optical depth at 550 nm 215090: - aluvpsn - Effective (snow effect included) UV visible albedo for direct radiation 215091: - aerdep10si - 10 metre wind speed dust emission potential 215092: - aerdep10fg - 10 metre wind gustiness dust emission potential 215093: - aot532 - Total aerosol optical thickness at 532 nm 215094: - naot532 - Natural (sea-salt and dust) aerosol optical thickness at 532 nm 215095: - aaot532 - Anthropogenic (black carbon, organic matter, sulphate) aerosol optical thickness at 532 nm 215096: - aodabs340 - Total absorption aerosol optical depth at 340 nm 215097: - aodabs355 - Total absorption aerosol optical depth at 355 nm 215098: - aodabs380 - Total absorption aerosol optical depth at 380 nm 215099: - aodabs400 - Total absorption aerosol optical depth at 400 nm 215100: - aodabs440 - Total absorption aerosol optical depth at 440 nm 215101: - aodabs469 - Total absorption aerosol optical depth at 469 nm 215102: - aodabs500 - Total absorption aerosol optical depth at 500 nm 215103: - aodabs532 - Total absorption aerosol optical depth at 532 nm 215104: - aodabs550 - Total absorption aerosol optical depth at 550 nm 215105: - aodabs645 - Total absorption aerosol optical depth at 645 nm 215106: - aodabs670 - Total absorption aerosol optical depth at 670 nm 215107: - aodabs800 - Total absorption aerosol optical depth at 800 nm 215108: - aodabs858 - Total absorption aerosol optical depth at 858 nm 215109: - aodabs865 - Total absorption aerosol optical depth at 865 nm 215110: - aodabs1020 - Total absorption aerosol optical depth at 1020 nm 215111: - aodabs1064 - Total absorption aerosol optical depth at 1064 nm 215112: - aodabs1240 - Total absorption aerosol optical depth at 1240 nm 215113: - aodabs1640 - Total absorption aerosol optical depth at 1640 nm 215114: - aodfm340 - Total fine mode (r < 0.5 um) aerosol optical depth at 340 nm 215115: - aodfm355 - Total fine mode (r < 0.5 um) aerosol optical depth at 355 nm 215116: - aodfm380 - Total fine mode (r < 0.5 um) aerosol optical depth at 380 nm 215117: - aodfm400 - Total fine mode (r < 0.5 um) aerosol optical depth at 400 nm 215118: - aodfm440 - Total fine mode (r < 0.5 um) aerosol optical depth at 440 nm 215119: - aodfm469 - Total fine mode (r < 0.5 um) aerosol optical depth at 469 nm 215120: - aodfm500 - Total fine mode (r < 0.5 um) aerosol optical depth at 500 nm 215121: - aodfm532 - Total fine mode (r < 0.5 um) aerosol optical depth at 532 nm 215122: - aodfm550 - Total fine mode (r < 0.5 um) aerosol optical depth at 550 nm 215123: - aodfm645 - Total fine mode (r < 0.5 um) aerosol optical depth at 645 nm 215124: - aodfm670 - Total fine mode (r < 0.5 um) aerosol optical depth at 670 nm 215125: - aodfm800 - Total fine mode (r < 0.5 um) aerosol optical depth at 800 nm 215126: - aodfm858 - Total fine mode (r < 0.5 um) aerosol optical depth at 858 nm 215127: - aodfm865 - Total fine mode (r < 0.5 um) aerosol optical depth at 865 nm 215128: - aodfm1020 - Total fine mode (r < 0.5 um) aerosol optical depth at 1020 nm 215129: - aodfm1064 - Total fine mode (r < 0.5 um) aerosol optical depth at 1064 nm 215130: - aodfm1240 - Total fine mode (r < 0.5 um) aerosol optical depth at 1240 nm 215131: - aodfm1640 - Total fine mode (r < 0.5 um) aerosol optical depth at 1640 nm 215132: - ssa340 - Single scattering albedo at 340 nm 215133: - ssa355 - Single scattering albedo at 355 nm 215134: - ssa380 - Single scattering albedo at 380 nm 215135: - ssa400 - Single scattering albedo at 400 nm 215136: - ssa440 - Single scattering albedo at 440 nm 215137: - ssa469 - Single scattering albedo at 469 nm 215138: - ssa500 - Single scattering albedo at 500 nm 215139: - ssa532 - Single scattering albedo at 532 nm 215140: - ssa550 - Single scattering albedo at 550 nm 215141: - ssa645 - Single scattering albedo at 645 nm 215142: - ssa670 - Single scattering albedo at 670 nm 215143: - ssa800 - Single scattering albedo at 800 nm 215144: - ssa858 - Single scattering albedo at 858 nm 215145: - ssa865 - Single scattering albedo at 865 nm 215146: - ssa1020 - Single scattering albedo at 1020 nm 215147: - ssa1064 - Single scattering albedo at 1064 nm 215148: - ssa1240 - Single scattering albedo at 1240 nm 215149: - ssa1640 - Single scattering albedo at 1640 nm 215150: - asymmetry340 - Asymmetry factor at 340 nm 215151: - asymmetry355 - Asymmetry factor at 355 nm 215152: - asymmetry380 - Asymmetry factor at 380 nm 215153: - asymmetry400 - Asymmetry factor at 400 nm 215154: - asymmetry440 - Asymmetry factor at 440 nm 215155: - asymmetry469 - Asymmetry factor at 469 nm 215156: - asymmetry500 - Asymmetry factor at 500 nm 215157: - asymmetry532 - Asymmetry factor at 532 nm 215158: - asymmetry550 - Asymmetry factor at 550 nm 215159: - asymmetry645 - Asymmetry factor at 645 nm 215160: - asymmetry670 - Asymmetry factor at 670 nm 215161: - asymmetry800 - Asymmetry factor at 800 nm 215162: - asymmetry858 - Asymmetry factor at 858 nm 215163: - asymmetry865 - Asymmetry factor at 865 nm 215164: - asymmetry1020 - Asymmetry factor at 1020 nm 215165: - asymmetry1064 - Asymmetry factor at 1064 nm 215166: - asymmetry1240 - Asymmetry factor at 1240 nm 215167: - asymmetry1640 - Asymmetry factor at 1640 nm 215168: - aersrcso2 - Source/gain of sulphur dioxide 215169: - aerddpso2 - Dry deposition of sulphur dioxide 215170: - aersdmso2 - Sedimentation of sulphur dioxide 215171: - aerwdlsso2 - Wet deposition of sulphur dioxide by large-scale precipitation 215172: - aerwdccso2 - Wet deposition of sulphur dioxide by convective precipitation 215173: - aerngtso2 - Negative fixer of sulphur dioxide 215174: - aermssso2 - Vertically integrated mass of sulphur dioxide 215175: - aerodso2 - Sulphur dioxide optical depth 215176: - aodabs2130 - Total absorption aerosol optical depth at 2130 nm 215177: - aodfm2130 - Total fine mode (r < 0.5 um) aerosol optical depth at 2130 nm 215178: - ssa2130 - Single scattering albedo at 2130 nm 215179: - asymmetry2130 - Asymmetry factor at 2130 nm 215180: - aerext355 - Aerosol extinction coefficient at 355 nm 215181: - aerext532 - Aerosol extinction coefficient at 532 nm 215182: - aerext1064 - Aerosol extinction coefficient at 1064 nm 215183: - aerbackscattoa355 - Aerosol backscatter coefficient at 355 nm (from top of atmosphere) 215184: - aerbackscattoa532 - Aerosol backscatter coefficient at 532 nm (from top of atmosphere) 215185: - aerbackscattoa1064 - Aerosol backscatter coefficient at 1064 nm (from top of atmosphere) 215186: - aerbackscatgnd355 - Aerosol backscatter coefficient at 355 nm (from ground) 215187: - aerbackscatgnd532 - Aerosol backscatter coefficient at 532 nm (from ground) 215188: - aerbackscatgnd1064 - Aerosol backscatter coefficient at 1064 nm (from ground) 215189: - aersrcnif - Source/gain of fine-mode nitrate aerosol 215190: - aersrcnic - Source/gain of coarse-mode nitrate aerosol 215191: - aerddpnif - Dry deposition of fine-mode nitrate aerosol 215192: - aerddpnic - Dry deposition of coarse-mode nitrate aerosol 215193: - aersdmnif - Sedimentation of fine-mode nitrate aerosol 215194: - aersdmnic - Sedimentation of coarse-mode nitrate aerosol 215195: - aerwdlnif - Wet deposition of fine-mode nitrate aerosol by large-scale precipitation 215196: - aerwdlnic - Wet deposition of coarse-mode nitrate aerosol by large-scale precipitation 215197: - aerwdcnif - Wet deposition of fine-mode nitrate aerosol by convective precipitation 215198: - aerwdcnic - Wet deposition of coarse-mode nitrate aerosol by convective precipitation 215199: - aerngtnif - Negative fixer of fine-mode nitrate aerosol 215200: - aerngtnic - Negative fixer of coarse-mode nitrate aerosol 215201: - aermssnif - Vertically integrated mass of fine-mode nitrate aerosol 215202: - aermssnic - Vertically integrated mass of coarse-mode nitrate aerosol 215203: - aerodnif - Fine-mode nitrate aerosol optical depth at 550 nm 215204: - aerodnic - Coarse-mode nitrate aerosol optical depth at 550 nm 215205: - aersrcam - Source/gain of ammonium aerosol 215206: - aerddpam - Dry deposition of ammonium aerosol 215207: - aersdmam - Sedimentation of ammonium aerosol 215208: - aerwdlam - Wet deposition of ammonium aerosol by large-scale precipitation 215209: - aerwdcam - Wet deposition of ammonium aerosol by convective precipitation 215210: - aerngtam - Negative fixer of ammonium aerosol 215211: - aermssam - Vertically integrated mass of ammonium aerosol 215212: - aersrcsoab - Source/gain of biogenic secondary organic aerosol 215213: - aerddpsoab - Dry deposition of biogenic secondary organic aerosol 215214: - aersdmsoab - Sedimentation of biogenic secondary organic aerosol 215215: - aerwdlsoab - Wet deposition of biogenic secondary organic aerosol by large-scale precipitation 215216: - aerwdcsoab - Wet deposition of biogenic secondary organic aerosol by convective precipitation 215217: - aerngtsoab - Negative fixer of biogenic secondary organic aerosol 215218: - aermsssoab - Vertically integrated mass of biogenic secondary organic aerosol 215219: - aersrcsoaa - Source/gain of anthropogenic secondary organic aerosol 215220: - aerddpsoaa - Dry deposition of anthropogenic secondary organic aerosol 215221: - aersdmsoaa - Sedimentation of anthropogenic secondary organic aerosol 215222: - aerwdlsoaa - Wet deposition of anthropogenic secondary organic aerosol by large-scale precipitation 215223: - aerwdcsoaa - Wet deposition of anthropogenic secondary organic aerosol by convective precipitation 215224: - aerngtsoaa - Negative fixer of anthropogenic secondary organic aerosol 215225: - aermsssoaa - Vertically integrated mass of anthropogenic secondary organic aerosol 215226: - soaod550 - Secondary organic aerosol optical depth at 550 nm 216001: - _param_216001 - Experimental product 216002: - _param_216002 - Experimental product 216003: - _param_216003 - Experimental product 216004: - _param_216004 - Experimental product 216005: - _param_216005 - Experimental product 216006: - _param_216006 - Experimental product 216007: - _param_216007 - Experimental product 216008: - _param_216008 - Experimental product 216009: - _param_216009 - Experimental product 216010: - _param_216010 - Experimental product 216011: - _param_216011 - Experimental product 216012: - _param_216012 - Experimental product 216013: - _param_216013 - Experimental product 216014: - _param_216014 - Experimental product 216015: - _param_216015 - Experimental product 216016: - _param_216016 - Experimental product 216017: - _param_216017 - Experimental product 216018: - _param_216018 - Experimental product 216019: - _param_216019 - Experimental product 216020: - _param_216020 - Experimental product 216021: - _param_216021 - Experimental product 216022: - _param_216022 - Experimental product 216023: - _param_216023 - Experimental product 216024: - _param_216024 - Experimental product 216025: - _param_216025 - Experimental product 216026: - _param_216026 - Experimental product 216027: - _param_216027 - Experimental product 216028: - _param_216028 - Experimental product 216029: - _param_216029 - Experimental product 216030: - _param_216030 - Experimental product 216031: - _param_216031 - Experimental product 216032: - _param_216032 - Experimental product 216033: - _param_216033 - Experimental product 216034: - _param_216034 - Experimental product 216035: - _param_216035 - Experimental product 216036: - _param_216036 - Experimental product 216037: - _param_216037 - Experimental product 216038: - _param_216038 - Experimental product 216039: - _param_216039 - Experimental product 216040: - _param_216040 - Experimental product 216041: - _param_216041 - Experimental product 216042: - _param_216042 - Experimental product 216043: - _param_216043 - Experimental product 216044: - _param_216044 - Experimental product 216045: - _param_216045 - Experimental product 216046: - _param_216046 - Experimental product 216047: - _param_216047 - Experimental product 216048: - _param_216048 - Experimental product 216049: - _param_216049 - Experimental product 216050: - _param_216050 - Experimental product 216051: - _param_216051 - Experimental product 216052: - _param_216052 - Experimental product 216053: - _param_216053 - Experimental product 216054: - _param_216054 - Experimental product 216055: - _param_216055 - Experimental product 216056: - _param_216056 - Experimental product 216057: - _param_216057 - Experimental product 216058: - _param_216058 - Experimental product 216059: - _param_216059 - Experimental product 216060: - _param_216060 - Experimental product 216061: - _param_216061 - Experimental product 216062: - _param_216062 - Experimental product 216063: - _param_216063 - Experimental product 216064: - _param_216064 - Experimental product 216065: - _param_216065 - Experimental product 216066: - _param_216066 - Experimental product 216067: - _param_216067 - Experimental product 216068: - _param_216068 - Experimental product 216069: - _param_216069 - Experimental product 216070: - _param_216070 - Experimental product 216071: - _param_216071 - Experimental product 216072: - _param_216072 - Experimental product 216073: - _param_216073 - Experimental product 216074: - _param_216074 - Experimental product 216075: - _param_216075 - Experimental product 216076: - _param_216076 - Experimental product 216077: - _param_216077 - Experimental product 216078: - _param_216078 - Experimental product 216079: - _param_216079 - Experimental product 216080: - _param_216080 - Experimental product 216081: - _param_216081 - Experimental product 216082: - _param_216082 - Experimental product 216083: - _param_216083 - Experimental product 216084: - _param_216084 - Experimental product 216085: - _param_216085 - Experimental product 216086: - _param_216086 - Experimental product 216087: - _param_216087 - Experimental product 216088: - _param_216088 - Experimental product 216089: - _param_216089 - Experimental product 216090: - _param_216090 - Experimental product 216091: - _param_216091 - Experimental product 216092: - _param_216092 - Experimental product 216093: - _param_216093 - Experimental product 216094: - _param_216094 - Experimental product 216095: - _param_216095 - Experimental product 216096: - _param_216096 - Experimental product 216097: - _param_216097 - Experimental product 216098: - _param_216098 - Experimental product 216099: - _param_216099 - Experimental product 216100: - _param_216100 - Experimental product 216101: - _param_216101 - Experimental product 216102: - _param_216102 - Experimental product 216103: - _param_216103 - Experimental product 216104: - _param_216104 - Experimental product 216105: - _param_216105 - Experimental product 216106: - _param_216106 - Experimental product 216107: - _param_216107 - Experimental product 216108: - _param_216108 - Experimental product 216109: - _param_216109 - Experimental product 216110: - _param_216110 - Experimental product 216111: - _param_216111 - Experimental product 216112: - _param_216112 - Experimental product 216113: - _param_216113 - Experimental product 216114: - _param_216114 - Experimental product 216115: - _param_216115 - Experimental product 216116: - _param_216116 - Experimental product 216117: - _param_216117 - Experimental product 216118: - _param_216118 - Experimental product 216119: - _param_216119 - Experimental product 216120: - _param_216120 - Experimental product 216121: - _param_216121 - Experimental product 216122: - _param_216122 - Experimental product 216123: - _param_216123 - Experimental product 216124: - _param_216124 - Experimental product 216125: - _param_216125 - Experimental product 216126: - _param_216126 - Experimental product 216127: - _param_216127 - Experimental product 216128: - _param_216128 - Experimental product 216129: - _param_216129 - Experimental product 216130: - _param_216130 - Experimental product 216131: - _param_216131 - Experimental product 216132: - _param_216132 - Experimental product 216133: - _param_216133 - Experimental product 216134: - _param_216134 - Experimental product 216135: - _param_216135 - Experimental product 216136: - _param_216136 - Experimental product 216137: - _param_216137 - Experimental product 216138: - _param_216138 - Experimental product 216139: - _param_216139 - Experimental product 216140: - _param_216140 - Experimental product 216141: - _param_216141 - Experimental product 216142: - _param_216142 - Experimental product 216143: - _param_216143 - Experimental product 216144: - _param_216144 - Experimental product 216145: - _param_216145 - Experimental product 216146: - _param_216146 - Experimental product 216147: - _param_216147 - Experimental product 216148: - _param_216148 - Experimental product 216149: - _param_216149 - Experimental product 216150: - _param_216150 - Experimental product 216151: - _param_216151 - Experimental product 216152: - _param_216152 - Experimental product 216153: - _param_216153 - Experimental product 216154: - _param_216154 - Experimental product 216155: - _param_216155 - Experimental product 216156: - _param_216156 - Experimental product 216157: - _param_216157 - Experimental product 216158: - _param_216158 - Experimental product 216159: - _param_216159 - Experimental product 216160: - _param_216160 - Experimental product 216161: - _param_216161 - Experimental product 216162: - _param_216162 - Experimental product 216163: - _param_216163 - Experimental product 216164: - _param_216164 - Experimental product 216165: - _param_216165 - Experimental product 216166: - _param_216166 - Experimental product 216167: - _param_216167 - Experimental product 216168: - _param_216168 - Experimental product 216169: - _param_216169 - Experimental product 216170: - _param_216170 - Experimental product 216171: - _param_216171 - Experimental product 216172: - _param_216172 - Experimental product 216173: - _param_216173 - Experimental product 216174: - _param_216174 - Experimental product 216175: - _param_216175 - Experimental product 216176: - _param_216176 - Experimental product 216177: - _param_216177 - Experimental product 216178: - _param_216178 - Experimental product 216179: - _param_216179 - Experimental product 216180: - _param_216180 - Experimental product 216181: - _param_216181 - Experimental product 216182: - _param_216182 - Experimental product 216183: - _param_216183 - Experimental product 216184: - _param_216184 - Experimental product 216185: - _param_216185 - Experimental product 216186: - _param_216186 - Experimental product 216187: - _param_216187 - Experimental product 216188: - _param_216188 - Experimental product 216189: - _param_216189 - Experimental product 216190: - _param_216190 - Experimental product 216191: - _param_216191 - Experimental product 216192: - _param_216192 - Experimental product 216193: - _param_216193 - Experimental product 216194: - _param_216194 - Experimental product 216195: - _param_216195 - Experimental product 216196: - _param_216196 - Experimental product 216197: - _param_216197 - Experimental product 216198: - _param_216198 - Experimental product 216199: - _param_216199 - Experimental product 216200: - _param_216200 - Experimental product 216201: - _param_216201 - Experimental product 216202: - _param_216202 - Experimental product 216203: - _param_216203 - Experimental product 216204: - _param_216204 - Experimental product 216205: - _param_216205 - Experimental product 216206: - _param_216206 - Experimental product 216207: - _param_216207 - Experimental product 216208: - _param_216208 - Experimental product 216209: - _param_216209 - Experimental product 216210: - _param_216210 - Experimental product 216211: - _param_216211 - Experimental product 216212: - _param_216212 - Experimental product 216213: - _param_216213 - Experimental product 216214: - _param_216214 - Experimental product 216215: - _param_216215 - Experimental product 216216: - _param_216216 - Experimental product 216217: - _param_216217 - Experimental product 216218: - _param_216218 - Experimental product 216219: - _param_216219 - Experimental product 216220: - _param_216220 - Experimental product 216221: - _param_216221 - Experimental product 216222: - _param_216222 - Experimental product 216223: - _param_216223 - Experimental product 216224: - _param_216224 - Experimental product 216225: - _param_216225 - Experimental product 216226: - _param_216226 - Experimental product 216227: - _param_216227 - Experimental product 216228: - _param_216228 - Experimental product 216229: - _param_216229 - Experimental product 216230: - _param_216230 - Experimental product 216231: - _param_216231 - Experimental product 216232: - _param_216232 - Experimental product 216233: - _param_216233 - Experimental product 216234: - _param_216234 - Experimental product 216235: - _param_216235 - Experimental product 216236: - _param_216236 - Experimental product 216237: - _param_216237 - Experimental product 216238: - _param_216238 - Experimental product 216239: - _param_216239 - Experimental product 216240: - _param_216240 - Experimental product 216241: - _param_216241 - Experimental product 216242: - _param_216242 - Experimental product 216243: - _param_216243 - Experimental product 216244: - _param_216244 - Experimental product 216245: - _param_216245 - Experimental product 216246: - _param_216246 - Experimental product 216247: - _param_216247 - Experimental product 216248: - _param_216248 - Experimental product 216249: - _param_216249 - Experimental product 216250: - _param_216250 - Experimental product 216251: - _param_216251 - Experimental product 216252: - _param_216252 - Experimental product 216253: - _param_216253 - Experimental product 216254: - _param_216254 - Experimental product 216255: - _param_216255 - Experimental product 217003: - h2o2 - Hydrogen peroxide 217004: - ch4_c - Methane (chemistry) 217006: - hno3 - Nitric acid 217007: - ch3ooh - Methyl peroxide 217009: - par - Paraffins 217010: - c2h4 - Ethene 217011: - ole - Olefins 217012: - ald2 - Aldehydes 217013: - pan - Peroxyacetyl nitrate 217014: - rooh - Peroxides 217015: - onit - Organic nitrates 217016: - c5h8 - Isoprene 217018: - dms - Dimethyl sulfide 217019: - nh3 - Ammonia mass mixing ratio 217020: - so4 - Sulfate 217021: - nh4 - Ammonium 217022: - msa - Methane sulfonic acid 217023: - ch3cocho - Methyl glyoxal 217024: - o3s - Stratospheric ozone 217026: - pb - Lead 217027: - 'no' - Nitrogen monoxide mass mixing ratio 217028: - ho2 - Hydroperoxy radical 217029: - ch3o2 - Methylperoxy radical 217030: - oh - Hydroxyl radical 217032: - no3 - Nitrate radical 217033: - n2o5 - Dinitrogen pentoxide 217034: - ho2no2 - Pernitric acid 217035: - c2o3 - Peroxy acetyl radical 217036: - ror - Organic ethers 217037: - rxpar - PAR budget corrector 217038: - xo2 - NO to NO2 operator 217039: - xo2n - NO to alkyl nitrate operator 217040: - nh2 - Amine 217041: - psc - Polar stratospheric cloud 217042: - ch3oh - Methanol 217043: - hcooh - Formic acid 217044: - mcooh - Methacrylic acid 217045: - c2h6 - Ethane 217046: - c2h5oh - Ethanol 217047: - c3h8 - Propane 217048: - c3h6 - Propene 217049: - c10h16 - Terpenes 217050: - ispd - Methacrolein MVK 217051: - no3_a - Nitrate 217052: - ch3coch3 - Acetone 217053: - aco2 - Acetone product 217054: - ic3h7o2 217055: - hypropo2 217056: - noxa - Nitrogen oxides Transp 217057: - co2_c - Carbon dioxide (chemistry) 217058: - n2o_c - Nitrous oxide (chemistry) 217059: - h2o - Water vapour (chemistry) 217060: - o2 - Oxygen 217061: - o2_1s - Singlet oxygen 217062: - o2_1d - Singlet delta oxygen 217063: - oclo - Chlorine dioxide 217064: - clono2 - Chlorine nitrate 217065: - hocl - Hypochlorous acid 217066: - cl2 - Chlorine 217067: - clno2 - Nitryl chloride 217068: - hbr - Hydrogen bromide 217069: - cl2o2 - Dichlorine dioxide 217070: - hobr - Hypobromous acid 217071: - cfc11 - Trichlorofluoromethane 217072: - cfc12 - Dichlorodifluoromethane 217073: - cfc113 - Trichlorotrifluoroethane 217074: - cfc114 - Dichlorotetrafluoroethane 217075: - cfc115 - Chloropentafluoroethane 217076: - ccl4 - Tetrachloromethane 217077: - ch3ccl3 - Methyl chloroform 217078: - ch3cl - Methyl chloride 217079: - hcfc22 - Chlorodifluoromethane 217080: - ch3br - Methyl bromide 217081: - ha1202 - Dibromodifluoromethane 217082: - ha1211 - Bromochlorodifluoromethane 217083: - ha1301 - Trifluorobromomethane 217084: - ha2402 - Cbrf2cbrf2 217085: - h2so4 - Sulfuric acid 217086: - hono - Nitrous acid 217087: - hc3 - Alkanes low oh rate 217088: - hc5 - Alkanes med oh rate 217089: - hc8 - Alkanes high oh rate 217090: - olt - Terminal alkenes 217091: - oli - Internal alkenes 217092: - c2h5o2 - Ethylperoxy radical 217093: - dien - Butadiene 217094: - c2h5ooh - Ethyl hydroperoxide 217095: - api - A-pinene cyclic terpenes 217096: - ch3cooh - Acetic acid 217097: - lim - D-limonene cyclic diene 217098: - ch3cho - Acetaldehyde 217099: - tol - Toluene and less reactive aromatics 217100: - xyl - Xylene and more reactive aromatics 217101: - glyald - Glycolaldehyde 217102: - cresol 217103: - ald - Acetaldehyde and higher 217104: - ch3coooh - Peracetic acid 217105: - ket - Ketones 217106: - eo2 - Hoch2ch2o2 217107: - glyoxal 217108: - eo - Hoch2ch2o 217109: - dcb - Unsaturated dicarbonyls 217110: - macr - Methacrolein 217111: - udd - Unsaturated hydroxy dicarbonyl 217112: - c3h7o2 - Isopropyldioxidanyl 217113: - hket - Hydroxy ketone 217114: - c3h7ooh - Isopropyl hydroperoxide 217115: - po2 - C3h6oho2 217116: - pooh - C3h6ohooh 217117: - op2 - Higher organic peroxides 217118: - hyac - Hydroxyacetone 217119: - paa - Peroxyacetic acid 217120: - ro2 - Ch3coch2o2 217121: - ethp - Peroxy radical from c2h6 217122: - hc3p - Peroxy radical from hc3 217123: - hc5p - Peroxy radical from hc5 217124: - bigene - Lumped alkenes 217125: - hc8p - Peroxy radical from hc8 217126: - bigalk - Lumped alkanes 217127: - etep - Peroxy radical from c2h4 217128: - mek - C4h8o 217129: - oltp - Peroxy radical from terminal alkenes 217130: - eneo2 - C4h9o3 217131: - olip - Peroxy radical from internal alkenes 217132: - meko2 - Ch3coch(oo)ch3 217133: - isopo2 - Peroxy radical from c5h8 217134: - mekooh - Ch3coch(ooh)ch3 217135: - apip - Peroxy radical from a-pinene cyclic terpenes 217136: - mco3 - Ch2=c(ch3)co3 217137: - limp - Peroxy radical from d-limonene cyclic diene 217138: - mvk - Methylvinylketone 217139: - pho - Phenoxy radical 217140: - tolp - Peroxy radical from toluene and less reactive aromatics 217141: - macro2 - Ch3c(o)ch(oo)ch2oh 217142: - xylp - Peroxy radical from xylene and more reactive aromatics 217143: - macrooh - H3c(o)ch(ooh)ch2oh 217144: - cslp - Peroxy radical from cresol 217145: - mpan - Unsaturated pans 217146: - tco3_c - Unsaturated acyl peroxy radical 217147: - ketp - Peroxy radical from ketones 217148: - alko2 - C5h11o2 217149: - olnn - No3-alkenes adduct reacting to form carbonitrates 217150: - alkooh - C5h11ooh 217151: - olnd - No3-alkenes adduct reacting via decomposition 217152: - bigald - Hoch2c(ch3)=chcho 217153: - hydrald - C5h6o2 217154: - sulf - Trop sulfuric acid 217155: - ox - Oxides 217156: - isopno3 - Ch2chc(ch3)(oo)ch2ono2 217157: - onitr - C3 organic nitrate 217158: - clox - Chlorine oxides 217159: - brox - Bromine oxides 217160: - xooh - Hoch2c(ooh)(ch3)chchoh 217161: - isopooh - Hoch2c(ooh)(ch3)ch=ch2 217162: - toluene - Lumped aromatics 217163: - dmso - Dimethyl sulfoxyde 217164: - tolo2 - C7h9o5 217165: - tolooh - C7h10o5 217166: - h2s - Hydrogensulfide 217167: - xoh - C7h10o6 217168: - noy - All nitrogen oxides 217169: - cly - Chlorine family 217170: - terpo2 - C10h16(oh)(oo) 217171: - bry - Bromine family 217172: - terpooh - C10h18o3 217173: - n - Nitrogen atom 217174: - clo - Chlorine monoxide 217175: - cl_c - Chlorine atom 217176: - bro - Bromine monoxide 217177: - h_c - Hydrogen atom 217178: - ch3 - Methyl group 217179: - addt - Aromatic-ho from toluene and less reactive aromatics 217180: - addx - Aromatic-ho from xylene and more reactive aromatics 217181: - nh4no3 - Ammonium nitrate 217182: - addc - Aromatic-ho from csl 217183: - soa1 - Secondary organic aerosol type 1 217184: - soa2a - Secondary organic aerosol type 2a 217185: - soa2b - Secondary organic aerosol type 2b 217186: - sog1 - Condensable gas type 1 217187: - sog2a - Condensable gas type 2a 217188: - sog2b - Condensable gas type 2b 217189: - so3 - Sulfur trioxide 217190: - ocs_c - Carbonyl sulfide 217191: - br - Bromine atom 217192: - br2 - Bromine 217193: - brcl - Bromine monochloride 217194: - brono2 - Bromine nitrate 217195: - ch2br2 - Dibromomethane 217196: - ch3o - Methoxy radical 217197: - chbr3 - Tribromomethane 217198: - cloo - Asymmetric chlorine dioxide radical 217199: - h2 - Hydrogen 217200: - hcl - Hydrogen chloride 217201: - hco - Formyl radical 217202: - hf - Hydrogen fluoride 217203: - o - Oxygen atom 217204: - o1d - Excited oxygen atom 217205: - o3p - Ground state oxygen atom 217206: - strataer - Stratospheric aerosol 217222: - aroo2 - Aromatic peroxy radical mass mixing ratio 217223: - c2h2 - Ethyne mass mixing ratio 217224: - ch3cn - Acetonitrile mass mixing ratio 217225: - ch3o2no2 - Methyl peroxy nitrate mass mixing ratio 217226: - hcn - Hydrogen cyanide mass mixing ratio 217227: - hpald1 - Hydroperoxy aldehydes type 1 mass mixing ratio 217228: - hpald - Hydroperoxy aldehydes type 2 mass mixing ratio 217229: - isopbo2 - Isoprene peroxy type B mass mixing ratio 217230: - isopdo2 - Isoprene peroxy type D mass mixing ratio 217231: - voca - Anthropogenic volatile organic compounds mass mixing ratio 217232: - vocbb - Biomass burning volatile organic compounds mass mixing ratio 218003: - tc_h2o2 - Total column hydrogen peroxide 218004: - tc_ch4 - Total column methane 218006: - tc_hno3 - Total column nitric acid 218007: - tc_ch3ooh - Total column methyl peroxide 218009: - tc_par - Total column paraffins 218010: - tc_c2h4 - Total column ethene 218011: - tc_ole - Total column olefins 218012: - tc_ald2 - Total column aldehydes 218013: - tc_pan - Total column peroxyacetyl nitrate 218014: - tc_rooh - Total column peroxides 218015: - tc_onit - Total column organic nitrates 218016: - tc_c5h8 - Total column isoprene 218018: - tc_dms - Total column dimethyl sulfide 218019: - tc_nh3 - Total column ammonia 218020: - tc_so4 - Total column sulfate 218021: - tc_nh4 - Total column ammonium 218022: - tc_msa - Total column methane sulfonic acid 218023: - tc_ch3cocho - Total column methyl glyoxal 218024: - tc_o3s - Total column stratospheric ozone 218026: - tc_pb - Total column lead 218027: - tc_no - Total column nitrogen monoxide 218028: - tc_ho2 - Total column hydroperoxy radical 218029: - tc_ch3o2 - Total column methylperoxy radical 218030: - tc_oh - Total column hydroxyl radical 218032: - tc_no3 - Total column nitrate radical 218033: - tc_n2o5 - Total column dinitrogen pentoxide 218034: - tc_ho2no2 - Total column pernitric acid 218035: - tc_c2o3 - Total column peroxy acetyl radical 218036: - tc_ror - Total column organic ethers 218037: - tc_rxpar - Total column PAR budget corrector 218038: - tc_xo2 - Total column NO to NO2 operator 218039: - tc_xo2n - Total column NO to alkyl nitrate operator 218040: - tc_nh2 - Total column amine 218041: - tc_psc - Total column polar stratospheric cloud 218042: - tc_ch3oh - Total column methanol 218043: - tc_hcooh - Total column formic acid 218044: - tc_mcooh - Total column methacrylic acid 218045: - tc_c2h6 - Total column ethane 218046: - tc_c2h5oh - Total column ethanol 218047: - tc_c3h8 - Total column propane 218048: - tc_c3h6 - Total column propene 218049: - tc_c10h16 - Total column terpenes 218050: - tc_ispd - Total column methacrolein MVK 218051: - tc_no3_a - Total column nitrate 218052: - tc_ch3coch3 - Total column acetone 218053: - tc_aco2 - Total column acetone product 218054: - tc_ic3h7o2 - Total column IC3H7O2 218055: - tc_hypropo2 - Total column HYPROPO2 218056: - tc_noxa - Total column nitrogen oxides Transp 218057: - tc_co2_c - Total column of carbon dioxide (chemistry) 218058: - tc_n2o_c - Total column of nitrous oxide (chemistry) 218059: - tc_h2o - Total column of water vapour (chemistry) 218060: - tc_o2 - Total column of oxygen 218061: - tc_o2_1s - Total column of singlet oxygen 218062: - tc_o2_1d - Total column of singlet delta oxygen 218063: - tc_oclo - Total column of chlorine dioxide 218064: - tc_clono2 - Total column of chlorine nitrate 218065: - tc_hocl - Total column of hypochlorous acid 218066: - tc_cl2 - Total column of chlorine 218067: - tc_clno2 - Total column of nitryl chloride 218068: - tc_hbr - Total column of hydrogen bromide 218069: - tc_cl2o2 - Total column of dichlorine dioxide 218070: - tc_hobr - Total column of hypobromous acid 218071: - tc_cfc11 - Total column of trichlorofluoromethane 218072: - tc_cfc12 - Total column of dichlorodifluoromethane 218073: - tc_cfc113 - Total column of trichlorotrifluoroethane 218074: - tc_cfc114 - Total column of dichlorotetrafluoroethane 218075: - tc_cfc115 - Total column of chloropentafluoroethane 218076: - tc_ccl4 - Total column of tetrachloromethane 218077: - tc_ch3ccl3 - Total column of methyl chloroform 218078: - tc_ch3cl - Total column of methyl chloride 218079: - tc_hcfc22 - Total column of chlorodifluoromethane 218080: - tc_ch3br - Total column of methyl bromide 218081: - tc_ha1202 - Total column of dibromodifluoromethane 218082: - tc_ha1211 - Total column of bromochlorodifluoromethane 218083: - tc_ha1301 - Total column of trifluorobromomethane 218084: - tc_ha2402 - Total column of cbrf2cbrf2 218085: - tc_h2so4 - Total column of sulfuric acid 218086: - tc_hono - Total column of nitrous acid 218087: - tc_hc3 - Total column of alkanes low oh rate 218088: - tc_hc5 - Total column of alkanes med oh rate 218089: - tc_hc8 - Total column of alkanes high oh rate 218090: - tc_olt - Total column of terminal alkenes 218091: - tc_oli - Total column of internal alkenes 218092: - tc_c2h5o2 - Total column of ethylperoxy radical 218093: - tc_dien - Total column of butadiene 218094: - tc_c2h5ooh - Total column of ethyl hydroperoxide 218095: - tc_api - Total column of a-pinene cyclic terpenes 218096: - tc_ch3cooh - Total column of acetic acid 218097: - tc_lim - Total column of d-limonene cyclic diene 218098: - tc_ch3cho - Total column of acetaldehyde 218099: - tc_tol - Total column of toluene and less reactive aromatics 218100: - tc_xyl - Total column of xylene and more reactive aromatics 218101: - tc_glyald - Total column of glycolaldehyde 218102: - tc_cresol - Total column of cresol 218103: - tc_ald - Total column of acetaldehyde and higher 218104: - tc_ch3coooh - Total column of peracetic acid 218105: - tc_ket - Total column of ketones 218106: - tc_eo2 - Total column of hoch2ch2o2 218107: - tc_glyoxal - Total column of glyoxal 218108: - tc_eo - Total column of hoch2ch2o 218109: - tc_dcb - Total column of unsaturated dicarbonyls 218110: - tc_macr - Total column of methacrolein 218111: - tc_udd - Total column of unsaturated hydroxy dicarbonyl 218112: - tc_c3h7o2 - Total column of isopropyldioxidanyl 218113: - tc_hket - Total column of hydroxy ketone 218114: - tc_c3h7ooh - Total column of isopropyl hydroperoxide 218115: - tc_po2 - Total column of c3h6oho2 218116: - tc_pooh - Total column of c3h6ohooh 218117: - tc_op2 - Total column of higher organic peroxides 218118: - tc_hyac - Total column of hydroxyacetone 218119: - tc_paa - Total column of peroxyacetic acid 218120: - tc_ro2 - Total column of ch3coch2o2 218121: - tc_ethp - Total column of peroxy radical from c2h6 218122: - tc_hc3p - Total column of peroxy radical from hc3 218123: - tc_hc5p - Total column of peroxy radical from hc5 218124: - tc_bigene - Total column of lumped alkenes 218125: - tc_hc8p - Total column of peroxy radical from hc8 218126: - tc_bigalk - Total column of lumped alkanes 218127: - tc_etep - Total column of peroxy radical from c2h4 218128: - tc_mek - Total column of c4h8o 218129: - tc_oltp - Total column of peroxy radical from terminal alkenes 218130: - tc_eneo2 - Total column of c4h9o3 218131: - tc_olip - Total column of peroxy radical from internal alkenes 218132: - tc_meko2 - Total column of ch3coch(oo)ch3 218133: - tc_isopo2 - Total column of peroxy radical from c5h8 218134: - tc_mekooh - Total column of ch3coch(ooh)ch3 218135: - tc_apip - Total column of peroxy radical from a-pinene cyclic terpenes 218136: - tc_mco3 - Total column of ch2=c(ch3)co3 218137: - tc_limp - Total column of peroxy radical from d-limonene cyclic diene 218138: - tc_mvk - Total column of methylvinylketone 218139: - tc_pho - Total column of phenoxy radical 218140: - tc_tolp - Total column of peroxy radical from toluene and less reactive aromatics 218141: - tc_macro2 - Total column of ch3c(o)ch(oo)ch2oh 218142: - tc_xylp - Total column of peroxy radical from xylene and more reactive aromatics 218143: - tc_macrooh - Total column of h3c(o)ch(ooh)ch2oh 218144: - tc_cslp - Total column of peroxy radical from cresol 218145: - tc_mpan - Total column of unsaturated pans 218146: - tc_tco3_c - Total column of unsaturated acyl peroxy radical 218147: - tc_ketp - Total column of peroxy radical from ketones 218148: - tc_alko2 - Total column of c5h11o2 218149: - tc_olnn - Total column of no3-alkenes adduct reacting to form carbonitrates 218150: - tc_alkooh - Total column of c5h11ooh 218151: - tc_olnd - Total column of no3-alkenes adduct reacting via decomposition 218152: - tc_bigald - Total column of hoch2c(ch3)=chcho 218153: - tc_hydrald - Total column of c5h6o2 218154: - tc_sulf - Total column of trop sulfuric acid 218155: - tc_ox - Total column of oxides 218156: - tc_isopno3 - Total column of ch2chc(ch3)(oo)ch2ono2 218157: - tc_onitr - Total column of c3 organic nitrate 218158: - tc_clox - Total column of chlorine oxides 218159: - tc_brox - Total column of bromine oxides 218160: - tc_xooh - Total column of hoch2c(ooh)(ch3)chchoh 218161: - tc_isopooh - Total column of hoch2c(ooh)(ch3)ch=ch2 218162: - tc_toluene - Total column of lumped aromatics 218163: - tc_dmso - Total column of dimethyl sulfoxyde 218164: - tc_tolo2 - Total column of c7h9o5 218165: - tc_tolooh - Total column of c7h10o5 218166: - tc_h2s - Total column of hydrogensulfide 218167: - tc_xoh - Total column of c7h10o6 218168: - tc_noy - Total column of all nitrogen oxides 218169: - tc_cly - Total column of chlorine family 218170: - tc_terpo2 - Total column of c10h16(oh)(oo) 218171: - tc_bry - Total column of bromine family 218172: - tc_terpooh - Total column of c10h18o3 218173: - tc_n - Total column of nitrogen atom 218174: - tc_clo - Total column of chlorine monoxide 218175: - tc_cl_c - Total column of chlorine atom 218176: - tc_bro - Total column of bromine monoxide 218177: - tc_h_c - Total column of hydrogen atom 218178: - tc_ch3 - Total column of methyl group 218179: - tc_addt - Total column of aromatic-ho from toluene and less reactive aromatics 218180: - tc_addx - Total column of aromatic-ho from xylene and more reactive aromatics 218181: - tc_nh4no3 - Total column of ammonium nitrate 218182: - tc_addc - Total column of aromatic-ho from csl 218183: - tc_soa1 - Total column of secondary organic aerosol type 1 218184: - tc_soa2a - Total column of secondary organic aerosol type 2a 218185: - tc_soa2b - Total column of secondary organic aerosol type 2b 218186: - tc_sog1 - Total column of condensable gas type 1 218187: - tc_sog2a - Total column of condensable gas type 2a 218188: - tc_sog2b - Total column of condensable gas type 2b 218189: - tc_so3 - Total column of sulfur trioxide 218190: - tc_ocs_c - Total column of carbonyl sulfide 218191: - tc_br - Total column of bromine atom 218192: - tc_br2 - Total column of bromine 218193: - tc_brcl - Total column of bromine monochloride 218194: - tc_brono2 - Total column of bromine nitrate 218195: - tc_ch2br2 - Total column of dibromomethane 218196: - tc_ch3o - Total column of methoxy radical 218197: - tc_chbr3 - Total column of tribromomethane 218198: - tc_cloo - Total column of asymmetric chlorine dioxide radical 218199: - tc_h2 - Total column of hydrogen 218200: - tc_hcl - Total column of hydrogen chloride 218201: - tc_hco - Total column of formyl radical 218202: - tc_hf - Total column of hydrogen fluoride 218203: - tc_o - Total column of oxygen atom 218204: - tc_o1d - Total column of excited oxygen atom 218205: - tc_o3p - Total column of ground state oxygen atom 218206: - tc_strataer - Total column of stratospheric aerosol 218221: - tc_vso2 - Column integrated mass density of Volcanic sulfur dioxide 218222: - tc_aroo2 - Column integrated mass density of Aromatic peroxy radical 218223: - tc_c2h2 - Column integrated mass density of Ethyne 218224: - tc_ch3cn - Column integrated mass density of Acetonitrile 218225: - tc_ch3o2no2 - Column integrated mass density of Methyl peroxy nitrate 218226: - tc_hcn - Column integrated mass density of Hydrogen cyanide 218227: - tc_hpald1 - Column integrated mass density of Hydroperoxy aldehydes type 1 218228: - tc_hpald2 - Column integrated mass density of Hydroperoxy aldehydes type 2 218229: - tc_isopbo2 - Column integrated mass density of Isoprene peroxy type B 218230: - tc_isopdo2 - Column integrated mass density of Isoprene peroxy type D 218231: - tc_voca - Column integrated mass density of Anthropogenic volatile organic compounds 218232: - tc_vocbb - Column integrated mass density of Biomass burning volatile organic compounds 219001: - e_go3 - Ozone emissions 219002: - e_nox - Nitrogen oxides emissions 219003: - e_h2o2 - Hydrogen peroxide emissions 219004: - e_ch4 - Methane emissions 219005: - e_co - Carbon monoxide emissions 219006: - e_hno3 - Nitric acid emissions 219007: - e_ch3ooh - Methyl peroxide emissions 219008: - e_hcho - Formaldehyde emissions 219009: - e_par - Paraffins emissions 219010: - e_c2h4 - Ethene emissions 219011: - e_ole - Olefins emissions 219012: - e_ald2 - Aldehydes emissions 219013: - e_pan - Peroxyacetyl nitrate emissions 219014: - e_rooh - Peroxides emissions 219015: - e_onit - Organic nitrates emissions 219016: - e_c5h8 - Isoprene emissions 219017: - e_so2 - Sulfur dioxide emissions 219018: - e_dms - Dimethyl sulfide emissions 219019: - e_nh3 - Ammonia emissions 219020: - e_so4 - Sulfate emissions 219021: - e_nh4 - Ammonium emissions 219022: - e_msa - Methane sulfonic acid emissions 219023: - e_ch3cocho - Methyl glyoxal emissions 219024: - e_o3s - Stratospheric ozone emissions 219025: - e_ra - Radon emissions 219026: - e_pb - Lead emissions 219027: - e_no - Nitrogen monoxide emissions 219028: - e_ho2 - Hydroperoxy radical emissions 219029: - e_ch3o2 - Methylperoxy radical emissions 219030: - e_oh - Hydroxyl radical emissions 219031: - e_no2 - Nitrogen dioxide emissions 219032: - e_no3 - Nitrate radical emissions 219033: - e_n2o5 - Dinitrogen pentoxide emissions 219034: - e_ho2no2 - Pernitric acid emissions 219035: - e_c2o3 - Peroxy acetyl radical emissions 219036: - e_ror - Organic ethers emissions 219037: - e_rxpar - PAR budget corrector emissions 219038: - e_xo2 - NO to NO2 operator emissions 219039: - e_xo2n - NO to alkyl nitrate operator emissions 219040: - e_nh2 - Amine emissions 219041: - e_psc - Polar stratospheric cloud emissions 219042: - e_ch3oh - Methanol emissions 219043: - e_hcooh - Formic acid emissions 219044: - e_mcooh - Methacrylic acid emissions 219045: - e_c2h6 - Ethane emissions 219046: - e_c2h5oh - Ethanol emissions 219047: - e_c3h8 - Propane emissions 219048: - e_c3h6 - Propene emissions 219049: - e_c10h16 - Terpenes emissions 219050: - e_ispd - Methacrolein MVK emissions 219051: - e_no3_a - Nitrate emissions 219052: - e_ch3coch3 - Acetone emissions 219053: - e_aco2 - Acetone product emissions 219054: - e_ic3h7o2 - IC3H7O2 emissions 219055: - e_hypropo2 - HYPROPO2 emissions 219056: - e_noxa - Nitrogen oxides Transp emissions 219057: - e_co2_c - Emissions of carbon dioxide (chemistry) 219058: - e_n2o_c - Emissions of nitrous oxide (chemistry) 219059: - e_h2o - Emissions of water vapour (chemistry) 219060: - e_o2 - Emissions of oxygen 219061: - e_o2_1s - Emissions of singlet oxygen 219062: - e_o2_1d - Emissions of singlet delta oxygen 219063: - e_oclo - Emissions of chlorine dioxide 219064: - e_clono2 - Emissions of chlorine nitrate 219065: - e_hocl - Emissions of hypochlorous acid 219066: - e_cl2 - Emissions of chlorine 219067: - e_clno2 - Emissions of nitryl chloride 219068: - e_hbr - Emissions of hydrogen bromide 219069: - e_cl2o2 - Emissions of dichlorine dioxide 219070: - e_hobr - Emissions of hypobromous acid 219071: - e_cfc11 - Emissions of trichlorofluoromethane 219072: - e_cfc12 - Emissions of dichlorodifluoromethane 219073: - e_cfc113 - Emissions of trichlorotrifluoroethane 219074: - e_cfc114 - Emissions of dichlorotetrafluoroethane 219075: - e_cfc115 - Emissions of chloropentafluoroethane 219076: - e_ccl4 - Emissions of tetrachloromethane 219077: - e_ch3ccl3 - Emissions of methyl chloroform 219078: - e_ch3cl - Emissions of methyl chloride 219079: - e_hcfc22 - Emissions of chlorodifluoromethane 219080: - e_ch3br - Emissions of methyl bromide 219081: - e_ha1202 - Emissions of dibromodifluoromethane 219082: - e_ha1211 - Emissions of bromochlorodifluoromethane 219083: - e_ha1301 - Emissions of trifluorobromomethane 219084: - e_ha2402 - Emissions of cbrf2cbrf2 219085: - e_h2so4 - Emissions of sulfuric acid 219086: - e_hono - Emissions of nitrous acid 219087: - e_hc3 - Emissions of alkanes low oh rate 219088: - e_hc5 - Emissions of alkanes med oh rate 219089: - e_hc8 - Emissions of alkanes high oh rate 219090: - e_olt - Emissions of terminal alkenes 219091: - e_oli - Emissions of internal alkenes 219092: - e_c2h5o2 - Emissions of ethylperoxy radical 219093: - e_dien - Emissions of butadiene 219094: - e_c2h5ooh - Emissions of ethyl hydroperoxide 219095: - e_api - Emissions of a-pinene cyclic terpenes 219096: - e_ch3cooh - Emissions of acetic acid 219097: - e_lim - Emissions of d-limonene cyclic diene 219098: - e_ch3cho - Emissions of acetaldehyde 219099: - e_tol - Emissions of toluene and less reactive aromatics 219100: - e_xyl - Emissions of xylene and more reactive aromatics 219101: - e_glyald - Emissions of glycolaldehyde 219102: - e_cresol - Emissions of cresol 219103: - e_ald - Emissions of acetaldehyde and higher 219104: - e_ch3coooh - Emissions of peracetic acid 219105: - e_ket - Emissions of ketones 219106: - e_eo2 - Emissions of hoch2ch2o2 219107: - e_glyoxal - Emissions of glyoxal 219108: - e_eo - Emissions of hoch2ch2o 219109: - e_dcb - Emissions of unsaturated dicarbonyls 219110: - e_macr - Emissions of methacrolein 219111: - e_udd - Emissions of unsaturated hydroxy dicarbonyl 219112: - e_c3h7o2 - Emissions of isopropyldioxidanyl 219113: - e_hket - Emissions of hydroxy ketone 219114: - e_c3h7ooh - Emissions of isopropyl hydroperoxide 219115: - e_po2 - Emissions of c3h6oho2 219116: - e_pooh - Emissions of c3h6ohooh 219117: - e_op2 - Emissions of higher organic peroxides 219118: - e_hyac - Emissions of hydroxyacetone 219119: - e_paa - Emissions of peroxyacetic acid 219120: - e_ro2 - Emissions of ch3coch2o2 219121: - e_ethp - Emissions of peroxy radical from c2h6 219122: - e_hc3p - Emissions of peroxy radical from hc3 219123: - e_hc5p - Emissions of peroxy radical from hc5 219124: - e_bigene - Emissions of lumped alkenes 219125: - e_hc8p - Emissions of peroxy radical from hc8 219126: - e_bigalk - Emissions of lumped alkanes 219127: - e_etep - Emissions of peroxy radical from c2h4 219128: - e_mek - Emissions of c4h8o 219129: - e_oltp - Emissions of peroxy radical from terminal alkenes 219130: - e_eneo2 - Emissions of c4h9o3 219131: - e_olip - Emissions of peroxy radical from internal alkenes 219132: - e_meko2 - Emissions of ch3coch(oo)ch3 219133: - e_isopo2 - Emissions of peroxy radical from c5h8 219134: - e_mekooh - Emissions of ch3coch(ooh)ch3 219135: - e_apip - Emissions of peroxy radical from a-pinene cyclic terpenes 219136: - e_mco3 - Emissions of ch2=c(ch3)co3 219137: - e_limp - Emissions of peroxy radical from d-limonene cyclic diene 219138: - e_mvk - Emissions of methylvinylketone 219139: - e_pho - Emissions of phenoxy radical 219140: - e_tolp - Emissions of peroxy radical from toluene and less reactive aromatics 219141: - e_macro2 - Emissions of ch3c(o)ch(oo)ch2oh 219142: - e_xylp - Emissions of peroxy radical from xylene and more reactive aromatics 219143: - e_macrooh - Emissions of h3c(o)ch(ooh)ch2oh 219144: - e_cslp - Emissions of peroxy radical from cresol 219145: - e_mpan - Emissions of unsaturated pans 219146: - e_tco3_c - Emissions of unsaturated acyl peroxy radical 219147: - e_ketp - Emissions of peroxy radical from ketones 219148: - e_alko2 - Emissions of c5h11o2 219149: - e_olnn - Emissions of no3-alkenes adduct reacting to form carbonitrates 219150: - e_alkooh - Emissions of c5h11ooh 219151: - e_olnd - Emissions of no3-alkenes adduct reacting via decomposition 219152: - e_bigald - Emissions of hoch2c(ch3)=chcho 219153: - e_hydrald - Emissions of c5h6o2 219154: - e_sulf - Emissions of trop sulfuric acid 219155: - e_ox - Emissions of oxides 219156: - e_isopno3 - Emissions of ch2chc(ch3)(oo)ch2ono2 219157: - e_onitr - Emissions of c3 organic nitrate 219158: - e_clox - Emissions of chlorine oxides 219159: - e_brox - Emissions of bromine oxides 219160: - e_xooh - Emissions of hoch2c(ooh)(ch3)chchoh 219161: - e_isopooh - Emissions of hoch2c(ooh)(ch3)ch=ch2 219162: - e_toluene - Emissions of lumped aromatics 219163: - e_dmso - Emissions of dimethyl sulfoxyde 219164: - e_tolo2 - Emissions of c7h9o5 219165: - e_tolooh - Emissions of c7h10o5 219166: - e_h2s - Emissions of hydrogensulfide 219167: - e_xoh - Emissions of c7h10o6 219168: - e_noy - Emissions of all nitrogen oxides 219169: - e_cly - Emissions of chlorine family 219170: - e_terpo2 - Emissions of c10h16(oh)(oo) 219171: - e_bry - Emissions of bromine family 219172: - e_terpooh - Emissions of c10h18o3 219173: - e_n - Emissions of nitrogen atom 219174: - e_clo - Emissions of chlorine monoxide 219175: - e_cl_c - Emissions of chlorine atom 219176: - e_bro - Emissions of bromine monoxide 219177: - e_h_c - Emissions of hydrogen atom 219178: - e_ch3 - Emissions of methyl group 219179: - e_addt - Emissions of aromatic-ho from toluene and less reactive aromatics 219180: - e_addx - Emissions of aromatic-ho from xylene and more reactive aromatics 219181: - e_nh4no3 - Emissions of ammonium nitrate 219182: - e_addc - Emissions of aromatic-ho from csl 219183: - e_soa1 - Emissions of secondary organic aerosol type 1 219184: - e_soa2a - Emissions of secondary organic aerosol type 2a 219185: - e_soa2b - Emissions of secondary organic aerosol type 2b 219186: - e_sog1 - Emissions of condensable gas type 1 219187: - e_sog2a - Emissions of condensable gas type 2a 219188: - e_sog2b - Emissions of condensable gas type 2b 219189: - e_so3 - Emissions of sulfur trioxide 219190: - e_ocs_c - Emissions of carbonyl sulfide 219191: - e_br - Emissions of bromine atom 219192: - e_br2 - Emissions of bromine 219193: - e_brcl - Emissions of bromine monochloride 219194: - e_brono2 - Emissions of bromine nitrate 219195: - e_ch2br2 - Emissions of dibromomethane 219196: - e_ch3o - Emissions of methoxy radical 219197: - e_chbr3 - Emissions of tribromomethane 219198: - e_cloo - Emissions of asymmetric chlorine dioxide radical 219199: - e_h2 - Emissions of hydrogen 219200: - e_hcl - Emissions of hydrogen chloride 219201: - e_hco - Emissions of formyl radical 219202: - e_hf - Emissions of hydrogen fluoride 219203: - e_o - Emissions of oxygen atom 219204: - e_o1d - Emissions of excited oxygen atom 219205: - e_o3p - Emissions of ground state oxygen atom 219206: - e_strataer - Emissions of stratospheric aerosol 219207: - parfire - Wildfire flux of paraffins 219208: - olefire - Wildfire flux of olefines 219209: - ald2fire - Wildfire flux of aldehydes 219210: - ketfire - Wildfire flux of ketones 219211: - apifire - Wildfire flux of f a-pinene cyclic terpenes 219212: - tolfire - Wildfire flux of toluene less reactive aromatics 219213: - xylfire - Wildfire flux of xylene more reactive aromatics 219214: - limfire - Wildfire flux of d-limonene cyclic diene 219215: - oltfire - Wildfire flux of terminal alkenes 219216: - hc3fire - Wildfire flux of alkanes low oh rate 219217: - hc5fire - Wildfire flux of alkanes med oh rate 219218: - hc8fire - Wildfire flux of alkanes high oh rate 219219: - hcnfire - Wildfire flux of hydrogen cyanide 219220: - ch3cnfire - Wildfire flux of acetonitrile 219221: - e_vso2 - Atmosphere emission mass flux of Volcanic sulfur dioxide 219222: - e_aroo2 - Atmosphere emission mass flux of Aromatic peroxy radical 219223: - e_c2h2 - Atmosphere emission mass flux of Ethyne 219224: - e_ch3cn - Atmosphere emission mass flux of Acetonitrile 219225: - e_ch3o2no2 - Atmosphere emission mass flux of Methyl peroxy nitrate 219226: - e_hcn - Atmosphere emission mass flux of Hydrogen cyanide 219227: - e_hpald1 - Atmosphere emission mass flux of Hydroperoxy aldehydes type 1 219228: - e_hpald2 - Atmosphere emission mass flux of Hydroperoxy aldehydes type 2 219229: - e_isopbo2 - Atmosphere emission mass flux of Isoprene peroxy type B 219230: - e_isopdo2 - Atmosphere emission mass flux of Isoprene peroxy type D 219231: - e_voca - Atmosphere emission mass flux of Anthropogenic volatile organic compounds 219232: - e_vocbb - Atmosphere emission mass flux of Biomass burning volatile organic compounds 220228: - tpoc - Total precipitation observation count 221001: - dv_go3 - Ozone deposition velocity 221002: - dv_nox - Nitrogen oxides deposition velocity 221003: - dv_h2o2 - Hydrogen peroxide deposition velocity 221004: - dv_ch4 - Methane deposition velocity 221005: - dv_co - Carbon monoxide deposition velocity 221006: - dv_hno3 - Nitric acid deposition velocity 221007: - dv_ch3ooh - Methyl peroxide deposition velocity 221008: - dv_hcho - Formaldehyde deposition velocity 221009: - dv_par - Paraffins deposition velocity 221010: - dv_c2h4 - Ethene deposition velocity 221011: - dv_ole - Olefins deposition velocity 221012: - dv_ald2 - Aldehydes deposition velocity 221013: - dv_pan - Peroxyacetyl nitrate deposition velocity 221014: - dv_rooh - Peroxides deposition velocity 221015: - dv_onit - Organic nitrates deposition velocity 221016: - dv_c5h8 - Isoprene deposition velocity 221017: - dv_so2 - Sulfur dioxide deposition velocity 221018: - dv_dms - Dimethyl sulfide deposition velocity 221019: - dv_nh3 - Ammonia deposition velocity 221020: - dv_so4 - Sulfate deposition velocity 221021: - dv_nh4 - Ammonium deposition velocity 221022: - dv_msa - Methane sulfonic acid deposition velocity 221023: - dv_ch3cocho - Methyl glyoxal deposition velocity 221024: - dv_o3s - Stratospheric ozone deposition velocity 221025: - dv_ra - Radon deposition velocity 221026: - dv_pb - Lead deposition velocity 221027: - dv_no - Nitrogen monoxide deposition velocity 221028: - dv_ho2 - Hydroperoxy radical deposition velocity 221029: - dv_ch3o2 - Methylperoxy radical deposition velocity 221030: - dv_oh - Hydroxyl radical deposition velocity 221031: - dv_no2 - Nitrogen dioxide deposition velocity 221032: - dv_no3 - Nitrate radical deposition velocity 221033: - dv_n2o5 - Dinitrogen pentoxide deposition velocity 221034: - dv_ho2no2 - Pernitric acid deposition velocity 221035: - dv_c2o3 - Peroxy acetyl radical deposition velocity 221036: - dv_ror - Organic ethers deposition velocity 221037: - dv_rxpar - PAR budget corrector deposition velocity 221038: - dv_xo2 - NO to NO2 operator deposition velocity 221039: - dv_xo2n - NO to alkyl nitrate operator deposition velocity 221040: - dv_nh2 - Amine deposition velocity 221041: - dv_psc - Polar stratospheric cloud deposition velocity 221042: - dv_ch3oh - Methanol deposition velocity 221043: - dv_hcooh - Formic acid deposition velocity 221044: - dv_mcooh - Methacrylic acid deposition velocity 221045: - dv_c2h6 - Ethane deposition velocity 221046: - dv_c2h5oh - Ethanol deposition velocity 221047: - dv_c3h8 - Propane deposition velocity 221048: - dv_c3h6 - Propene deposition velocity 221049: - dv_c10h16 - Terpenes deposition velocity 221050: - dv_ispd - Methacrolein MVK deposition velocity 221051: - dv_no3_a - Nitrate deposition velocity 221052: - dv_ch3coch3 - Acetone deposition velocity 221053: - dv_aco2 - Acetone product deposition velocity 221054: - dv_ic3h7o2 - IC3H7O2 deposition velocity 221055: - dv_hypropo2 - HYPROPO2 deposition velocity 221056: - dv_noxa - Nitrogen oxides Transp deposition velocity 221057: - dv_co2_c - Dry deposition velocity of carbon dioxide (chemistry) 221058: - dv_n2o_c - Dry deposition velocity of nitrous oxide (chemistry) 221059: - dv_h2o - Dry deposition velocity of water vapour (chemistry) 221060: - dv_o2 - Dry deposition velocity of oxygen 221061: - dv_o2_1s - Dry deposition velocity of singlet oxygen 221062: - dv_o2_1d - Dry deposition velocity of singlet delta oxygen 221063: - dv_oclo - Dry deposition velocity of chlorine dioxide 221064: - dv_clono2 - Dry deposition velocity of chlorine nitrate 221065: - dv_hocl - Dry deposition velocity of hypochlorous acid 221066: - dv_cl2 - Dry deposition velocity of chlorine 221067: - dv_clno2 - Dry deposition velocity of nitryl chloride 221068: - dv_hbr - Dry deposition velocity of hydrogen bromide 221069: - dv_cl2o2 - Dry deposition velocity of dichlorine dioxide 221070: - dv_hobr - Dry deposition velocity of hypobromous acid 221071: - dv_cfc11 - Dry deposition velocity of trichlorofluoromethane 221072: - dv_cfc12 - Dry deposition velocity of dichlorodifluoromethane 221073: - dv_cfc113 - Dry deposition velocity of trichlorotrifluoroethane 221074: - dv_cfc114 - Dry deposition velocity of dichlorotetrafluoroethane 221075: - dv_cfc115 - Dry deposition velocity of chloropentafluoroethane 221076: - dv_ccl4 - Dry deposition velocity of tetrachloromethane 221077: - dv_ch3ccl3 - Dry deposition velocity of methyl chloroform 221078: - dv_ch3cl - Dry deposition velocity of methyl chloride 221079: - dv_hcfc22 - Dry deposition velocity of chlorodifluoromethane 221080: - dv_ch3br - Dry deposition velocity of methyl bromide 221081: - dv_ha1202 - Dry deposition velocity of dibromodifluoromethane 221082: - dv_ha1211 - Dry deposition velocity of bromochlorodifluoromethane 221083: - dv_ha1301 - Dry deposition velocity of trifluorobromomethane 221084: - dv_ha2402 - Dry deposition velocity of cbrf2cbrf2 221085: - dv_h2so4 - Dry deposition velocity of sulfuric acid 221086: - dv_hono - Dry deposition velocity of nitrous acid 221087: - dv_hc3 - Dry deposition velocity of alkanes low oh rate 221088: - dv_hc5 - Dry deposition velocity of alkanes med oh rate 221089: - dv_hc8 - Dry deposition velocity of alkanes high oh rate 221090: - dv_olt - Dry deposition velocity of terminal alkenes 221091: - dv_oli - Dry deposition velocity of internal alkenes 221092: - dv_c2h5o2 - Dry deposition velocity of ethylperoxy radical 221093: - dv_dien - Dry deposition velocity of butadiene 221094: - dv_c2h5ooh - Dry deposition velocity of ethyl hydroperoxide 221095: - dv_api - Dry deposition velocity of a-pinene cyclic terpenes 221096: - dv_ch3cooh - Dry deposition velocity of acetic acid 221097: - dv_lim - Dry deposition velocity of d-limonene cyclic diene 221098: - dv_ch3cho - Dry deposition velocity of acetaldehyde 221099: - dv_tol - Dry deposition velocity of toluene and less reactive aromatics 221100: - dv_xyl - Dry deposition velocity of xylene and more reactive aromatics 221101: - dv_glyald - Dry deposition velocity of glycolaldehyde 221102: - dv_cresol - Dry deposition velocity of cresol 221103: - dv_ald - Dry deposition velocity of acetaldehyde and higher 221104: - dv_ch3coooh - Dry deposition velocity of peracetic acid 221105: - dv_ket - Dry deposition velocity of ketones 221106: - dv_eo2 - Dry deposition velocity of hoch2ch2o2 221107: - dv_glyoxal - Dry deposition velocity of glyoxal 221108: - dv_eo - Dry deposition velocity of hoch2ch2o 221109: - dv_dcb - Dry deposition velocity of unsaturated dicarbonyls 221110: - dv_macr - Dry deposition velocity of methacrolein 221111: - dv_udd - Dry deposition velocity of unsaturated hydroxy dicarbonyl 221112: - dv_c3h7o2 - Dry deposition velocity of isopropyldioxidanyl 221113: - dv_hket - Dry deposition velocity of hydroxy ketone 221114: - dv_c3h7ooh - Dry deposition velocity of isopropyl hydroperoxide 221115: - dv_po2 - Dry deposition velocity of c3h6oho2 221116: - dv_pooh - Dry deposition velocity of c3h6ohooh 221117: - dv_op2 - Dry deposition velocity of higher organic peroxides 221118: - dv_hyac - Dry deposition velocity of hydroxyacetone 221119: - dv_paa - Dry deposition velocity of peroxyacetic acid 221120: - dv_ro2 - Dry deposition velocity of ch3coch2o2 221121: - dv_ethp - Dry deposition velocity of peroxy radical from c2h6 221122: - dv_hc3p - Dry deposition velocity of peroxy radical from hc3 221123: - dv_hc5p - Dry deposition velocity of peroxy radical from hc5 221124: - dv_bigene - Dry deposition velocity of lumped alkenes 221125: - dv_hc8p - Dry deposition velocity of peroxy radical from hc8 221126: - dv_bigalk - Dry deposition velocity of lumped alkanes 221127: - dv_etep - Dry deposition velocity of peroxy radical from c2h4 221128: - dv_mek - Dry deposition velocity of c4h8o 221129: - dv_oltp - Dry deposition velocity of peroxy radical from terminal alkenes 221130: - dv_eneo2 - Dry deposition velocity of c4h9o3 221131: - dv_olip - Dry deposition velocity of peroxy radical from internal alkenes 221132: - dv_meko2 - Dry deposition velocity of ch3coch(oo)ch3 221133: - dv_isopo2 - Dry deposition velocity of peroxy radical from c5h8 221134: - dv_mekooh - Dry deposition velocity of ch3coch(ooh)ch3 221135: - dv_apip - Dry deposition velocity of peroxy radical from a-pinene cyclic terpenes 221136: - dv_mco3 - Dry deposition velocity of ch2=c(ch3)co3 221137: - dv_limp - Dry deposition velocity of peroxy radical from d-limonene cyclic diene 221138: - dv_mvk - Dry deposition velocity of methylvinylketone 221139: - dv_pho - Dry deposition velocity of phenoxy radical 221140: - dv_tolp - Dry deposition velocity of peroxy radical from toluene and less reactive aromatics 221141: - dv_macro2 - Dry deposition velocity of ch3c(o)ch(oo)ch2oh 221142: - dv_xylp - Dry deposition velocity of peroxy radical from xylene and more reactive aromatics 221143: - dv_macrooh - Dry deposition velocity of h3c(o)ch(ooh)ch2oh 221144: - dv_cslp - Dry deposition velocity of peroxy radical from cresol 221145: - dv_mpan - Dry deposition velocity of unsaturated pans 221146: - dv_tco3_c - Dry deposition velocity of unsaturated acyl peroxy radical 221147: - dv_ketp - Dry deposition velocity of peroxy radical from ketones 221148: - dv_alko2 - Dry deposition velocity of c5h11o2 221149: - dv_olnn - Dry deposition velocity of no3-alkenes adduct reacting to form carbonitrates 221150: - dv_alkooh - Dry deposition velocity of c5h11ooh 221151: - dv_olnd - Dry deposition velocity of no3-alkenes adduct reacting via decomposition 221152: - dv_bigald - Dry deposition velocity of hoch2c(ch3)=chcho 221153: - dv_hydrald - Dry deposition velocity of c5h6o2 221154: - dv_sulf - Dry deposition velocity of trop sulfuric acid 221155: - dv_ox - Dry deposition velocity of oxides 221156: - dv_isopno3 - Dry deposition velocity of ch2chc(ch3)(oo)ch2ono2 221157: - dv_onitr - Dry deposition velocity of c3 organic nitrate 221158: - dv_clox - Dry deposition velocity of chlorine oxides 221159: - dv_brox - Dry deposition velocity of bromine oxides 221160: - dv_xooh - Dry deposition velocity of hoch2c(ooh)(ch3)chchoh 221161: - dv_isopooh - Dry deposition velocity of hoch2c(ooh)(ch3)ch=ch2 221162: - dv_toluene - Dry deposition velocity of lumped aromatics 221163: - dv_dmso - Dry deposition velocity of dimethyl sulfoxyde 221164: - dv_tolo2 - Dry deposition velocity of c7h9o5 221165: - dv_tolooh - Dry deposition velocity of c7h10o5 221166: - dv_h2s - Dry deposition velocity of hydrogensulfide 221167: - dv_xoh - Dry deposition velocity of c7h10o6 221168: - dv_noy - Dry deposition velocity of all nitrogen oxides 221169: - dv_cly - Dry deposition velocity of chlorine family 221170: - dv_terpo2 - Dry deposition velocity of c10h16(oh)(oo) 221171: - dv_bry - Dry deposition velocity of bromine family 221172: - dv_terpooh - Dry deposition velocity of c10h18o3 221173: - dv_n - Dry deposition velocity of nitrogen atom 221174: - dv_clo - Dry deposition velocity of chlorine monoxide 221175: - dv_cl_c - Dry deposition velocity of chlorine atom 221176: - dv_bro - Dry deposition velocity of bromine monoxide 221177: - dv_h_c - Dry deposition velocity of hydrogen atom 221178: - dv_ch3 - Dry deposition velocity of methyl group 221179: - dv_addt - Dry deposition velocity of aromatic-ho from toluene and less reactive aromatics 221180: - dv_addx - Dry deposition velocity of aromatic-ho from xylene and more reactive aromatics 221181: - dv_nh4no3 - Dry deposition velocity of ammonium nitrate 221182: - dv_addc - Dry deposition velocity of aromatic-ho from csl 221183: - dv_soa1 - Dry deposition velocity of secondary organic aerosol type 1 221184: - dv_soa2a - Dry deposition velocity of secondary organic aerosol type 2a 221185: - dv_soa2b - Dry deposition velocity of secondary organic aerosol type 2b 221186: - dv_sog1 - Dry deposition velocity of condensable gas type 1 221187: - dv_sog2a - Dry deposition velocity of condensable gas type 2a 221188: - dv_sog2b - Dry deposition velocity of condensable gas type 2b 221189: - dv_so3 - Dry deposition velocity of sulfur trioxide 221190: - dv_ocs_c - Dry deposition velocity of carbonyl sulfide 221191: - dv_br - Dry deposition velocity of bromine atom 221192: - dv_br2 - Dry deposition velocity of bromine 221193: - dv_brcl - Dry deposition velocity of bromine monochloride 221194: - dv_brono2 - Dry deposition velocity of bromine nitrate 221195: - dv_ch2br2 - Dry deposition velocity of dibromomethane 221196: - dv_ch3o - Dry deposition velocity of methoxy radical 221197: - dv_chbr3 - Dry deposition velocity of tribromomethane 221198: - dv_cloo - Dry deposition velocity of asymmetric chlorine dioxide radical 221199: - dv_h2 - Dry deposition velocity of hydrogen 221200: - dv_hcl - Dry deposition velocity of hydrogen chloride 221201: - dv_hco - Dry deposition velocity of formyl radical 221202: - dv_hf - Dry deposition velocity of hydrogen fluoride 221203: - dv_o - Dry deposition velocity of oxygen atom 221204: - dv_o1d - Dry deposition velocity of excited oxygen atom 221205: - dv_o3p - Dry deposition velocity of ground state oxygen atom 221206: - dv_strataer - Dry deposition velocity of stratospheric aerosol 221221: - dv_vso2 - Dry deposition velocity of Volcanic sulfur dioxide 221222: - dv_aroo2 - Dry deposition velocity of Aromatic peroxy radical 221223: - dv_c2h2 - Dry deposition velocity of Ethyne 221224: - dv_ch3cn - Dry deposition velocity of Acetonitrile 221225: - dv_ch3o2no2 - Dry deposition velocity of Methyl peroxy nitrate 221226: - dv_hcn - Dry deposition velocity of Hydrogen cyanide 221227: - dv_hpald1 - Dry deposition velocity of Hydroperoxy aldehydes type 1 221228: - dv_hpald2 - Dry deposition velocity of Hydroperoxy aldehydes type 2 221229: - dv_isopbo2 - Dry deposition velocity of Isoprene peroxy type B 221230: - dv_isopdo2 - Dry deposition velocity of Isoprene peroxy type D 221231: - dv_voca - Dry deposition velocity of Anthropogenic volatile organic compounds 221232: - dv_vocbb - Dry deposition velocity of Biomass burning volatile organic compounds 222001: - acc_dry_depm_o3 - Time-integrated dry deposition mass flux of Ozone 222003: - acc_dry_depm_h2o2 - Time-integrated dry deposition mass flux of Hydrogen peroxide 222005: - acc_dry_depm_co - Time-integrated dry deposition mass flux of Carbon monoxide 222006: - acc_dry_depm_hno3 - Time-integrated dry deposition mass flux of Nitric acid 222007: - acc_dry_depm_ch3ooh - Time-integrated dry deposition mass flux of Methyl peroxide 222008: - acc_dry_depm_hcho - Time-integrated dry deposition mass flux of Formaldehyde 222012: - acc_dry_depm_ald2 - Time-integrated dry deposition mass flux of Aldehydes 222013: - acc_dry_depm_pan - Time-integrated dry deposition mass flux of Peroxyacetyl nitrate 222014: - acc_dry_depm_rooh - Time-integrated dry deposition mass flux of Peroxides 222015: - acc_dry_depm_onit - Time-integrated dry deposition mass flux of Organic nitrates 222016: - acc_dry_depm_c5h8 - Time-integrated dry deposition mass flux of Isoprene 222017: - acc_dry_depm_so2 - Time-integrated dry deposition mass flux of Sulphur dioxide 222019: - acc_dry_depm_nh3 - Time-integrated dry deposition mass flux of Ammonia 222020: - acc_dry_depm_so4 - Time-integrated dry deposition mass flux of Sulfate 222021: - acc_dry_depm_nh4 - Time-integrated dry deposition mass flux of Ammonium 222023: - acc_dry_depm_ch3cocho - Time-integrated dry deposition mass flux of Methyl glyoxal 222024: - acc_dry_depm_o3s - Time-integrated dry deposition mass flux of Ozone (stratospheric) 222027: - acc_dry_depm_no - Time-integrated dry deposition mass flux of Nitrogen monoxide 222028: - acc_dry_depm_ho2 - Time-integrated dry deposition mass flux of Hydroperoxy radical 222029: - acc_dry_depm_ch3o2 - Time-integrated dry deposition mass flux of Methylperoxy radical 222031: - acc_dry_depm_no2 - Time-integrated dry deposition mass flux of Nitrogen dioxide 222032: - acc_dry_depm_no3 - Time-integrated dry deposition mass flux of Nitrate radical 222033: - acc_dry_depm_n2o5 - Time-integrated dry deposition mass flux of Dinitrogen pentoxide 222034: - acc_dry_depm_ho2no2 - Time-integrated dry deposition mass flux of Pernitric acid 222042: - acc_dry_depm_ch3oh - Time-integrated dry deposition mass flux of Methanol 222043: - acc_dry_depm_hcooh - Time-integrated dry deposition mass flux of Formic acid 222044: - acc_dry_depm_mcooh - Time-integrated dry deposition mass flux of Methacrylic acid 222045: - acc_dry_depm_c2h6 - Time-integrated dry deposition mass flux of Ethane 222046: - acc_dry_depm_c2h5oh - Time-integrated dry deposition mass flux of Ethanol 222050: - acc_dry_depm_ispd - Time-integrated dry deposition mass flux of Methacrolein 222051: - acc_dry_depm_no3_a - Time-integrated dry deposition mass flux of Nitrate 222052: - acc_dry_depm_ch3coch3 - Time-integrated dry deposition mass flux of Acetone 222086: - acc_dry_depm_hono - Time-integrated dry deposition mass flux of Nitrous acid 222099: - acc_dry_depm_tol - Time-integrated dry deposition mass flux of Toluene and less reactive aromatics 222100: - acc_dry_depm_xyl - Time-integrated dry deposition mass flux of Xylene and more reactive aromatics 222101: - acc_dry_depm_glyald - Time-integrated dry deposition mass flux of Glycolaldehyde 222107: - acc_dry_depm_gly - Time-integrated dry deposition mass flux of Glyoxal 222118: - acc_dry_depm_hyac - Time-integrated dry deposition mass flux of Hydroxyacetone 222161: - acc_dry_depm_isopooh - Time-integrated dry deposition mass flux of all hydroxy-peroxides products of the reaction of hydroxy-isoprene adducts with O2 222224: - acc_dry_depm_ch3cn - Time-integrated dry deposition mass flux of Acetonitrile 222226: - acc_dry_depm_hcn - Time-integrated dry deposition mass flux of Hydrogen cyanide 223001: - acc_wet_depm_o3 - Time-integrated wet deposition mass flux of Ozone 223003: - acc_wet_depm_h2o2 - Time-integrated wet deposition mass flux of Hydrogen peroxide 223006: - acc_wet_depm_hno3 - Time-integrated wet deposition mass flux of Nitric acid 223007: - acc_wet_depm_ch3ooh - Time-integrated wet deposition mass flux of Methyl peroxide 223008: - acc_wet_depm_hcho - Time-integrated wet deposition mass flux of Formaldehyde 223012: - acc_wet_depm_ald2 - Time-integrated wet deposition mass flux of Aldehydes 223013: - acc_wet_depm_pan - Time-integrated wet deposition mass flux of Peroxyacetyl nitrate 223014: - acc_wet_depm_rooh - Time-integrated wet deposition mass flux of Peroxides 223015: - acc_wet_depm_onit - Time-integrated wet deposition mass flux of Organic nitrates 223016: - acc_wet_depm_c5h8 - Time-integrated wet deposition mass flux of Isoprene 223017: - acc_wet_depm_so2 - Time-integrated wet deposition mass flux of Sulphur dioxide 223019: - acc_wet_depm_nh3 - Time-integrated wet deposition mass flux of Ammonia 223020: - acc_wet_depm_so4 - Time-integrated wet deposition mass flux of Sulfate 223021: - acc_wet_depm_nh4 - Time-integrated wet deposition mass flux of Ammonium 223022: - acc_wet_depm_msa - Time-integrated wet deposition mass flux of Methane sulfonic acid 223023: - acc_wet_depm_ch3cocho - Time-integrated wet deposition mass flux of Methyl glyoxal 223025: - acc_wet_depm_co - Time-integrated wet deposition mass flux of Carbon monoxide 223026: - acc_wet_depm_pb - Time-integrated wet deposition mass flux of Lead 223027: - acc_wet_depm_no - Time-integrated wet deposition mass flux of Nitrogen monoxide 223028: - acc_wet_depm_ho2 - Time-integrated wet deposition mass flux of Hydroperoxy radical 223029: - acc_wet_depm_ch3o2 - Time-integrated wet deposition mass flux of Methylperoxy radical 223031: - acc_wet_depm_no2 - Time-integrated wet deposition mass flux of Nitrogen dioxide 223032: - acc_wet_depm_no3 - Time-integrated wet deposition mass flux of Nitrate radical 223033: - acc_wet_depm_n2o5 - Time-integrated wet deposition mass flux of Dinitrogen pentoxide 223034: - acc_wet_depm_ho2no2 - Time-integrated wet deposition mass flux of Pernitric acid 223042: - acc_wet_depm_ch3oh - Time-integrated wet deposition mass flux of Methanol 223043: - acc_wet_depm_hcooh - Time-integrated wet deposition mass flux of Formic acid 223044: - acc_wet_depm_mcooh - Time-integrated wet deposition mass flux of Methacrylic acid 223045: - acc_wet_depm_c2h6 - Time-integrated wet deposition mass flux of Ethane 223046: - acc_wet_depm_c2h5oh - Time-integrated wet deposition mass flux of Ethanol 223050: - acc_wet_depm_ispd - Time-integrated wet deposition mass flux of Methacrolein 223051: - acc_wet_depm_no3_a - Time-integrated wet deposition mass flux of Nitrate 223052: - acc_wet_depm_ch3coch3 - Time-integrated wet deposition mass flux of Acetone 223064: - acc_wet_depm_clono2 - Time-integrated wet deposition mass flux of Chlorine nitrate 223065: - acc_wet_depm_hocl - Time-integrated wet deposition mass flux of Hypochlorous acid 223068: - acc_wet_depm_hbr - Time-integrated wet deposition mass flux of Hydrogen bromide 223070: - acc_wet_depm_hobr - Time-integrated wet deposition mass flux of Hypobromous acid 223086: - acc_wet_depm_hono - Time-integrated wet deposition mass flux of Nitrous acid 223099: - acc_wet_depm_tol - Time-integrated wet deposition mass flux of Toluene and less reactive aromatics 223100: - acc_wet_depm_xyl - Time-integrated wet deposition mass flux of Xylene and more reactive aromatics 223101: - acc_wet_depm_glyald - Time-integrated wet deposition mass flux of Glycolaldehyde 223107: - acc_wet_depm_gly - Time-integrated wet deposition mass flux of Glyoxal 223118: - acc_wet_depm_hyac - Time-integrated wet deposition mass flux of Hydroxyacetone 223161: - acc_wet_depm_isopooh - Time-integrated wet deposition mass flux of all hydroxy-peroxides products of the reaction of hydroxy-isoprene adducts with O2 223186: - acc_wet_depm_sog1 - Time-integrated wet deposition mass flux of Condensable gas type 1 223187: - acc_wet_depm_sog2a - Time-integrated wet deposition mass flux of Condensable gas type 2a 223188: - acc_wet_depm_sog2b - Time-integrated wet deposition mass flux of Condensable gas type 2b 223194: - acc_wet_depm_brono2 - Time-integrated wet deposition mass flux of Bromine nitrate 223200: - acc_wet_depm_hcl - Time-integrated wet deposition mass flux of Hydrogen chloride 223224: - acc_wet_depm_ch3cn - Time-integrated wet deposition mass flux of Acetonitrile 223226: - acc_wet_depm_hcn - Time-integrated wet deposition mass flux of Hydrogen cyanide 223227: - acc_wet_depm_hpald1 - Time-integrated wet deposition mass flux of Hydroperoxy aldehydes type 1 223228: - acc_wet_depm_hpald2 - Time-integrated wet deposition mass flux of Hydroperoxy aldehydes type 2 228001: - cin - Convective inhibition 228002: - orog - Orography 228003: - zust - Friction velocity 228004: - avg_2t - Time-mean 2 metre temperature 228005: - avg_10ws - Time-mean 10 metre wind speed 228006: - meantcc - Mean total cloud cover 228007: - dl - Lake total depth 228008: - lmlt - Lake mix-layer temperature 228009: - lmld - Lake mix-layer depth 228010: - lblt - Lake bottom temperature 228011: - ltlt - Lake total layer temperature 228012: - lshf - Lake shape factor 228013: - lict - Lake ice surface temperature 228014: - licd - Lake ice total depth 228015: - dndzn - Minimum vertical gradient of refractivity inside trapping layer 228016: - dndza - Mean vertical gradient of refractivity inside trapping layer 228017: - dctb - Duct base height 228018: - tplb - Trapping layer base height 228019: - tplt - Trapping layer top height 228020: - degm10l - Geometric height of -10 degrees C atmospheric isothermal level above ground 228021: - fdir - Surface direct short-wave (solar) radiation 228022: - cdir - Surface direct short-wave radiation, clear sky 228023: - cbh - Cloud base height 228024: - deg0l - Geometric height of 0 degrees C atmospheric isothermal level above ground - deg0 228025: - hvis - Horizontal visibility 228026: - mx2t3 - Maximum temperature at 2 metres in the last 3 hours 228027: - mn2t3 - Minimum temperature at 2 metres in the last 3 hours 228028: - 10fg3 - Maximum 10 metre wind gust in the last 3 hours 228029: - i10fg - Instantaneous 10 metre wind gust 228030: - rhw - Relative humidity with respect to water 228031: - rhi - Relative humidity with respect to ice 228032: - asn - Snow albedo 228034: - fcpc - Fraction of convective precipitation cover 228035: - mxcape6 - Maximum CAPE in the last 6 hours 228036: - mxcapes6 - Maximum CAPES in the last 6 hours 228037: - 2rhw - 2 metre relative humidity with respect to water 228038: - lwcs - Liquid water content in snow pack 228039: - sm - Soil moisture 228040: - swi1 - Soil wetness index in layer 1 228041: - swi2 - Soil wetness index in layer 2 228042: - swi3 - Soil wetness index in layer 3 228043: - swi4 - Soil wetness index in layer 4 228044: - capes - Convective available potential energy shear 228045: - trpp - Tropopause pressure 228046: - hcct - Height of convective cloud top 228047: - hwbt0 - Height of zero-degree wet-bulb temperature 228048: - hwbt1 - Height of one-degree wet-bulb temperature 228050: - litoti - Instantaneous total lightning flash density 228051: - litota1 - Averaged total lightning flash density in the last hour 228052: - licgi - Instantaneous cloud-to-ground lightning flash density 228053: - licga1 - Averaged cloud-to-ground lightning flash density in the last hour 228054: - ucq - Unbalanced component of specific humidity 228055: - ucclwc - Unbalanced component of specific cloud liquid water content 228056: - ucciwc - Unbalanced component of specific cloud ice water content 228057: - litota3 - Averaged total lightning flash density in the last 3 hours 228058: - litota6 - Averaged total lightning flash density in the last 6 hours 228059: - licga3 - Averaged cloud-to-ground lightning flash density in the last 3 hours 228060: - licga6 - Averaged cloud-to-ground lightning flash density in the last 6 hours 228070: - smnnob - SMOS observed soil moisture retrieved using neural network 228071: - smnner - SMOS observed soil moisture uncertainty retrieved using neural network 228072: - smnnrfi - SMOS radio frequency interference probability 228073: - smnnnb - SMOS number of observations per grid point 228074: - smnntim - SMOS observation time for the satellite soil moisture data 228078: - gppbfas - GPP coefficient from Biogenic Flux Adjustment System 228079: - recbfas - Rec coefficient from Biogenic Flux Adjustment System 228080: - aco2nee - Accumulated Carbon Dioxide Net Ecosystem Exchange 228081: - aco2gpp - Accumulated Carbon Dioxide Gross Primary Production 228082: - aco2rec - Accumulated Carbon Dioxide Ecosystem Respiration 228083: - fco2nee - Carbon dioxide net ecosystem exchange flux 228084: - fco2gpp - Carbon dioxide gross primary production flux 228085: - fco2rec - Carbon dioxide ecosystem respiration flux 228086: - sm20 - Soil moisture top 20 cm 228087: - sm100 - Soil moisture top 100 cm 228088: - tcslw - Total column supercooled liquid water 228089: - tcrw - Total column rain water 228090: - tcsw - Total column snow water 228091: - ccf - Canopy cover fraction 228092: - stf - Soil texture fraction 228093: - swv - Volumetric soil moisture 228094: - ist - Ice temperature 228095: - st20 - Soil temperature top 20 cm 228096: - st100 - Soil temperature top 100 cm 228100: - evatc - Evaporation from the top of canopy 228101: - evabs - Evaporation from bare soil 228102: - evaow - Evaporation from open water surfaces excluding oceans 228103: - evavt - Evaporation from vegetation transpiration 228104: - e_wlch4 - Atmosphere emission mass flux of Methane from Wetlands 228105: - sif740 - Solar induced Chlorophyll fluorescence at 740nm 228106: - sif755 - Solar induced Chlorophyll fluorescence at 755nm 228107: - sif771 - Solar induced Chlorophyll fluorescence at 771nm 228108: - sif757 - Solar induced Chlorophyll fluorescence at 757nm 228109: - acc_e_wlch4 - Accumulated mass emission of methane from Wetlands 228129: - ssrdc - Surface short-wave (solar) radiation downward clear-sky 228130: - strdc - Surface long-wave (thermal) radiation downward clear-sky 228131: - u10n - 10 metre u-component of neutral wind 228132: - v10n - 10 metre v-component of neutral wind 228134: - vtnowd - V-tendency from non-orographic wave drag 228136: - utnowd - U-tendency from non-orographic wave drag 228139: - st - Soil temperature 228141: - sd - Snow depth water equivalent 228143: - cp - Convective precipitation 228144: - sf - Snowfall water equivalent 228164: - tcc - Total Cloud Cover 228170: - cap - Field capacity 228171: - wilt - Wilting point 228205: - ro - Water runoff and drainage 228216: - fzra - Accumulated freezing rain 228217: - ilspf - Instantaneous large-scale precipitation fraction 228218: - crr - Convective rain rate 228219: - lsrr - Large scale rain rate 228220: - csfr - Convective snowfall rate water equivalent 228221: - lssfr - Large scale snowfall rate water equivalent 228222: - mxtpr3 - Maximum total precipitation rate in the last 3 hours 228223: - mntpr3 - Minimum total precipitation rate in the last 3 hours 228224: - mxtpr6 - Maximum total precipitation rate in the last 6 hours 228225: - mntpr6 - Minimum total precipitation rate in the last 6 hours 228226: - mxtpr - Maximum total precipitation rate since previous post-processing 228227: - mntpr - Minimum total precipitation rate since previous post-processing 228228: - tp - Total Precipitation 228229: - smos_tb_cdfa - SMOS first Brightness Temperature Bias Correction parameter 228230: - smos_tb_cdfb - SMOS second Brightness Temperature Bias Correction parameter 228231: - mlcape50 - Mixed-layer CAPE in the lowest 50 hPa 228232: - mlcin50 - Mixed-layer CIN in the lowest 50 hPa 228233: - mlcape100 - Mixed-layer CAPE in the lowest 100 hPa 228234: - mlcin100 - Mixed-layer CIN in the lowest 100 hPa 228235: - mucape - Most-unstable CAPE 228236: - mucin - Most-unstable CIN 228237: - mudlp - Departure level of the most unstable parcel expressed as Pressure 228239: - 200u - 200 metre U wind component 228240: - 200v - 200 metre V wind component 228241: - 200si - 200 metre wind speed 228242: - fdif - Surface solar radiation diffuse total sky 228243: - cdif - Surface solar radiation diffuse clear-sky 228244: - aldr - Surface albedo of direct radiation 228245: - aldf - Surface albedo of diffuse radiation 228246: - 100u - 100 metre U wind component 228247: - 100v - 100 metre V wind component 228248: - tccsw - Surface short wave-effective total cloudiness 228249: - 100si - 100 metre wind speed 228250: - irrfr - Irrigation fraction 228251: - pev - Potential evaporation 228252: - irr - Irrigation 228253: - ascat_sm_cdfa - ASCAT first soil moisture CDF matching parameter 228254: - ascat_sm_cdfb - ASCAT second soil moisture CDF matching parameter 228255: - tcclw - Surface long wave-effective total cloudiness 229001: - cur - Urban cover 229002: - cro - Road Cover 229003: - cbu - Building cover 229004: - bldh - Building height 229005: - hwr - Vertical-to-horizontal area ratio 229006: - bhstd - Standard deviation of building height 229007: - cwe - Wetland cover 229008: - twe - Wetland type 229009: - cirr - Irrigation cover 229010: - c4cr - C4 crop cover 229011: - c4gr - C4 grass cover 230008: - srovar - Surface runoff (variable resolution) 230009: - ssrovar - Sub-surface runoff (variable resolution) 230020: - parcsvar - Clear sky surface photosynthetically active radiation (variable resolution) 230021: - fdirvar - Total sky direct solar radiation at surface (variable resolution) 230022: - cdirvar - Clear-sky direct solar radiation at surface (variable resolution) 230044: - esvar - Snow evaporation (variable resolution) 230045: - smltvar - Snowmelt (variable resolution) 230046: - sdurvar - Solar duration (variable resolution) 230047: - dsrpvar - Direct solar radiation (variable resolution) 230050: - lspfvar - Large-scale precipitation fraction (variable resolution) 230057: - uvbvar - Downward UV radiation at the surface (variable resolution) 230058: - parvar - Photosynthetically active radiation at the surface (variable resolution) 230080: - aco2neevar - Accumulated Carbon Dioxide Net Ecosystem Exchange (variable resolution) 230081: - aco2gppvar - Accumulated Carbon Dioxide Gross Primary Production (variable resolution) 230082: - aco2recvar - Accumulated Carbon Dioxide Ecosystem Respiration (variable resolution) 230129: - ssrdcvar - Surface solar radiation downward clear-sky (variable resolution) 230130: - strdcvar - Surface thermal radiation downward clear-sky (variable resolution) 230142: - lspvar - Stratiform precipitation (Large-scale precipitation) (variable resolution) 230143: - cpvar - Convective precipitation (variable resolution) 230144: - sfvar - Snowfall (convective + stratiform) (variable resolution) 230145: - bldvar - Boundary layer dissipation (variable resolution) 230146: - sshfvar - Surface sensible heat flux (variable resolution) 230147: - slhfvar - Surface latent heat flux (variable resolution) 230169: - ssrdvar - Surface solar radiation downwards (variable resolution) 230174: - alvar - Albedo (variable resolution) 230175: - strdvar - Surface thermal radiation downwards (variable resolution) 230176: - ssrvar - Surface net solar radiation (variable resolution) 230177: - strvar - Surface net thermal radiation (variable resolution) 230178: - tsrvar - Top net solar radiation (variable resolution) 230179: - ttrvar - Top net thermal radiation (variable resolution) 230180: - ewssvar - East-West surface stress (variable resolution) 230181: - nsssvar - North-South surface stress (variable resolution) 230182: - evar - Evaporation (variable resolution) 230189: - sundvar - Sunshine duration (variable resolution) 230195: - lgwsvar - Longitudinal component of gravity wave stress (variable resolution) 230196: - mgwsvar - Meridional component of gravity wave stress (variable resolution) 230197: - gwdvar - Gravity wave dissipation (variable resolution) 230198: - srcvar - Skin reservoir content (variable resolution) 230205: - rovar - Runoff (variable resolution) 230208: - tsrcvar - Top net solar radiation, clear sky (variable resolution) 230209: - ttrcvar - Top net thermal radiation, clear sky (variable resolution) 230210: - ssrcvar - Surface net solar radiation, clear sky (variable resolution) 230211: - strcvar - Surface net thermal radiation, clear sky (variable resolution) 230212: - tisrvar - TOA incident solar radiation (variable resolution) 230213: - vimdvar - Vertically integrated moisture divergence (variable resolution) 230216: - fzravar - Accumulated freezing rain (variable resolution) 230228: - tpvar - Total precipitation (variable resolution) 230239: - csfvar - Convective snowfall (variable resolution) 230240: - lsfvar - Large-scale snowfall (variable resolution) 230251: - pevvar - Potential evaporation (variable resolution) 231001: - fzrawe - Accumulated freezing rain water equivalent 231002: - rowe - Runoff water equivalent (surface plus subsurface) 231003: - eswe - Snow evaporation water equivalent 231004: - pevr - Potential evaporation rate 231005: - peva - Potential evaporation 231006: - tifr - Tile fraction 231007: - tipe - Tile percentage 231008: - flsrm - Forecast logarithm of surface roughness length for moisture 231009: - surfror - Surface runoff rate 231010: - surfro - Surface runoff 231011: - ssurfror - Sub-surface runoff rate 231012: - ssurfro - Sub-surface runoff 231013: - rfl04 - Reflectance in 0.4 micron channel 231014: - vdiv - Vertical divergence 231015: - dtc - Drag thermal coefficient 231016: - dec - Drag evaporation coefficient 231017: - pdhs - Pressure departure from hydrostatic state 231018: - snrf - Surface net radiation flux (SW and LW) 231019: - tnrf - Top net radiation flux (SW and LW) 231020: - mtnrf - Time-mean top net radiation flux (SW and LW) 231021: - gits - Global irradiance on tilted surfaces 231022: - eagr - Eady growth rate 231023: - tdtc - Tropical cyclones track density 231024: - cant - Canopy air temperature 231025: - swit - Soil wetness index (total layer) 231026: - swir - Soil wetness index (root zone) 231027: - swil - Soil wetness index (layer) 231028: - ddrf - Distance downward from roof surface 231029: - diws - Distance inward from outer wall surface 231030: - ddrd - Distance downward from road surface 231031: - rpc - Renewable power capacity 231032: - rppr - Renewable power production rate 231033: - rpp - Renewable power production 231034: - wpc - Wind power capacity 231035: - wppr - Wind power production rate 231036: - wpp - Wind power production 231037: - pvpc - Solar photovoltaic (PV) power capacity 231038: - pvppr - Solar photovoltaic (PV) power production rate 231039: - pvpp - Solar photovoltaic (PV) power production 231040: - tgrp - Graupel (snow pellets) precipitation 231041: - litotint - Time-integrated total lightning flash density 231042: - maxtcg - Maximum total column integrated graupel (snow pellets) 231043: - minvis - Minimum visibility 231044: - hthg - Geometric height of theta level above ground 231045: - pcdb - Pressure at cloud base 231046: - hacg - Geometric height of adiabatic condensation level above ground 231047: - hfcg - Geometric height of free convection level above ground 231048: - hnbg - Geometric height of neutral buoyancy level above ground 231049: - deg3l - Geometric height of 3 degrees C atmospheric isothermal level above ground 231050: - rft - Roof temperature 231051: - wlt - Wall temperature 231052: - rdt - Road temperature 231053: - sdrf - Snow depth water equivalent on roof 231054: - sdrd - Snow depth water equivalent on road 231055: - urct - Urban canyon temperature 231056: - urcq - Urban canyon specific humidity 231057: - csfwe - Convective snowfall water equivalent 231058: - lsfwe - Large-scale snowfall water equivalent 231059: - lslt - Lake surface temperature 231060: - sbrn - Surface bulk Richardson number 231061: - mx2r - Time-maximum 2 metre relative humidity 231062: - mn2r - Time-minimum 2 metre relative humidity 231063: - srhe - Surface roughness for heat 231064: - srmo - Surface roughness for moisture 231065: - esrwe - Snow evaporation rate water equivalent 231066: - rare - Radar reflectivity 231067: - pcdc - Pressure at cloud ceiling 231068: - visp - Visibility through precipitation 231069: - h0thg - Geometric height of 0 degrees C theta level above ground 231070: - h1thg - Geometric height of 1 degree C theta level above ground 231071: - h1p5thg - Geometric height of 1.5 degrees C theta level above ground 231072: - vswrz - Volumetric soil moisture (root zone) 231073: - tcvmrpr - Total column vertical-maximum radar precipitation rate 231074: - tcvmrr - Total column vertical-maximum radar reflectivity 231500: - hits - Thunderstorm hazard index 231501: - hiswf - Surface water flooding hazard index 231502: - hipa - Polluted air hazard index 231503: - hiss - Storm surge hazard index 231504: - hicw - Cold wave hazard index 231505: - hihf - Frost (hoar frost) hazard index 231506: - hihw - Heatwave hazard index 231507: - hiw - Wind hazard index 231508: - hiwf - Wildfires hazard index 232000: - fba - Burned area 232001: - bia - Burning area 232002: - baa - Burnable area 232003: - ubaa - Un-burnable area 232004: - fuell - Fuel load 232005: - combc - Combustion completeness 232006: - fuelmc - Fuel moisture content 232007: - llfl - Live leaf fuel load 232008: - lwfl - Live wood fuel load 232009: - dlfl - Dead leaf fuel load 232010: - dwfl - Dead wood fuel load 232011: - lfmc - Live fuel moisture content 232012: - fdlmc - Fine dead leaf moisture content 232013: - ddlmc - Dense dead leaf moisture content 232014: - fdwmc - Fine dead wood moisture content 232015: - ddwmc - Dense dead wood moisture content 232016: - lfmclv - Live fuel moisture content in low vegetation 232017: - lfmchv - Live fuel moisture content in high vegetation 233000: - tvige - Time-integrated total column vertically-integrated eastward geopotential flux 233001: - tvign - Time-integrated total column vertically-integrated northward geopotential flux 233002: - tviwgd - Time-integrated total column vertically-integrated divergence of water geopotential flux 233003: - tvigd - Time-integrated total column vertically-integrated divergence of geopotential flux 233004: - tviee - Time-integrated total column vertically-integrated eastward enthalpy flux 233005: - tvien - Time-integrated total column vertically-integrated northward enthalpy flux 233006: - tvikee - Time-integrated total column vertically-integrated eastward kinetic energy flux 233007: - tviken - Time-integrated total column vertically-integrated northward kinetic energy flux 233008: - tvitee - Time-integrated total column vertically-integrated eastward total energy flux 233009: - tviten - Time-integrated total column vertically-integrated northward total energy flux 233010: - tvied - Time-integrated total column vertically-integrated divergence of enthalpy flux 233011: - tviked - Time-integrated total column vertically-integrated divergence of kinetic energy flux 233012: - tvited - Time-integrated total column vertically-integrated divergence of total energy flux 233013: - tviwed - Time-integrated total column vertically-integrated divergence of water enthalpy flux 233014: - tvimad - Time-integrated total column vertically-integrated divergence of mass flux 233015: - tvimae - Time-integrated total column vertically-integrated eastward mass flux 233016: - tviman - Time-integrated total column vertically-integrated northward mass flux 233017: - tviwvd - Time-integrated total column vertically-integrated divergence of water vapour flux 233018: - tviclwd - Time-integrated total column vertically-integrated divergence of cloud liquid water flux 233019: - tviciwd - Time-integrated total column vertically-integrated divergence of cloud ice water flux 233020: - tvird - Time-integrated total column vertically-integrated divergence of rain flux 233021: - tvisd - Time-integrated total column vertically-integrated divergence of snow flux 233022: - tviwve - Time-integrated total column vertically-integrated eastward water vapour flux 233023: - tviwvn - Time-integrated total column vertically-integrated northward water vapour flux 233024: - tviclwe - Time-integrated total column vertically-integrated eastward cloud liquid water flux 233025: - tviclwn - Time-integrated total column vertically-integrated northward cloud liquid water flux 233026: - tviciwe - Time-integrated total column vertically-integrated eastward cloud ice water flux 233027: - tviciwn - Time-integrated total column vertically-integrated northward cloud ice water flux 233028: - tvire - Time-integrated total column vertically-integrated eastward rain flux 233029: - tvirn - Time-integrated total column vertically-integrated northward rain flux 233030: - tvise - Time-integrated total column vertically-integrated eastward snow flux 233031: - tvisn - Time-integrated total column vertically-integrated northward snow flux 233032: - tvioze - Time-integrated total column vertically-integrated eastward ozone flux 233033: - tviozn - Time-integrated total column vertically-integrated northward ozone flux 233034: - tviozd - Time-integrated total column vertically-integrated divergence of ozone flux 233035: - tvions - Time-integrated total column vertically-integrated net source of ozone 234139: - sts - Surface temperature significance 234151: - msls - Mean sea level pressure significance 234167: - 2ts - 2 metre temperature significance 234228: - tps - Total precipitation significance 235001: - avg_ttswr - Time-mean temperature tendency due to short-wave radiation 235002: - avg_ttlwr - Time-mean temperature tendency due to long-wave radiation 235003: - avg_ttswrcs - Time-mean temperature tendency due to short wave radiation, clear sky 235004: - avg_ttlwrcs - Time-mean temperature tendency due to long-wave radiation, clear sky 235005: - avg_ttpm - Time-mean temperature tendency due to parametrisations 235006: - avg_qtpm - Time-mean specific humidity tendency due to parametrisations 235007: - avg_utpm - Time-mean eastward wind tendency due to parametrisations 235008: - avg_vtpm - Time-mean northward wind tendency due to parametrisations 235009: - avg_umf - Time-mean updraught mass flux 235010: - avg_dmf - Time-mean downdraught mass flux 235011: - avg_udr - Time-mean updraught detrainment rate 235012: - avg_ddr - Time-mean downdraught detrainment rate 235013: - avg_tpf - Time-mean total precipitation flux 235014: - avg_tdch - Time-mean turbulent diffusion coefficient for heat 235015: - tirf - Time integral of rain flux 235017: - tisemf - Time integral of surface eastward momentum flux 235018: - tisnmf - Time integral of surface northward momentum flux 235019: - tislhef - Time integral of surface latent heat evaporation flux 235020: - avg_surfror - Time-mean surface runoff rate 235021: - avg_ssurfror - Time-mean sub-surface runoff rate 235022: - avg_parcsf - Time-mean surface photosynthetically active radiation flux, clear sky 235023: - avg_esrwe - Time-mean snow evaporation rate water equivalent 235024: - avg_smr - Time-mean snow melt rate 235025: - avg_imagss - Time-mean magnitude of turbulent surface stress 235026: - avg_ilspf - Time-mean large-scale precipitation fraction 235027: - avg_sduvrf - Time-mean surface downward UV radiation flux 235028: - avg_sparf - Time-mean surface photosynthetically active radiation flux 235029: - avg_lsprate - Time-mean large-scale precipitation rate 235030: - avg_cpr - Time-mean convective precipitation rate 235031: - avg_tsrwe - Time-mean total snowfall rate water equivalent 235032: - avg_ibld - Time-mean boundary layer dissipation 235033: - avg_ishf - Time-mean surface sensible heat flux 235034: - avg_slhtf - Time-mean surface latent heat flux 235035: - avg_sdswrf - Time-mean surface downward short-wave radiation flux 235036: - avg_sdlwrf - Time-mean surface downward long-wave radiation flux 235037: - avg_snswrf - Time-mean surface net short-wave radiation flux 235038: - avg_snlwrf - Time-mean surface net long-wave radiation flux 235039: - avg_tnswrf - Time-mean top net short-wave radiation flux 235040: - avg_tnlwrf - Time-mean top net long-wave radiation flux 235041: - avg_iews - Time-mean eastward turbulent surface stress 235042: - avg_inss - Time-mean northward turbulent surface stress 235043: - avg_ie - Time-mean moisture flux 235044: - avg_sdf - Time-mean sunshine duration fraction 235045: - avg_iegwss - Time-mean eastward gravity wave surface stress 235046: - avg_ingwss - Time-mean northward gravity wave surface stress 235047: - avg_igwd - Time-mean gravity wave dissipation 235048: - avg_rorwe - Time-mean runoff rate water equivalent (surface plus subsurface) 235049: - avg_tnswrfcs - Time-mean top net short-wave radiation flux, clear sky 235050: - avg_tnlwrfcs - Time-mean top net long-wave radiation flux, clear sky 235051: - avg_snswrfcs - Time-mean surface net short-wave radiation flux, clear sky 235052: - avg_snlwrfcs - Time-mean surface net long-wave radiation flux, clear sky 235053: - avg_tdswrf - Time mean top downward short-wave radiation flux 235054: - avg_vimdf - Time-mean total column vertically-integrated moisture divergence flux 235055: - avg_tprate - Time-mean total precipitation rate 235056: - avg_csfr - Time-mean convective snowfall rate water equivalent 235057: - avg_lssfr - Time-mean large scale snowfall rate water equivalent 235058: - avg_sdirswrf - Time-mean surface direct short-wave radiation flux 235059: - avg_sdirswrfcs - Time-mean surface direct short-wave radiation flux, clear sky 235060: - msdfswrf - Mean surface diffuse short-wave radiation flux 235061: - avg_sdifswrfcs - Time-mean surface diffuse short-wave radiation flux, clear sky 235062: - avg_fco2nee - Time-mean carbon dioxide net ecosystem exchange flux 235063: - avg_fco2gpp - Time-mean carbon dioxide gross primary production flux 235064: - avg_fco2rec - Time-mean carbon dioxide ecosystem respiration flux 235065: - mrr - Mean rain rate 235066: - mcrr - Mean convective rain rate 235067: - mlsrr - Mean large-scale rain rate 235068: - avg_sdswrfcs - Time-mean surface downward short-wave radiation flux, clear sky 235069: - avg_sdlwrfcs - Time-mean surface downward long-wave radiation flux, clear sky 235070: - avg_pevr - Time-mean potential evaporation rate 235071: - tislhsf - Time integral of surface latent heat sublimation flux 235072: - tisef - Time integral of snow evaporation flux 235073: - tietrf - Time integral of evapotranspiration flux 235074: - avg_etr - Time-mean evapotranspiration rate 235075: - tipet - Time integral of potential evapotranspiration rate 235076: - avg_petr - Time-mean potential evapotranspiration rate 235077: - avg_vsw - Time-mean volumetric soil moisture 235078: - avg_sd - Time-mean snow depth water equivalent 235079: - avg_skt - Time-mean skin temperature 235080: - avg_rsn - Time-mean snow density 235081: - avg_cvl - Time-mean low vegetation cover 235082: - avg_cvh - Time-mean high vegetation cover 235083: - avg_ci - Time-mean sea ice area fraction 235084: - avg_sst - Time-mean sea surface temperature 235085: - avg_lai_lv - Time-mean leaf area index, low vegetation 235086: - avg_lai_hv - Time-mean leaf area index, high vegetation 235087: - avg_tclw - Time-mean total column liquid water 235088: - avg_tciw - Time-mean total column cloud ice water 235089: - avg_2sh - Time-mean 2 metre specific humidity 235090: - avg_lmlt - Time-mean lake mix-layer temperature 235091: - avg_lmld - Time-mean lake mix-layer depth 235092: - avg_2r - Time-mean 2 metre relative humidity 235093: - avg_fscov - Time-mean fraction of snow cover 235094: - avg_sot - Time-mean soil temperature 235095: - avg_sde - Time-mean snow depth 235096: - avg_snowc - Time-mean snow cover 235097: - avg_ws - Time-mean wind speed 235098: - avg_pres - Time-mean pressure 235099: - avg_chnk - Time-mean charnock 235100: - avg_pv - Time-mean potential vorticity 235101: - avg_crwc - Time-mean specific rain water content 235102: - avg_cswc - Time-mean specific snow water content 235103: - avg_etadot - Time-mean eta-coordinate vertical velocity 235105: - avg_vis - Time-mean visibility 235106: - avg_wdir - Time-mean wind direction 235107: - avg_tcsw - Time-mean total column snow water 235108: - avg_lcc - Time-mean low cloud cover 235109: - avg_mcc - Time-mean medium cloud cover 235110: - avg_hcc - Time-mean high cloud cover 235111: - avg_tcrw - Time-mean total column rain water 235113: - avg_sm20 - Time-mean soil moisture top 20 cm 235114: - avg_sm100 - Time-mean soil moisture top 100 cm 235115: - avg_st20 - Time-mean soil temperature top 20 cm 235116: - avg_st100 - Time-mean soil temperature top 100 cm 235117: - avg_mucape - Time-mean most-unstable CAPE 235118: - avg_vsw20 - Time-mean volumetric soil moisture top 20 cm 235119: - avg_vsw100 - Time-mean volumetric soil moisture top 100 cm 235120: - avg_tcolg - Time-mean total column vertically-integrated graupel (snow pellets) 235129: - avg_z - Time-mean geopotential 235130: - avg_t - Time-mean temperature 235131: - avg_u - Time-mean U component of wind 235132: - avg_v - Time-mean V component of wind 235133: - avg_q - Time-mean specific humidity 235134: - avg_sp - Time-mean surface pressure 235135: - avg_w - Time-mean vertical velocity 235136: - avg_tcw - Time-mean total column water 235137: - avg_tcwv - Time-mean total column vertically-integrated water vapour 235138: - avg_vo - Time-mean vorticity (relative) 235141: - avg_sd_m - Time-mean snow depth 235149: - avg_snrf - Time-mean surface net radiation flux (SW and LW) 235150: - avg_tnrf - Time-mean top net radiation flux (SW and LW) 235151: - avg_msl - Time-mean mean sea level pressure 235152: - avg_lnsp - Time-mean logarithm of surface pressure 235155: - avg_d - Time-mean divergence 235157: - avg_r - Time-mean relative humidity 235159: - avg_blh - Time-mean boundary layer height 235165: - avg_10u - Time-mean 10 metre U wind component 235166: - avg_10v - Time-mean 10 metre V wind component 235168: - avg_2d - Time-mean 2 metre dewpoint temperature 235186: - avg_lcc_frac - Time-mean low cloud cover 235187: - avg_mcc_frac - Time-mean medium cloud cover 235188: - avg_hcc_frac - Time-mean high cloud cover 235189: - avg_suns - Time-mean sunshine 235203: - avg_o3 - Time-mean ozone mass mixing ratio 235238: - avg_tsn - Time-mean temperature of snow layer 235243: - avg_fal_frac - Time-mean forecast albedo 235244: - avg_fsr - Time-mean forecast surface roughness 235245: - avg_flsr - Time-mean forecast logarithm of surface roughness for heat 235246: - avg_clwc - Time-mean specific cloud liquid water content 235247: - avg_ciwc - Time-mean specific cloud ice water content 235248: - avg_cc - Time-mean fraction of cloud cover 235257: - avg_kx - Time-mean K index 235258: - avg_totalx - Time-mean total totals index 235261: - avg_10wdir - Time-mean 10 metre wind direction 235262: - avg_cat - Time-mean clear air turbulence (CAT) 235263: - avg_al - Time-mean forecast albedo 235269: - avg_pt - Time-mean potential temperature 235270: - avg_dis - Time-mean discharge from rivers or streams 235271: - avg_swit - Time-mean soil wetness index (total layer) 235272: - avg_swir - Time-mean soil wetness index (root zone) 235273: - avg_swil - Time-mean soil wetness index(layer) 235274: - avg_flddep - Time-mean floodplain depth 235275: - avg_fldffr - Time-mean floodplain flooded fraction 235276: - avg_fldfar - Time-mean floodplain flooded area 235277: - avg_rivfr - Time-mean river fraction 235278: - avg_rivar - Time-mean river area 235279: - avg_rivcffr - Time-mean fraction of river coverage plus river related flooding 235280: - avg_rivcfar - Time-mean area of river coverage plus river related flooding 235281: - avg_aluvp_p - Time-mean UV visible albedo for direct radiation (climatological) 235282: - avg_aluvd_p - Time-mean UV visible albedo for diffuse radiation (climatological) 235283: - avg_alnip_p - Time-mean near IR albedo for direct radiation (climatological) 235284: - avg_alnid_p - Time-mean near IR albedo for diffuse radiation (climatological) 235285: - avg_asn - Time-mean snow albedo 235286: - avg_mont - Time-mean montgomery potential 235287: - avg_cape - Time-mean convective available potential energy 235288: - avg_tcc - Time-mean total cloud cover 235289: - avg_srcon - Time-mean skin reservoir content 235290: - avg_tcioz - Time-mean total column integrated ozone 235291: - avg_vike - Time-mean total column vertically-integrated kinetic energy 235292: - avg_vithe - Time-mean total column vertically-integrated enthalpy 235293: - avg_vipie - Time-mean total column vertically-integrated potential + internal energy 235294: - avg_vipile - Time-mean total column vertically-integrated potential+internal+latent energy 235295: - avg_vitoe - Time-mean total column vertically-integrated total energy 235296: - avg_viwe - Time-mean total column vertically-integrated water enthalpy 235297: - avg_aluvpi_p - Time-mean UV visible albedo for direct radiation, isotropic component (climatological) 235298: - avg_aluvpv_p - Time-mean UV visible albedo for direct radiation, volumetric component (climatological) 235299: - avg_aluvpg_p - Time-mean UV visible albedo for direct radiation, geometric component (climatological) 235300: - avg_alnipi_p - Time-mean near IR albedo for direct radiation, isotropic component (climatological) 235301: - avg_alnipv_p - Time-mean near IR albedo for direct radiation, volumetric component (climatological) 235302: - avg_alnipg_p - Time-mean near IR albedo for direct radiation, geometric component (climatological) 235303: - avg_cin - Time-mean convective inhibition 235304: - avg_zust - Time-mean friction velocity 235305: - avg_lblt - Time-mean lake bottom temperature 235306: - avg_ltlt - Time-mean lake total layer temperature 235307: - avg_lshf - Time-mean lake shape factor 235308: - avg_lict - Time-mean lake ice surface temperature 235309: - avg_licd - Time-mean lake ice total depth 235310: - avg_dndzn - Time-mean minimum vertical gradient of refractivity inside trapping layer 235311: - avg_dndza - Time-mean mean vertical gradient of refractivity inside trapping layer 235312: - avg_dctb - Time-mean duct base height 235313: - avg_tplb - Time-mean trapping layer base height 235314: - avg_tplt - Time-mean trapping layer top height 235315: - avg_degm10l - Time-mean geometric height of -10 degrees C atmospheric isothermal level above ground 235316: - avg_cbh - Time-mean cloud base height 235317: - avg_deg0l - Time-mean geometric height of 0 degrees C atmospheric isothermal level above ground 235318: - avg_i10fg - Time-mean 10 metre wind gust 235319: - avg_rhw - Time-mean relative humidity with respect to water 235320: - avg_2rhw - Time-mean 2 metre relative humidity with respect to water 235321: - avg_capes - Time-mean convective available potential energy shear 235322: - avg_trpp - Time-mean tropopause pressure 235323: - avg_hcct - Time-mean height of convective cloud top 235324: - avg_hwbt0 - Time-mean height of zero-degree wet-bulb temperature 235325: - avg_hwbt1 - Time-mean height of one-degree wet-bulb temperature 235326: - avg_litoti - Time-mean total lightning flash density 235327: - avg_tcslw - Time-mean total column supercooled liquid water 235328: - avg_u10n - Time-mean 10 metre U-component of neutral wind 235329: - avg_v10n - Time-mean 10 metre V-component of neutral wind 235330: - avg_crr - Time-mean convective rain rate 235331: - avg_lsrr - Time-mean large scale rain rate 235332: - avg_mlcape50 - Time-mean mixed-layer CAPE in the lowest 50 hPa 235333: - avg_mlcin50 - Time-mean mixed-layer CIN in the lowest 50 hPa 235334: - avg_mlcape100 - Time-mean mixed-layer CAPE in the lowest 100 hPa 235335: - avg_mlcin100 - Time-mean mixed-layer CIN in the lowest 100 hPa 235336: - avg_mudlp - Time-mean departure level of the most unstable parcel expressed as Pressure 235337: - avg_ceil - Time-mean ceiling 235338: - avg_tcsqw - Time-mean total column integrated saturation specific humidity with respect to water 235339: - avg_sdirnswrf - Time-mean surface direct normal short-wave radiation flux 235340: - avg_cossza - Time-mean cosine of solar zenith angle 235341: - avg_fprate - Time-mean freezing rain precipitation rate 235342: - avg_vige - Time-mean total column vertically-integrated eastward geopotential flux 235343: - avg_vign - Time-mean total column vertically-integrated northward geopotential flux 235344: - avg_viwgd - Time-mean total column vertically-integrated divergence of water geopotential flux 235345: - avg_vigd - Time-mean total column vertically-integrated divergence of geopotential flux 235346: - avg_viee - Time-mean total column vertically-integrated eastward enthalpy flux 235347: - avg_vien - Time-mean total column vertically-integrated northward enthalpy flux 235348: - avg_vikee - Time-mean total column vertically-integrated eastward kinetic energy flux 235349: - avg_viken - Time-mean total column vertically-integrated northward kinetic energy flux 235350: - avg_vitee - Time-mean total column vertically-integrated eastward total energy flux 235351: - avg_viten - Time-mean total column vertically-integrated northward total energy flux 235352: - avg_vied - Time-mean total column vertically-integrated divergence of enthalpy flux 235353: - avg_viked - Time-mean total column vertically-integrated divergence of kinetic energy flux 235354: - avg_vited - Time-mean total column vertically-integrated divergence of total energy flux 235355: - avg_viwed - Time-mean total column vertically-integrated divergence of water enthalpy flux 235356: - avg_vimad - Time-mean total column vertically-integrated divergence of mass flux 235357: - avg_vimae - Time-mean total column vertically-integrated eastward mass flux 235358: - avg_viman - Time-mean total column vertically-integrated northward mass flux 235359: - avg_viwvd - Time-mean total column vertically-integrated divergence of water vapour flux 235360: - avg_viclwd - Time-mean total column vertically-integrated divergence of cloud liquid water flux 235361: - avg_viciwd - Time-mean total column vertically-integrated divergence of cloud ice water flux 235362: - avg_vird - Time-mean total column vertically-integrated divergence of rain flux 235363: - avg_visd - Time-mean total column vertically-integrated divergence of snow flux 235364: - avg_viwve - Time-mean total column vertically-integrated eastward water vapour flux 235365: - avg_viwvn - Time-mean total column vertically-integrated northward water vapour flux 235366: - avg_viclwe - Time-mean total column vertically-integrated eastward cloud liquid water flux 235367: - avg_viclwn - Time-mean total column vertically-integrated northward cloud liquid water flux 235368: - avg_viciwe - Time-mean total column vertically-integrated eastward cloud ice water flux 235369: - avg_viciwn - Time-mean total column vertically-integrated northward cloud ice water flux 235370: - avg_vire - Time-mean total column vertically-integrated eastward rain flux 235371: - avg_virn - Time-mean total column vertically-integrated northward rain flux 235372: - avg_vise - Time-mean total column vertically-integrated eastward snow flux 235373: - avg_visn - Time-mean total column vertically-integrated northward snow flux 235374: - avg_vioze - Time-mean total column vertically-integrated eastward ozone flux 235375: - avg_viozn - Time-mean total column vertically-integrated northward ozone flux 235376: - avg_viozd - Time-mean total column vertically-integrated divergence of ozone flux 235377: - avg_vions - Time-mean total column vertically-integrated net source of ozone 235378: - avg_ietssofd - Time-mean eastward turbulent surface stress due to orographic form drag 235379: - avg_intssofd - Time-mean northward turbulent surface stress due to orographic form drag 235380: - avg_ietsssr - Time-mean eastward turbulent surface stress due to surface roughness 235381: - avg_intsssr - Time-mean northward turbulent surface stress due to surface roughness 235382: - avg_visp - Time-mean visibility through precipitation 235383: - avg_licgi - Time-mean cloud-to-ground lightning flash density 235386: - avg_lshnf - Time-mean land surface heat net flux 235387: - avg_sdirnswrfcs - Time-mean surface direct normal short-wave radiation flux, clear sky 235388: - avg_10cogu - Time-mean 10 metre convective gust 235389: - avg_10tugu - Time-mean 10 metre turbulent gust 235390: - avg_h0thg - Time-mean geometric height of 0 degrees C theta level above ground 235391: - avg_h1thg - Time-mean geometric height of 1 degree C theta level above ground 235392: - avg_h1p5thg - Time-mean geometric height of 1.5 degrees C theta level above ground 235393: - avg_veg - Time-mean vegetation 235394: - avg_lai - Time-mean leaf area index 235395: - avg_srhe - Time-mean surface roughness for heat 235396: - avg_rol - Time-mean Reciprocal Obukhov length 235397: - avg_deg3l - Time-mean geometric height of 3 degrees C atmospheric isothermal level above ground 235398: - avg_rivsto - Time-mean river storage of water 235399: - avg_fldsto - Time-mean floodplain storage of water 235400: - avg_rivdph - Time-mean river depth 235401: - avg_rivout - Time-mean river outflow of water 235402: - avg_fldout - Time-mean floodplain outflow of water 235403: - avg_pthflw - Time-mean floodpath outflow of water 235404: - avg_sdifswrfcs - Time-mean surface diffuse short-wave radiation flux, clear sky 235405: - avg_vswrz - Time-mean volumetric soil moisture (root zone) 235406: - avg_lwcs - Time-mean liquid water content in snow pack 235407: - avg_mucin - Time-mean most-unstable CIN 235408: - avg_crwc_conv - Time-mean specific rain water content (convective) 235409: - avg_cswc_conv - Time-mean specific snow water content (convective) 235410: - avg_mld - Time-mean mixed layer depth 235411: - avg_cur - Time-mean urban cover 235412: - avg_wse - Time-mean water surface elevation 235413: - avg_grfr - Time-mean groundwater return flow rate 235414: - avg_rivfldsto - Time-mean river and floodplain storage 235415: - avg_darv - Time-mean depth-averaged river velocity 236386: - acc_lshnf - Time-integrated land surface heat net flux 236387: - acc_sdirnswrfcs - Time-integrated surface direct normal short-wave radiation flux, clear sky 237001: - max_ttswr - Time-maximum temperature tendency due to short-wave radiation 237002: - max_ttlwr - Time-maximum temperature tendency due to long-wave radiation 237003: - max_ttswrcs - Time-maximum temperature tendency due to short wave radiation, clear sky 237004: - max_ttlwrcs - Time-maximum temperature tendency due to long-wave radiation, clear sky 237005: - max_ttpm - Time-maximum temperature tendency due to parametrisations 237006: - max_qtpm - Time-maximum specific humidity tendency due to parametrisations 237007: - max_utpm - Time-maximum eastward wind tendency due to parametrisations 237008: - max_vtpm - Time-maximum northward wind tendency due to parametrisations 237009: - max_umf - Time-maximum updraught mass flux 237010: - max_dmf - Time-maximum downdraught mass flux 237011: - max_udr - Time-maximum updraught detrainment rate 237012: - max_ddr - Time-maximum downdraught detrainment rate 237013: - max_tpf - Time-maximum total precipitation flux 237014: - max_tdch - Time-maximum turbulent diffusion coefficient for heat 237022: - max_parcsf - Time-maximum surface photosynthetically active radiation flux, clear sky 237023: - max_esrwe - Time-maximum snow evaporation rate water equivalent 237024: - max_smr - Time-maximum snow melt rate 237025: - max_imagss - Time-maximum magnitude of turbulent surface stress 237026: - max_ilspf - Time-maximum large-scale precipitation fraction 237027: - max_sduvrf - Time-maximum surface downward UV radiation flux 237028: - max_sparf - Time-maximum surface photosynthetically active radiation flux 237029: - max_lsprate - Time-maximum large-scale precipitation rate 237030: - max_cpr - Time-maximum convective precipitation rate 237031: - max_tsrwe - Time-maximum total snowfall rate water equivalent 237032: - max_ibld - Time-maximum boundary layer dissipation 237033: - max_ishf - Time-maximum surface sensible heat flux 237034: - max_slhtf - Time-maximum surface latent heat flux 237035: - max_sdswrf - Time-maximum surface downward short-wave radiation flux 237036: - max_sdlwrf - Time-maximum surface downward long-wave radiation flux 237037: - max_snswrf - Time-maximum surface net short-wave radiation flux 237038: - max_snlwrf - Time-maximum surface net long-wave radiation flux 237039: - max_tnswrf - Time-maximum top net short-wave radiation flux 237040: - max_tnlwrf - Time-maximum top net long-wave radiation flux 237041: - max_iews - Time-maximum eastward turbulent surface stress 237042: - max_inss - Time-maximum northward turbulent surface stress 237043: - max_ie - Time-maximum moisture flux 237045: - max_iegwss - Time-maximum eastward gravity wave surface stress 237046: - max_ingwss - Time-maximum northward gravity wave surface stress 237047: - max_igwd - Time-maximum gravity wave dissipation 237048: - max_rorwe - Time-maximum runoff rate water equivalent (surface plus subsurface) 237049: - max_tnswrfcs - Time-maximum top net short-wave radiation flux, clear sky 237050: - max_tnlwrfcs - Time-maximum top net long-wave radiation flux, clear sky 237051: - max_snswrfcs - Time-maximum surface net short-wave radiation flux, clear sky 237052: - max_snlwrfcs - Time-maximum surface net long-wave radiation flux, clear sky 237053: - max_tdswrf - Time mean top downward short-wave radiation flux 237054: - max_vimdf - Time-maximum total column vertically-integrated moisture divergence flux 237055: - max_tprate - Time-maximum total precipitation rate 237056: - max_csfr - Time-maximum convective snowfall rate water equivalent 237057: - max_lssfr - Time-maximum large scale snowfall rate water equivalent 237058: - max_sdirswrf - Time-maximum surface direct short-wave radiation flux 237059: - max_sdirswrfcs - Time-maximum surface direct short-wave radiation flux, clear sky 237062: - max_fco2nee - Time-maximum carbon dioxide net ecosystem exchange flux 237063: - max_fco2gpp - Time-maximum carbon dioxide gross primary production flux 237064: - max_fco2rec - Time-maximum carbon dioxide ecosystem respiration flux 237068: - max_sdswrfcs - Time-maximum surface downward short-wave radiation flux, clear sky 237069: - max_sdlwrfcs - Time-maximum surface downward long-wave radiation flux, clear sky 237070: - max_pevr - Time-maximum potential evaporation rate 237077: - max_vsw - Time-maximum volumetric soil moisture 237078: - max_sd - Time-maximum snow depth water equivalent 237079: - max_skt - Time-maximum skin temperature 237080: - max_rsn - Time-maximum snow density 237083: - max_ci - Time-maximum sea ice area fraction 237084: - max_sst - Time-maximum sea surface temperature 237085: - max_lai_lv - Time-maximum leaf area index, low vegetation 237086: - max_lai_hv - Time-maximum leaf area index, high vegetation 237087: - max_tclw - Time-maximum total column cloud liquid water 237088: - max_tciw - Time-maximum total column cloud ice water 237089: - max_2sh - Time-maximum 2 metre specific humidity 237090: - max_lmlt - Time-maximum lake mix-layer temperature 237091: - max_lmld - Time-maximum lake mix-layer depth 237092: - max_2r - Time-maximum 2 metre relative humidity 237093: - max_fscov - Time-maximum fraction of snow cover 237094: - max_sot - Time-maximum soil temperature 237097: - max_ws - Time-maximum wind speed 237098: - max_pres - Time-maximum pressure 237099: - max_chnk - Time-maximum charnock 237100: - max_pv - Time-maximum potential vorticity 237101: - max_crwc - Time-maximum specific rain water content 237102: - max_cswc - Time-maximum specific snow water content 237103: - max_etadot - Time-maximum eta-coordinate vertical velocity 237105: - max_vis - Time-maximum visibility 237106: - max_wdir - Time-maximum wind direction 237107: - max_tcsw - Time-maximum total column snow water 237108: - max_lcc - Time-maximum low cloud cover 237109: - max_mcc - Time-maximum medium cloud cover 237110: - max_hcc - Time-maximum high cloud cover 237111: - max_tcrw - Time-maximum total column rain water 237117: - max_mucape - Time-maximum most-unstable CAPE 237120: - max_tcolg - Time-maximum total column vertically-integrated graupel (snow pellets) 237129: - max_z - Time-maximum geopotential 237130: - max_t - Time-maximum temperature 237131: - max_u - Time-maximum u component of wind 237132: - max_v - Time-maximum v component of wind 237133: - max_q - Time-maximum specific humidity 237134: - max_sp - Time-maximum surface pressure 237135: - max_w - Time-maximum vertical velocity 237136: - max_tcw - Time-maximum total column water 237137: - max_tcwv - Time-maximum total column vertically-integrated water vapour 237138: - max_vo - Time-maximum vorticity (relative) 237151: - max_msl - Time-maximum mean sea level pressure 237152: - max_lnsp - Time-maximum logarithm of surface pressure 237155: - max_d - Time-maximum divergence 237157: - max_r - Time-maximum relative humidity 237159: - max_blh - Time-maximum boundary layer height 237165: - max_10u - Time-maximum 10 metre U wind component 237166: - max_10v - Time-maximum 10 metre V wind component 237167: - max_2t - Time-maximum 2 metre temperature 237168: - max_2d - Time-maximum 2 metre dewpoint temperature 237189: - max_suns - Time-maximum sunshine 237203: - max_o3 - Time-maximum ozone mass mixing ratio 237207: - max_10si - Time-maximum 10 metre wind speed 237238: - max_tsn - Time-maximum temperature of snow layer 237244: - max_fsr - Time-maximum forecast surface roughness 237245: - max_flsr - Time-maximum forecast logarithm of surface roughness for heat 237246: - max_clwc - Time-maximum specific cloud liquid water content 237247: - max_ciwc - Time-maximum specific cloud ice water content 237248: - max_cc - Time-maximum fraction of cloud cover 237257: - max_kx - Time-maximum k index 237258: - max_totalx - Time-maximum total totals index 237261: - max_10wdir - Time-maximum 10 metre wind direction 237262: - max_cat - Time-maximum clear air turbulence (CAT) 237263: - max_al - Time-maximum forecast albedo 237269: - max_pt - Time-maximum potential temperature 237281: - max_aluvp_p - Time-maximum UV visible albedo for direct radiation (climatological) 237282: - max_aluvd_p - Time-maximum UV visible albedo for diffuse radiation (climatological) 237283: - max_alnip_p - Time-maximum near IR albedo for direct radiation (climatological) 237284: - max_alnid_p - Time-maximum near IR albedo for diffuse radiation (climatological) 237285: - max_asn - Time-maximum snow albedo 237286: - max_mont - Time-maximum montgomery potential 237287: - max_cape - Time-maximum convective available potential energy 237288: - max_tcc - Time-maximum total cloud cover 237289: - max_srcon - Time-maximum skin reservoir content 237290: - max_tcioz - Time-maximum total column integrated ozone 237291: - max_vike - Time-maximum total column vertically-integrated kinetic energy 237292: - max_vithe - Time-maximum total column vertically-integrated enthalpy 237293: - max_vipie - Time-maximum total column vertically-integrated potential + internal energy 237294: - max_vipile - Time-maximum total column vertically-integrated potential+internal+latent energy 237295: - max_vitoe - Time-maximum total column vertically-integrated total energy 237296: - max_viwe - Time-maximum total column vertically-integrated water enthalpy 237297: - max_aluvpi_p - Time-maximum UV visible albedo for direct radiation, isotropic component (climatological) 237298: - max_aluvpv_p - Time-maximum UV visible albedo for direct radiation, volumetric component (climatological) 237299: - max_aluvpg_p - Time-maximum UV visible albedo for direct radiation, geometric component (climatological) 237300: - max_alnipi_p - Time-maximum near IR albedo for direct radiation, isotropic component (climatological) 237301: - max_alnipv_p - Time-maximum near IR albedo for direct radiation, volumetric component (climatological) 237302: - max_alnipg_p - Time-maximum near IR albedo for direct radiation, geometric component (climatological) 237303: - max_cin - Time-maximum convective inhibition 237304: - max_zust - Time-maximum friction velocity 237305: - max_lblt - Time-maximum lake bottom temperature 237306: - max_ltlt - Time-maximum lake total layer temperature 237307: - max_lshf - Time-maximum lake shape factor 237308: - max_lict - Time-maximum lake ice surface temperature 237309: - max_licd - Time-maximum lake ice total depth 237310: - max_dndzn - Time-maximum minimum vertical gradient of refractivity inside trapping layer 237311: - max_dndza - Time-maximum mean vertical gradient of refractivity inside trapping layer 237312: - max_dctb - Time-maximum duct base height 237313: - max_tplb - Time-maximum trapping layer base height 237314: - max_tplt - Time-maximum trapping layer top height 237315: - max_degm10l - Time-maximum geometric height of -10 degrees C atmospheric isothermal level above ground 237316: - max_cbh - Time-maximum cloud base height 237317: - max_deg0l - Time-maximum geometric height of 0 degrees C atmospheric isothermal level above ground 237318: - max_i10fg - Time-maximum 10 metre wind gust 237319: - max_rhw - Time-maximum relative humidity with respect to water 237320: - max_2rhw - Time-maximum 2 metre relative humidity with respect to water 237321: - max_capes - Time-maximum convective available potential energy shear 237322: - max_trpp - Time-maximum tropopause pressure 237323: - max_hcct - Time-maximum height of convective cloud top 237324: - max_hwbt0 - Time-maximum height of zero-degree wet-bulb temperature 237325: - max_hwbt1 - Time-maximum height of one-degree wet-bulb temperature 237326: - max_litoti - Time-maximum total lightning flash density 237327: - max_tcslw - Time-maximum total column supercooled liquid water 237328: - max_u10n - Time-maximum 10 metre U-component of neutral wind 237329: - max_v10n - Time-maximum 10 metre V-component of neutral wind 237330: - max_crr - Time-maximum convective rain rate 237331: - max_lsrr - Time-maximum large scale rain rate 237332: - max_mlcape50 - Time-maximum mixed-layer CAPE in the lowest 50 hPa 237333: - max_mlcin50 - Time-maximum mixed-layer CIN in the lowest 50 hPa 237334: - max_mlcape100 - Time-maximum mixed-layer CAPE in the lowest 100 hPa 237335: - max_mlcin100 - Time-maximum mixed-layer CIN in the lowest 100 hPa 237336: - max_mudlp - Time-maximum departure level of the most unstable parcel expressed as Pressure 237337: - max_ceil - Time-maximum ceiling 237338: - max_tcsqw - Time-maximum total column integrated saturation specific humidity with respect to water 237339: - max_sdirnswrf - Time-maximum surface direct normal short-wave radiation flux 237340: - max_cossza - Time-maximum cosine of solar zenith angle 237341: - max_fprate - Time-maximum freezing rain precipitation rate 237342: - max_vige - Time-maximum total column vertically-integrated eastward geopotential flux 237343: - max_vign - Time-maximum total column vertically-integrated northward geopotential flux 237344: - max_viwgd - Time-maximum total column vertically-integrated divergence of water geopotential flux 237345: - max_vigd - Time-maximum total column vertically-integrated divergence of geopotential flux 237346: - max_viee - Time-maximum total column vertically-integrated eastward enthalpy flux 237347: - max_vien - Time-maximum total column vertically-integrated northward enthalpy flux 237348: - max_vikee - Time-maximum total column vertically-integrated eastward kinetic energy flux 237349: - max_viken - Time-maximum total column vertically-integrated northward kinetic energy flux 237350: - max_vitee - Time-maximum total column vertically-integrated eastward total energy flux 237351: - max_viten - Time-maximum total column vertically-integrated northward total energy flux 237352: - max_vied - Time-maximum total column vertically-integrated divergence of enthalpy flux 237353: - max_viked - Time-maximum total column vertically-integrated divergence of kinetic energy flux 237354: - max_vited - Time-maximum total column vertically-integrated divergence of total energy flux 237355: - max_viwed - Time-maximum total column vertically-integrated divergence of water enthalpy flux 237356: - max_vimad - Time-maximum total column vertically-integrated divergence of mass flux 237357: - max_vimae - Time-maximum total column vertically-integrated eastward mass flux 237358: - max_viman - Time-maximum total column vertically-integrated northward mass flux 237359: - max_viwvd - Time-maximum total column vertically-integrated divergence of water vapour flux 237360: - max_viclwd - Time-maximum total column vertically-integrated divergence of cloud liquid water flux 237361: - max_viciwd - Time-maximum total column vertically-integrated divergence of cloud ice water flux 237362: - max_vird - Time-maximum total column vertically-integrated divergence of rain flux 237363: - max_visd - Time-maximum total column vertically-integrated divergence of snow flux 237364: - max_viwve - Time-maximum total column vertically-integrated eastward water vapour flux 237365: - max_viwvn - Time-maximum total column vertically-integrated northward water vapour flux 237366: - max_viclwe - Time-maximum total column vertically-integrated eastward cloud liquid water flux 237367: - max_viclwn - Time-maximum total column vertically-integrated northward cloud liquid water flux 237368: - max_viciwe - Time-maximum total column vertically-integrated eastward cloud ice water flux 237369: - max_viciwn - Time-maximum total column vertically-integrated northward cloud ice water flux 237370: - max_vire - Time-maximum total column vertically-integrated eastward rain flux 237371: - max_virn - Time-maximum total column vertically-integrated northward rain flux 237372: - max_vise - Time-maximum total column vertically-integrated eastward snow flux 237373: - max_visn - Time-maximum total column vertically-integrated northward snow flux 237374: - max_vioze - Time-maximum total column vertically-integrated eastward ozone flux 237375: - max_viozn - Time-maximum total column vertically-integrated northward ozone flux 237376: - max_viozd - Time-maximum total column vertically-integrated divergence of ozone flux 237377: - max_vions - Time-maximum total column vertically-integrated net source of ozone 237378: - max_ietssofd - Time-maximum eastward turbulent surface stress due to orographic form drag 237379: - max_intssofd - Time-maximum northward turbulent surface stress due to orographic form drag 237380: - max_ietsssr - Time-maximum eastward turbulent surface stress due to surface roughness 237381: - max_intsssr - Time-maximum northward turbulent surface stress due to surface roughness 237382: - max_visp - Time-maximum visibility through precipitation 237383: - max_licgi - Time-maximum cloud-to-ground lightning flash density 237384: - max_10efg - Time-maximum 10 metre eastward wind gust 237385: - max_10nfg - Time-maximum 10 metre northward wind gust 237386: - max_lshnf - Time-maximum land surface heat net flux 237387: - max_sdirnswrfcs - Time-maximum surface direct normal short-wave radiation flux, clear sky 237388: - max_10cogu - Time-maximum 10 metre convective gust 237389: - max_10tugu - Time-maximum 10 metre turbulent gust 237390: - max_h0thg - Time-maximum geometric height of 0 degrees C theta level above ground 237391: - max_h1thg - Time-maximum geometric height of 1 degree C theta level above ground 237392: - max_h1p5thg - Time-maximum geometric height of 1.5 degrees C theta level above ground 237393: - max_veg - Time-maximum vegetation 237394: - max_lai - Time-maximum leaf area index 237395: - max_srhe - Time-maximum surface roughness for heat 237396: - max_rol - Time-maximum Reciprocal Obukhov length 237397: - max_deg3l - Time-maximum geometric height of 3 degrees C atmospheric isothermal level above ground 237404: - max_sdifswrfcs - Time-maximum surface diffuse short-wave radiation flux, clear sky 237405: - max_vswrz - Time-maximum volumetric soil moisture (root zone) 237406: - max_lwcs - Time-maximum liquid water content in snow pack 237407: - max_mucin - Time-maximum most-unstable CIN 237408: - max_crwc_conv - Time-maximum specific rain water content (convective) 237409: - max_cswc_conv - Time-maximum specific snow water content (convective) 237410: - max_mld - Time-maximum mixed layer depth 237411: - max_cur - Time-maximum urban cover 238001: - min_ttswr - Time-minimum temperature tendency due to short-wave radiation 238002: - min_ttlwr - Time-minimum temperature tendency due to long-wave radiation 238003: - min_ttswrcs - Time-minimum temperature tendency due to short wave radiation, clear sky 238004: - min_ttlwrcs - Time-minimum temperature tendency due to long-wave radiation, clear sky 238005: - min_ttpm - Time-minimum temperature tendency due to parametrisations 238006: - min_qtpm - Time-minimum specific humidity tendency due to parametrisations 238007: - min_utpm - Time-minimum eastward wind tendency due to parametrisations 238008: - min_vtpm - Time-minimum northward wind tendency due to parametrisations 238009: - min_umf - Time-minimum updraught mass flux 238010: - min_dmf - Time-minimum downdraught mass flux 238011: - min_udr - Time-minimum updraught detrainment rate 238012: - min_ddr - Time-minimum downdraught detrainment rate 238013: - min_tpf - Time-minimum total precipitation flux 238014: - min_tdch - Time-minimum turbulent diffusion coefficient for heat 238022: - min_parcsf - Time-minimum surface photosynthetically active radiation flux, clear sky 238023: - min_esrwe - Time-minimum snow evaporation rate 238024: - min_smr - Time-minimum snowmelt rate 238025: - min_imagss - Time-minimum magnitude of turbulent surface stress 238026: - min_ilspf - Time-minimum large-scale precipitation fraction 238027: - min_sduvrf - Time-minimum surface downward UV radiation flux 238028: - min_sparf - Time-minimum surface photosynthetically active radiation flux 238029: - min_lsprate - Time-minimum large-scale precipitation rate 238030: - min_cpr - Time-minimum convective precipitation rate 238031: - min_tsrwe - Time-minimum snowfall rate 238032: - min_ibld - Time-minimum boundary layer dissipation 238033: - min_ishf - Time-minimum surface sensible heat flux 238034: - min_slhtf - Time-minimum surface latent heat flux 238035: - min_sdswrf - Time-minimum surface downward short-wave radiation flux 238036: - min_sdlwrf - Time-minimum surface downward long-wave radiation flux 238037: - min_snswrf - Time-minimum surface net short-wave radiation flux 238038: - min_snlwrf - Time-minimum surface net long-wave radiation flux 238039: - min_tnswrf - Time-minimum top net short-wave radiation flux 238040: - min_tnlwrf - Time-minimum top net long-wave radiation flux 238041: - min_iews - Time-minimum eastward turbulent surface stress 238042: - min_inss - Time-minimum northward turbulent surface stress 238043: - min_ie - Time-minimum evaporation rate 238045: - min_iegwss - Time-minimum eastward gravity wave surface stress 238046: - min_ingwss - Time-minimum northward gravity wave surface stress 238047: - min_igwd - Time-minimum gravity wave dissipation 238048: - min_rorwe - Time-minimum runoff rate 238049: - min_tnswrfcs - Time-minimum top net short-wave radiation flux, clear sky 238050: - min_tnlwrfcs - Time-minimum top net long-wave radiation flux, clear sky 238051: - min_snswrfcs - Time-minimum surface net short-wave radiation flux, clear sky 238052: - min_snlwrfcs - Time-minimum surface net long-wave radiation flux, clear sky 238053: - min_tdswrf - Time-minimum top downward short-wave radiation flux 238054: - min_vimdf - Time-minimum vertically integrated moisture divergence 238055: - min_tprate - Time-minimum total precipitation rate 238056: - min_csfr - Time-minimum convective snowfall rate water equivalent 238057: - min_lssfr - Time-minimum large scale snowfall rate water equivalent 238058: - min_sdirswrf - Time-minimum surface direct short-wave radiation flux 238059: - min_sdirswrfcs - Time-minimum surface direct short-wave radiation flux, clear sky 238062: - min_fco2nee - Time-minimum carbon dioxide net ecosystem exchange flux 238063: - min_fco2gpp - Time-minimum carbon dioxide gross primary production flux 238064: - min_fco2rec - Time-minimum carbon dioxide ecosystem respiration flux 238068: - min_sdswrfcs - Time-minimum surface downward short-wave radiation flux, clear sky 238069: - min_sdlwrfcs - Time-minimum surface downward long-wave radiation flux, clear sky 238070: - min_pevr - Time-minimum potential evaporation rate 238077: - min_vsw - Time-minimum volumetric soil moisture 238078: - min_sd - Time-minimum snow depth water equivalent 238079: - min_skt - Time-minimum skin temperature 238080: - min_rsn - Time-minimum snow density 238083: - min_ci - Time-minimum sea ice area fraction 238084: - min_sst - Time-minimum sea surface temperature 238085: - min_lai_lv - Time-minimum leaf area index, low vegetation 238086: - min_lai_hv - Time-minimum leaf area index, high vegetation 238087: - min_tclw - Time-minimum total column cloud liquid water 238088: - min_tciw - Time-minimum total column cloud ice water 238089: - min_2sh - Time-minimum 2 metre specific humidity 238090: - min_lmlt - Time-minimum lake mix-layer temperature 238091: - min_lmld - Time-minimum lake mix-layer depth 238092: - min_2r - Time-minimum 2 metre relative humidity 238093: - min_fscov - Time-minimum fraction of snow cover 238094: - min_sot - Time-minimum soil temperature 238097: - min_ws - Time-minimum wind speed 238098: - min_pres - Time-minimum pressure 238099: - min_chnk - Time-minimum charnock 238100: - min_pv - Time-minimum potential vorticity 238101: - min_crwc - Time-minimum specific rain water content 238102: - min_cswc - Time-minimum specific snow water content 238103: - min_etadot - Time-minimum eta-coordinate vertical velocity 238105: - min_vis - Time-minimum visibility 238106: - min_wdir - Time-minimum wind direction 238107: - min_tcsw - Time-minimum total column snow water 238108: - min_lcc - Time-minimum low cloud cover 238109: - min_mcc - Time-minimum medium cloud cover 238110: - min_hcc - Time-minimum high cloud cover 238111: - min_tcrw - Time-minimum total column rain water 238117: - min_mucape - Time-minimum most-unstable CAPE 238120: - min_tcolg - Time-minimum total column vertically-integrated graupel (snow pellets) 238129: - min_z - Time-minimum geopotential 238130: - min_t - Time-minimum temperature 238131: - min_u - Time-minimum u component of wind 238132: - min_v - Time-minimum v component of wind 238133: - min_q - Time-minimum specific humidity 238134: - min_sp - Time-minimum surface pressure 238135: - min_w - Time-minimum vertical velocity 238136: - min_tcw - Time-minimum total column water 238137: - min_tcwv - Time-minimum total column vertically-integrated water vapour 238138: - min_vo - Time-minimum vorticity (relative) 238151: - min_msl - Time-minimum mean sea level pressure 238152: - min_lnsp - Time-minimum logarithm of surface pressure 238155: - min_d - Time-minimum divergence 238157: - min_r - Time-minimum relative humidity 238159: - min_blh - Time-minimum boundary layer height 238165: - min_10u - Time-minimum 10 metre U wind component 238166: - min_10v - Time-minimum 10 metre V wind component 238167: - min_2t - Time-minimum 2 metre temperature 238168: - min_2d - Time-minimum 2 metre dewpoint temperature 238189: - min_suns - Time-minimum sunshine 238203: - min_o3 - Time-minimum ozone mass mixing ratio 238207: - min_10si - Time-minimum 10 metre wind speed 238238: - min_tsn - Time-minimum temperature of snow layer 238244: - min_fsr - Time-minimum forecast surface roughness 238245: - min_flsr - Time-minimum forecast logarithm of surface roughness for heat 238246: - min_clwc - Time-minimum specific cloud liquid water content 238247: - min_ciwc - Time-minimum specific cloud ice water content 238248: - min_cc - Time-minimum fraction of cloud cover 238257: - min_kx - Time-minimum k index 238258: - min_totalx - Time-minimum total totals index 238261: - min_10wdir - Time-minimum 10 metre wind direction 238262: - min_cat - Time-minimum clear air turbulence (CAT) 238263: - min_al - Time-minimum forecast albedo 238269: - min_pt - Time-minimum potential temperature 238281: - min_aluvp_p - Time-minimum UV visible albedo for direct radiation (climatological) 238282: - min_aluvd_p - Time-minimum UV visible albedo for diffuse radiation (climatological) 238283: - min_alnip_p - Time-minimum near IR albedo for direct radiation (climatological) 238284: - min_alnid_p - Time-minimum near IR albedo for diffuse radiation (climatological) 238285: - min_asn - Time-minimum snow albedo 238286: - min_mont - Time-minimum montgomery potential 238287: - min_cape - Time-minimum convective available potential energy 238288: - min_tcc - Time-minimum total cloud cover 238289: - min_srcon - Time-minimum skin reservoir content 238290: - min_tcioz - Time-minimum total column integrated ozone 238291: - min_vike - Time-minimum total column vertically-integrated kinetic energy 238292: - min_vithe - Time-minimum total column vertically-integrated enthalpy 238293: - min_vipie - Time-minimum total column vertically-integrated potential + internal energy 238294: - min_vipile - Time-minimum vertical integral of potential+internal+latent energy 238295: - min_vitoe - Time-minimum total column vertically-integrated total energy 238296: - min_viwe - Time-minimum total column vertically-integrated water enthalpy 238297: - min_aluvpi_p - Time-minimum UV visible albedo for direct radiation, isotropic component (climatological) 238298: - min_aluvpv_p - Time-minimum UV visible albedo for direct radiation, volumetric component (climatological) 238299: - min_aluvpg_p - Time-minimum UV visible albedo for direct radiation, geometric component (climatological) 238300: - min_alnipi_p - Time-minimum near IR albedo for direct radiation, isotropic component (climatological) 238301: - min_alnipv_p - Time-minimum near IR albedo for direct radiation, volumetric component (climatological) 238302: - min_alnipg_p - Time-minimum near IR albedo for direct radiation, geometric component (climatological) 238303: - min_cin - Time-minimum convective inhibition 238304: - min_zust - Time-minimum friction velocity 238305: - min_lblt - Time-minimum lake bottom temperature 238306: - min_ltlt - Time-minimum lake total layer temperature 238307: - min_lshf - Time-minimum lake shape factor 238308: - min_lict - Time-minimum lake ice surface temperature 238309: - min_licd - Time-minimum lake ice total depth 238310: - min_dndzn - Time-minimum minimum vertical gradient of refractivity inside trapping layer 238311: - min_dndza - Time-minimum mean vertical gradient of refractivity inside trapping layer 238312: - min_dctb - Time-minimum duct base height 238313: - min_tplb - Time-minimum trapping layer base height 238314: - min_tplt - Time-minimum trapping layer top height 238315: - min_degm10l - Time-minimum geometric height of -10 degrees C atmospheric isothermal level above ground 238316: - min_cbh - Time-minimum cloud base height 238317: - min_deg0l - Time-minimum geometric height of 0 degrees C atmospheric isothermal level above ground 238318: - min_i10fg - Time-minimum 10 metre wind gust 238319: - min_rhw - Time-minimum relative humidity with respect to water 238320: - min_2rhw - Time-minimum 2 metre relative humidity with respect to water 238321: - min_capes - Time-minimum convective available potential energy shear 238322: - min_trpp - Time-minimum tropopause pressure 238323: - min_hcct - Time-minimum height of convective cloud top 238324: - min_hwbt0 - Time-minimum height of zero-degree wet-bulb temperature 238325: - min_hwbt1 - Time-minimum height of one-degree wet-bulb temperature 238326: - min_litoti - Time-minimum total lightning flash density 238327: - min_tcslw - Time-minimum total column supercooled liquid water 238328: - min_u10n - Time-minimum 10 metre U-component of neutral wind 238329: - min_v10n - Time-minimum 10 metre V-component of neutral wind 238330: - min_crr - Time-minimum convective rain rate 238331: - min_lsrr - Time-minimum large scale rain rate 238332: - min_mlcape50 - Time-minimum mixed-layer CAPE in the lowest 50 hPa 238333: - min_mlcin50 - Time-minimum mixed-layer CIN in the lowest 50 hPa 238334: - min_mlcape100 - Time-minimum mixed-layer CAPE in the lowest 100 hPa 238335: - min_mlcin100 - Time-minimum mixed-layer CIN in the lowest 100 hPa 238336: - min_mudlp - Time-minimum departure level of the most unstable parcel expressed as Pressure 238337: - min_ceil - Time-minimum ceiling 238338: - min_tcsqw - Time-minimum total column integrated saturation specific humidity with respect to water 238339: - min_sdirnswrf - Time-minimum direct solar radiation flux 238340: - min_cossza - Time-minimum cosine of solar zenith angle 238341: - min_fprate - Time-minimum freezing rain rate water equivalent 238342: - min_vige - Time-minimum total column vertically-integrated eastward geopotential flux 238343: - min_vign - Time-minimum total column vertically-integrated northward geopotential flux 238344: - min_viwgd - Time-minimum total column vertically-integrated divergence of water geopotential flux 238345: - min_vigd - Time-minimum total column vertically-integrated divergence of geopotential flux 238346: - min_viee - Time-minimum total column vertically-integrated eastward enthalpy flux 238347: - min_vien - Time-minimum total column vertically-integrated northward enthalpy flux 238348: - min_vikee - Time-minimum total column vertically-integrated eastward kinetic energy flux 238349: - min_viken - Time-minimum total column vertically-integrated northward kinetic energy flux 238350: - min_vitee - Time-minimum total column vertically-integrated eastward total energy flux 238351: - min_viten - Time-minimum total column vertically-integrated northward total energy flux 238352: - min_vied - Time-minimum total column vertically-integrated divergence of enthalpy flux 238353: - min_viked - Time-minimum total column vertically-integrated divergence of kinetic energy flux 238354: - min_vited - Time-minimum total column vertically-integrated divergence of total energy flux 238355: - min_viwed - Time-minimum total column vertically-integrated divergence of water enthalpy flux 238356: - min_vimad - Time-minimum vertically integrated divergence of mass flux 238357: - min_vimae - Time-minimum vertically integrated eastward mass flux 238358: - min_viman - Time-minimum vertically integrated northward mass flux 238359: - min_viwvd - Time-minimum vertically integrated divergence of water vapour flux 238360: - min_viclwd - Time-minimum vertically integrated divergence of cloud liquid water flux 238361: - min_viciwd - Time-minimum vertically integrated divergence of cloud ice water flux 238362: - min_vird - Time-minimum vertically integrated divergence of rain flux 238363: - min_visd - Time-minimum vertically integrated divergence of snow flux 238364: - min_viwve - Time-minimum vertically integrated eastward water vapour flux 238365: - min_viwvn - Time-minimum vertically integrated northward water vapour flux 238366: - min_viclwe - Time-minimum vertically integrated eastward cloud liquid water flux 238367: - min_viclwn - Time-minimum vertically integrated northward cloud liquid water flux 238368: - min_viciwe - Time-minimum vertically integrated eastward cloud ice water flux 238369: - min_viciwn - Time-minimum vertically integrated northward cloud ice water flux 238370: - min_vire - Time-minimum vertically integrated eastward rain flux 238371: - min_virn - Time-minimum vertically integrated northward rain flux 238372: - min_vise - Time-minimum vertically integrated eastward snow flux 238373: - min_visn - Time-minimum vertically integrated northward snow flux 238374: - min_vioze - Time-minimum vertically integrated eastward ozone flux 238375: - min_viozn - Time-minimum vertically integrated northward ozone flux 238376: - min_viozd - Time-minimum vertically integrated divergence of ozone flux 238377: - min_vions - Time-minimum vertically integrated net source of ozone 238378: - min_ietssofd - Time-minimum eastward turbulent surface stress due to orographic form drag 238379: - min_intssofd - Time-minimum northward turbulent surface stress due to orographic form drag 238380: - min_ietsssr - Time-minimum eastward turbulent surface stress due to surface roughness 238381: - min_intsssr - Time-minimum northward turbulent surface stress due to surface roughness 238382: - min_visp - Time-minimum visibility through precipitation 238383: - min_licgi - Time-minimum cloud-to-ground lightning flash density 238386: - min_lshnf - Time-minimum land surface heat net flux 238387: - min_sdirnswrfcs - Time-minimum surface direct normal short-wave radiation flux, clear sky 238388: - min_10cogu - Time-minimum 10 metre convective gust 238389: - min_10tugu - Time-minimum 10 metre turbulent gust 238390: - min_h0thg - Time-minimum geometric height of 0 degrees C theta level above ground 238391: - min_h1thg - Time-minimum geometric height of 1 degree C theta level above ground 238392: - min_h1p5thg - Time-minimum geometric height of 1.5 degrees C theta level above ground 238393: - min_veg - Time-minimum vegetation 238394: - min_lai - Time-minimum leaf area index 238395: - min_srhe - Time-minimum surface roughness for heat 238396: - min_rol - Time-minimum Reciprocal Obukhov length 238397: - min_deg3l - Time-minimum geometric height of 3 degrees C atmospheric isothermal level above ground 238404: - min_sdifswrfcs - Time-minimum surface diffuse short-wave radiation flux, clear sky 238405: - min_vswrz - Time-minimum volumetric soil moisture (root zone) 238406: - min_lwcs - Time-minimum liquid water content in snow pack 238407: - min_mucin - Time-minimum most-unstable CIN 238408: - min_crwc_conv - Time-minimum specific rain water content (convective) 238409: - min_cswc_conv - Time-minimum specific snow water content (convective) 238410: - min_mld - Time-minimum mixed layer depth 238411: - min_cur - Time-minimum urban cover 239001: - std_ttswr - Time-standard-deviation temperature tendency due to short-wave radiation 239002: - std_ttlwr - Time-standard-deviation temperature tendency due to long-wave radiation 239003: - std_ttswrcs - Time-standard-deviation temperature tendency due to short wave radiation, clear sky 239004: - std_ttlwrcs - Time-standard-deviation temperature tendency due to long-wave radiation, clear sky 239005: - std_ttpm - Time-standard-deviation temperature tendency due to parametrisations 239006: - std_qtpm - Time-standard-deviation specific humidity tendency due to parametrisations 239007: - std_utpm - Time-standard-deviation eastward wind tendency due to parametrisations 239008: - std_vtpm - Time-standard-deviation northward wind tendency due to parametrisations 239009: - std_umf - Time-standard-deviation updraught mass flux 239010: - std_dmf - Time-standard-deviation downdraught mass flux 239011: - std_udr - Time-standard-deviation updraught detrainment rate 239012: - std_ddr - Time-standard-deviation downdraught detrainment rate 239013: - std_tpf - Time-standard-deviation total precipitation flux 239014: - std_tdch - Time-standard-deviation turbulent diffusion coefficient for heat 239022: - std_parcsf - Time-standard-deviation surface photosynthetically active radiation flux, clear sky 239023: - std_esrwe - Time-standard-deviation snow evaporation rate 239024: - std_smr - Time-standard-deviation snowmelt rate 239025: - std_imagss - Time-standard-deviation magnitude of turbulent surface stress 239026: - std_ilspf - Time-standard-deviation large-scale precipitation fraction 239027: - std_sduvrf - Time-standard-deviation surface downward UV radiation flux 239028: - std_sparf - Time-standard-deviation surface photosynthetically active radiation flux 239029: - std_lsprate - Time-standard-deviation large-scale precipitation rate 239030: - std_cpr - Time-standard-deviation convective precipitation rate 239031: - std_tsrwe - Time-standard-deviation snowfall rate 239032: - std_ibld - Time-standard-deviation boundary layer dissipation 239033: - std_ishf - Time-standard-deviation surface sensible heat flux 239034: - std_slhtf - Time-standard-deviation surface latent heat flux 239035: - std_sdswrf - Time-standard-deviation surface downward short-wave radiation flux 239036: - std_sdlwrf - Time-standard-deviation surface downward long-wave radiation flux 239037: - std_snswrf - Time-standard-deviation surface net short-wave radiation flux 239038: - std_snlwrf - Time-standard-deviation surface net long-wave radiation flux 239039: - std_tnswrf - Time-standard-deviation top net short-wave radiation flux 239040: - std_tnlwrf - Time-standard-deviation top net long-wave radiation flux 239041: - std_iews - Time-standard-deviation eastward turbulent surface stress 239042: - std_inss - Time-standard-deviation northward turbulent surface stress 239043: - std_ie - Time-standard-deviation evaporation rate 239045: - std_iegwss - Time-standard-deviation eastward gravity wave surface stress 239046: - std_ingwss - Time-standard-deviation northward gravity wave surface stress 239047: - std_igwd - Time-standard-deviation gravity wave dissipation 239048: - std_rorwe - Time-standard-deviation runoff rate 239049: - std_tnswrfcs - Time-standard-deviation top net short-wave radiation flux, clear sky 239050: - std_tnlwrfcs - Time-standard-deviation top net long-wave radiation flux, clear sky 239051: - std_snswrfcs - Time-standard-deviation surface net short-wave radiation flux, clear sky 239052: - std_snlwrfcs - Time-standard-deviation surface net long-wave radiation flux, clear sky 239053: - std_tdswrf - Time-standard-deviation top downward short-wave radiation flux 239054: - std_vimdf - Time-standard-deviation vertically integrated moisture divergence 239055: - std_tprate - Time-standard-deviation total precipitation rate 239056: - std_csfr - Time-standard-deviation convective snowfall rate water equivalent 239057: - std_lssfr - Time-standard-deviation large scale snowfall rate water equivalent 239058: - std_sdirswrf - Time-standard-deviation surface direct short-wave radiation flux 239059: - std_sdirswrfcs - Time-standard-deviation surface direct short-wave radiation flux, clear sky 239062: - std_fco2nee - Time-standard-deviation carbon dioxide net ecosystem exchange flux 239063: - std_fco2gpp - Time-standard-deviation carbon dioxide gross primary production flux 239064: - std_fco2rec - Time-standard-deviation carbon dioxide ecosystem respiration flux 239068: - std_sdswrfcs - Time-standard-deviation surface downward short-wave radiation flux, clear sky 239069: - std_sdlwrfcs - Time-standard-deviation surface downward long-wave radiation flux, clear sky 239070: - std_pevr - Time-standard-deviation potential evaporation rate 239077: - std_vsw - Time-standard-deviation volumetric soil moisture 239078: - std_sd - Time-standard-deviation snow depth water equivalent 239079: - std_skt - Time-standard-deviation skin temperature 239080: - std_rsn - Time-standard-deviation snow density 239083: - std_ci - Time-standard-deviation sea ice area fraction 239084: - std_sst - Time-standard-deviation sea surface temperature 239085: - std_lai_lv - Time-standard-deviation leaf area index, low vegetation 239086: - std_lai_hv - Time-standard-deviation leaf area index, high vegetation 239087: - std_tclw - Time-standard-deviation total column cloud liquid water 239088: - std_tciw - Time-standard-deviation total column cloud ice water 239089: - std_2sh - Time-standard-deviation 2 metre specific humidity 239090: - std_lmlt - Time-standard-deviation lake mix-layer temperature 239091: - std_lmld - Time-standard-deviation lake mix-layer depth 239093: - std_fscov - Time-standard-deviation fraction of snow cover 239094: - std_sot - Time-standard-deviation soil temperature 239097: - std_ws - Time-standard-deviation wind speed 239098: - std_pres - Time-standard-deviation pressure 239099: - std_chnk - Time-standard-deviation charnock 239100: - std_pv - Time-standard-deviation potential vorticity 239101: - std_crwc - Time-standard-deviation specific rain water content 239102: - std_cswc - Time-standard-deviation specific snow water content 239103: - std_etadot - Time-standard-deviation eta-coordinate vertical velocity 239105: - std_vis - Time-standard-deviation visibility 239106: - std_wdir - Time-standard-deviation wind direction 239107: - std_tcsw - Time-standard-deviation total column snow water 239108: - std_lcc - Time-standard-deviation low cloud cover 239109: - std_mcc - Time-standard-deviation medium cloud cover 239110: - std_hcc - Time-standard-deviation high cloud cover 239111: - std_tcrw - Time-standard-deviation total column rain water 239117: - std_mucape - Time-standard-deviation most-unstable CAPE 239120: - std_tcolg - Time-standard-deviation total column vertically-integrated graupel (snow pellets) 239129: - std_z - Time-standard-deviation geopotential 239130: - std_t - Time-standard-deviation temperature 239131: - std_u - Time-standard-deviation u component of wind 239132: - std_v - Time-standard-deviation v component of wind 239133: - std_q - Time-standard-deviation specific humidity 239134: - std_sp - Time-standard-deviation surface pressure 239135: - std_w - Time-standard-deviation vertical velocity 239136: - std_tcw - Time-standard-deviation total column water 239137: - std_tcwv - Time-standard-deviation total column vertically-integrated water vapour 239138: - std_vo - Time-standard-deviation vorticity (relative) 239151: - std_msl - Time-standard-deviation mean sea level pressure 239152: - std_lnsp - Time-standard-deviation logarithm of surface pressure 239155: - std_d - Time-standard-deviation divergence 239157: - std_r - Time-standard-deviation relative humidity 239159: - std_blh - Time-standard-deviation boundary layer height 239165: - std_10u - Time-standard-deviation 10 metre U wind component 239166: - std_10v - Time-standard-deviation 10 metre V wind component 239167: - std_2t - Time-standard-deviation 2 metre temperature 239168: - std_2d - Time-standard-deviation 2 metre dewpoint temperature 239189: - std_suns - Time-standard-deviation sunshine 239203: - std_o3 - Time-standard-deviation ozone mass mixing ratio 239207: - std_10si - Time-standard-deviation 10 metre wind speed 239238: - std_tsn - Time-standard-deviation temperature of snow layer 239244: - std_fsr - Time-standard-deviation forecast surface roughness 239245: - std_flsr - Time-standard-deviation forecast logarithm of surface roughness for heat 239246: - std_clwc - Time-standard-deviation specific cloud liquid water content 239247: - std_ciwc - Time-standard-deviation specific cloud ice water content 239248: - std_cc - Time-standard-deviation fraction of cloud cover 239257: - std_kx - Time-standard-deviation k index 239258: - std_totalx - Time-standard-deviation total totals index 239261: - std_10wdir - Time-standard-deviation 10 metre wind direction 239262: - std_cat - Time-standard-deviation clear air turbulence (CAT) 239263: - std_al - Time-standard-deviation forecast albedo 239269: - std_pt - Time-standard-deviation potential temperature 239281: - std_aluvp_p - Time-standard-deviation UV visible albedo for direct radiation (climatological) 239282: - std_aluvd_p - Time-standard-deviation UV visible albedo for diffuse radiation (climatological) 239283: - std_alnip_p - Time-standard-deviation near IR albedo for direct radiation (climatological) 239284: - std_alnid_p - Time-standard-deviation near IR albedo for diffuse radiation (climatological) 239285: - std_asn - Time-standard-deviation snow albedo 239286: - std_mont - Time-standard-deviation montgomery potential 239287: - std_cape - Time-standard-deviation convective available potential energy 239288: - std_tcc - Time-standard-deviation total cloud cover 239289: - std_srcon - Time-standard-deviation skin reservoir content 239290: - std_tcioz - Time-standard-deviation total column integrated ozone 239291: - std_vike - Time-standard-deviation total column vertically-integrated kinetic energy 239292: - std_vithe - Time-standard-deviation total column vertically-integrated enthalpy 239293: - std_vipie - Time-standard-deviation total column vertically-integrated potential + internal energy 239294: - std_vipile - Time-standard-deviation vertical integral of potential+internal+latent energy 239295: - std_vitoe - Time-standard-deviation total column vertically-integrated total energy 239296: - std_viwe - Time-standard-deviation total column vertically-integrated water enthalpy 239297: - std_aluvpi_p - Time-standard-deviation UV visible albedo for direct radiation, isotropic component (climatological) 239298: - std_aluvpv_p - Time-standard-deviation UV visible albedo for direct radiation, volumetric component (climatological) 239299: - std_aluvpg_p - Time-standard-deviation UV visible albedo for direct radiation, geometric component (climatological) 239300: - std_alnipi_p - Time-standard-deviation near IR albedo for direct radiation, isotropic component (climatological) 239301: - std_alnipv_p - Time-standard-deviation near IR albedo for direct radiation, volumetric component (climatological) 239302: - std_alnipg_p - Time-standard-deviation near IR albedo for direct radiation, geometric component (climatological) 239303: - std_cin - Time-standard-deviation convective inhibition 239304: - std_zust - Time-standard-deviation friction velocity 239305: - std_lblt - Time-standard-deviation lake bottom temperature 239306: - std_ltlt - Time-standard-deviation lake total layer temperature 239307: - std_lshf - Time-standard-deviation lake shape factor 239308: - std_lict - Time-standard-deviation lake ice surface temperature 239309: - std_licd - Time-standard-deviation lake ice total depth 239310: - std_dndzn - Time-standard-deviation minimum vertical gradient of refractivity inside trapping layer 239311: - std_dndza - Time-standard-deviation mean vertical gradient of refractivity inside trapping layer 239312: - std_dctb - Time-standard-deviation duct base height 239313: - std_tplb - Time-standard-deviation trapping layer base height 239314: - std_tplt - Time-standard-deviation trapping layer top height 239315: - std_degm10l - Time-standard-deviation geometric height of -10 degrees C atmospheric isothermal level above ground 239316: - std_cbh - Time-standard-deviation cloud base height 239317: - std_deg0l - Time-standard-deviation geometric height of 0 degrees C atmospheric isothermal level above ground 239318: - std_i10fg - Time-standard-deviation 10 metre wind gust 239319: - std_rhw - Time-standard-deviation relative humidity with respect to water 239320: - std_2rhw - Time-standard-deviation 2 metre relative humidity with respect to water 239321: - std_capes - Time-standard-deviation convective available potential energy shear 239322: - std_trpp - Time-standard-deviation tropopause pressure 239323: - std_hcct - Time-standard-deviation height of convective cloud top 239324: - std_hwbt0 - Time-standard-deviation height of zero-degree wet-bulb temperature 239325: - std_hwbt1 - Time-standard-deviation height of one-degree wet-bulb temperature 239326: - std_litoti - Time-standard-deviation total lightning flash density 239327: - std_tcslw - Time-standard-deviation total column supercooled liquid water 239328: - std_u10n - Time-standard-deviation 10 metre U-component of neutral wind 239329: - std_v10n - Time-standard-deviation 10 metre V-component of neutral wind 239330: - std_crr - Time-standard-deviation convective rain rate 239331: - std_lsrr - Time-standard-deviation large scale rain rate 239332: - std_mlcape50 - Time-standard-deviation mixed-layer CAPE in the lowest 50 hPa 239333: - std_mlcin50 - Time-standard-deviation mixed-layer CIN in the lowest 50 hPa 239334: - std_mlcape100 - Time-standard-deviation mixed-layer CAPE in the lowest 100 hPa 239335: - std_mlcin100 - Time-standard-deviation mixed-layer CIN in the lowest 100 hPa 239336: - std_mudlp - Time-standard-deviation departure level of the most unstable parcel expressed as Pressure 239337: - std_ceil - Time-standard-deviation ceiling 239338: - std_tcsqw - Time-standard-deviation total column integrated saturation specific humidity with respect to water 239339: - std_sdirnswrf - Time-standard-deviation direct solar radiation flux 239340: - std_cossza - Time-standard-deviation cosine of solar zenith angle 239341: - std_fprate - Time-standard-deviation freezing rain rate water equivalent 239342: - std_vige - Time-standard-deviation total column vertically-integrated eastward geopotential flux 239343: - std_vign - Time-standard-deviation total column vertically-integrated northward geopotential flux 239344: - std_viwgd - Time-standard-deviation total column vertically-integrated divergence of water geopotential flux 239345: - std_vigd - Time-standard-deviation total column vertically-integrated divergence of geopotential flux 239346: - std_viee - Time-standard-deviation total column vertically-integrated eastward enthalpy flux 239347: - std_vien - Time-standard-deviation total column vertically-integrated northward enthalpy flux 239348: - std_vikee - Time-standard-deviation total column vertically-integrated eastward kinetic energy flux 239349: - std_viken - Time-standard-deviation total column vertically-integrated northward kinetic energy flux 239350: - std_vitee - Time-standard-deviation total column vertically-integrated eastward total energy flux 239351: - std_viten - Time-standard-deviation total column vertically-integrated northward total energy flux 239352: - std_vied - Time-standard-deviation total column vertically-integrated divergence of enthalpy flux 239353: - std_viked - Time-standard-deviation total column vertically-integrated divergence of kinetic energy flux 239354: - std_vited - Time-standard-deviation total column vertically-integrated divergence of total energy flux 239355: - std_viwed - Time-standard-deviation total column vertically-integrated divergence of water enthalpy flux 239356: - std_vimad - Time-standard-deviation vertically integrated divergence of mass flux 239357: - std_vimae - Time-standard-deviation vertically integrated eastward mass flux 239358: - std_viman - Time-standard-deviation vertically integrated northward mass flux 239359: - std_viwvd - Time-standard-deviation vertically integrated divergence of water vapour flux 239360: - std_viclwd - Time-standard-deviation vertically integrated divergence of cloud liquid water flux 239361: - std_viciwd - Time-standard-deviation vertically integrated divergence of cloud ice water flux 239362: - std_vird - Time-standard-deviation vertically integrated divergence of rain flux 239363: - std_visd - Time-standard-deviation vertically integrated divergence of snow flux 239364: - std_viwve - Time-standard-deviation vertically integrated eastward water vapour flux 239365: - std_viwvn - Time-standard-deviation vertically integrated northward water vapour flux 239366: - std_viclwe - Time-standard-deviation vertically integrated eastward cloud liquid water flux 239367: - std_viclwn - Time-standard-deviation vertically integrated northward cloud liquid water flux 239368: - std_viciwe - Time-standard-deviation vertically integrated eastward cloud ice water flux 239369: - std_viciwn - Time-standard-deviation vertically integrated northward cloud ice water flux 239370: - std_vire - Time-standard-deviation vertically integrated eastward rain flux 239371: - std_virn - Time-standard-deviation vertically integrated northward rain flux 239372: - std_vise - Time-standard-deviation vertically integrated eastward snow flux 239373: - std_visn - Time-standard-deviation vertically integrated northward snow flux 239374: - std_vioze - Time-standard-deviation vertically integrated eastward ozone flux 239375: - std_viozn - Time-standard-deviation vertically integrated northward ozone flux 239376: - std_viozd - Time-standard-deviation vertically integrated divergence of ozone flux 239377: - std_vions - Time-standard-deviation vertically integrated net source of ozone 239378: - std_ietssofd - Time-standard-deviation eastward turbulent surface stress due to orographic form drag 239379: - std_intssofd - Time-standard-deviation northward turbulent surface stress due to orographic form drag 239380: - std_ietsssr - Time-standard-deviation eastward turbulent surface stress due to surface roughness 239381: - std_intsssr - Time-standard-deviation northward turbulent surface stress due to surface roughness 239383: - std_licgi - Time-standard-deviation cloud-to-ground lightning flash density 239386: - std_lshnf - Time-standard-deviation land surface heat net flux 239387: - std_sdirnswrfcs - Time-standard-deviation surface direct normal short-wave radiation flux, clear sky 239388: - std_10cogu - Time-standard-deviation 10 metre convective gust 239389: - std_10tugu - Time-standard-deviation 10 metre turbulent gust 239390: - std_h0thg - Time-standard-deviation geometric height of 0 degrees C theta level above ground 239391: - std_h1thg - Time-standard-deviation geometric height of 1 degree C theta level above ground 239392: - std_h1p5thg - Time-standard-deviation geometric height of 1.5 degrees C theta level above ground 239393: - std_veg - Time-standard-deviation vegetation 239394: - std_lai - Time-standard-deviation leaf area index 239395: - std_srhe - Time-standard-deviation surface roughness for heat 239396: - std_rol - Time-standard-deviation Reciprocal Obukhov length 239397: - std_deg3l - Time-standard-deviation geometric height of 3 degrees C atmospheric isothermal level above ground 239404: - std_sdifswrfcs - Time-standard-deviation surface diffuse short-wave radiation flux, clear sky 239405: - std_vswrz - Time-standard-deviation volumetric soil moisture (root zone) 239406: - std_lwcs - Time-standard-deviation liquid water content in snow pack 239407: - std_mucin - Time-standard-deviation most-unstable CIN 239408: - std_crwc_conv - Time-standard-deviation specific rain water content (convective) 239409: - std_cswc_conv - Time-standard-deviation specific snow water content (convective) 239410: - std_mld - Time-standard-deviation mixed layer depth 239411: - std_cur - Time-standard-deviation urban cover 240011: - chcross - Cross sectional area of flow in channel 240012: - chside - Side flow into river channel 240013: - dis - Discharge from rivers or streams 240014: - rivsto - River storage of water 240015: - fldsto - Floodplain storage of water 240016: - fldfrc - Water fraction 240017: - dslr - Days since last observation 240018: - frost - Frost index 240020: - woss - Depth of water on soil surface 240021: - tpups - Upstream accumulated precipitation 240022: - smups - Upstream accumulated snow melt 240023: - dis06 - Mean discharge in the last 6 hours 240024: - dis24 - Mean discharge in the last 24 hours 240026: - sd_elev - Snow depth at elevation bands 240028: - gwus - Groundwater upper storage 240029: - gwls - Groundwater lower storage 240030: - lakdph - Lake depth 240031: - rivdph - River depth 240032: - rivout - River outflow of water 240033: - fldout - Floodplain outflow of water 240034: - pthflw - Floodpath outflow of water 240035: - flddep - Floodplain depth 240036: - fldffr - Floodplain flooded fraction 240037: - fldfar - Floodplain flooded area 240038: - rivfr - River fraction 240039: - rivar - River area 240040: - rivcffr - Fraction of river coverage plus river related flooding 240041: - rivcfar - Area of river coverage plus river related flooding 240042: - wse - Water surface elevation 240043: - grfr - Groundwater return flow rate 240044: - rivfldsto - River and floodplain storage 240045: - darv - Depth-averaged river velocity 250001: - lat - Latitude 250002: - lon - Longitude 250003: - tlat - Latitude on T grid 250004: - tlon - Longitude on T grid 250005: - ulat - Latitude on U grid 250006: - ulon - Longitude on U grid 250007: - vlat - Latitude on V grid 250008: - vlon - Longitude on V grid 250009: - wlat - Latitude on W grid 250010: - wlon - Longitude on W grid 250011: - flat - Latitude on F grid 250012: - flon - Longitude on F grid 254001: - covar_t2m_swvl1 - Covariance between 2-metre temperature and volumetric soil water layer 1 254002: - covar_rh2m_swvl1 - Covariance between 2-metre relative humidity and volumetric soil water layer 1 254003: - covar_ssm_swvl1 - Covariance between surface soil moisture and volumetric soil water layer 1 254004: - covar_t2m_swvl2 - Covariance between 2-metre temperature and volumetric soil water layer 2 254005: - covar_rh2m_swvl2 - Covariance between 2-metre relative humidity and volumetric soil water layer 2 254006: - covar_ssm_swvl2 - Covariance between surface soil moisture and volumetric soil water layer 2 254007: - covar_t2m_swvl3 - Covariance between 2-metre temperature and volumetric soil water layer 3 254008: - covar_rh2m_swvl3 - Covariance between 2-metre relative humidity and volumetric soil water layer 3 254009: - covar_ssm_swvl3 - Covariance between surface soil moisture and volumetric soil water layer 3 254010: - covar_t2m_stl1 - Covariance between 2-metre temperature and soil temperature layer 1 254011: - covar_rh2m_stl1 - Covariance between 2-metre relative humidity and soil temperature layer 1 254012: - covar_t2m_stl2 - Covariance between 2-metre temperature and soil temperature layer 2 254013: - covar_rh2m_stl2 - Covariance between 2-metre relative humidity and soil temperature layer 2 254014: - covar_t2m_stl3 - Covariance between 2-metre temperature and soil temperature layer 3 254015: - covar_rh2m_stl3 - Covariance between 2-metre relative humidity and soil temperature layer 3 254016: - covar_t2m_tsn1 - Covariance between 2-metre temperature and temperature of snow layer 1 254017: - covar_rh2m_tsn1 - Covariance between 2-metre relative humidity and temperature of snow layer 1 254018: - covar_t2m_tsn2 - Covariance between 2-metre temperature and temperature of snow layer 2 254019: - covar_rh2m_tsn2 - Covariance between 2-metre relative humidity and temperature of snow layer 2 254020: - covar_t2m_tsn3 - Covariance between 2-metre temperature and temperature of snow layer 3 254021: - covar_rh2m_tsn3 - Covariance between 2-metre relative humidity and temperature of snow layer 3 254022: - covar_skt_stl1 - Covariance between skin temperature and soil temperature layer 1 254023: - covar_skt_stl2 - Covariance between skin temperature and soil temperature layer 2 254024: - covar_skt_stl3 - Covariance between skin temperature and soil temperature layer 3 254025: - covar_skt_swvl1 - Covariance between skin temperature and volumetric soil water layer 1 254026: - covar_skt_swvl2 - Covariance between skin temperature and volumetric soil water layer 2 254027: - covar_skt_swvl3 - Covariance between skin temperature and volumetric soil water layer 3 254028: - covar_skt_tsn1 - Covariance between skin temperature and temperature of snow layer 1 254029: - covar_skt_tsn2 - Covariance between skin temperature and temperature of snow layer 2 254030: - covar_skt_tsn3 - Covariance between skin temperature and temperature of snow layer 3 254500: - curvmsl - CURV diagnostic mean sea level pressure 254501: - curvz - CURV diagnostic geopotential 260001: - tcolg - Total column vertically-integrated graupel (snow pellets) 260002: - slhtf - Surface latent heat net flux 260004: - heatx - Heat index 260005: - wcf - Wind chill factor 260006: - mindpd - Minimum dew point depression 260007: - snohf - Snow phase change heat flux 260008: - vapp - Vapor pressure 260009: - ncpcp - Large scale precipitation (non-convective) 260010: - srweq - Snowfall rate water equivalent 260011: - snoc - Convective snow 260012: - snol - Large scale snow 260013: - snoag - Snow age 260014: - absh - Absolute humidity 260015: - ptype - Precipitation type 260016: - iliqw - Integrated liquid water 260017: - tcond - Condensate 260018: - clwmr - Cloud mixing ratio 260019: - icmr - Ice water mixing ratio 260020: - rwmr - Rain mixing ratio 260021: - snmr - Snow mixing ratio 260022: - mconv - Horizontal moisture convergence 260023: - maxrh - Maximum relative humidity 260024: - maxah - Maximum absolute humidity 260025: - asnow - Total snowfall 260026: - pwcat - Precipitable water category 260027: - hail 260028: - grle - Graupel (snow pellets) 260029: - crain - Categorical rain 260030: - cfrzr - Categorical freezing rain 260031: - cicep - Categorical ice pellets 260032: - csnow - Categorical snow 260033: - cpr - Convective precipitation rate 260034: - mdiv - Horizontal moisture divergence 260035: - cpofp - Percent frozen precipitation 260036: - pevap - Potential evaporation 260038: - snowc - Snow cover 260039: - frain - Rain fraction of total cloud water 260040: - rime - Rime factor 260041: - tcolr - Total column integrated rain 260042: - tcols - Total column integrated snow 260043: - lswp - Large scale water precipitation (non-convective) 260044: - cwp - Convective water precipitation 260045: - twatp - Total water precipitation 260046: - tsnowp - Total snow precipitation 260047: - tcwat - Total column water (Vertically integrated total water (vapour + cloud water/ice)) 260048: - tprate - Total precipitation rate 260049: - tsrwe - Total snowfall rate water equivalent 260050: - lsprate - Large scale precipitation rate 260053: - tsrate - Total snowfall rate 260054: - csrate - Convective snowfall rate 260055: - lssrate - Large scale snowfall rate 260056: - sdwe - Water equivalent of accumulated snow depth (deprecated) 260057: - tciwv - Total column integrated water vapour 260058: - rprate - Rain precipitation rate 260059: - sprate - Snow precipitation rate 260060: - fprate - Freezing rain precipitation rate 260061: - iprate - Ice pellets precipitation rate 260062: - uflx - Momentum flux, u component 260063: - vflx - Momentum flux, v component 260064: - maxgust - Maximum wind speed 260065: - gust - Wind speed (gust) 260066: - ugust - u-component of wind (gust) 260067: - vgust - v-component of wind (gust) 260068: - vwsh - Vertical speed shear 260069: - mflx - Horizontal momentum flux 260070: - ustm - U-component storm motion 260071: - vstm - V-component storm motion 260072: - cd - Drag coefficient 260073: - fricv - Frictional velocity 260074: - prmsl - Pressure reduced to MSL 260076: - alts - Altimeter setting 260077: - thick - Thickness 260078: - presalt - Pressure altitude 260079: - denalt - Density altitude 260080: - 5wavh - 5-wave geopotential height 260081: - iegwss - Instantaneous eastward gravity wave surface stress 260082: - ingwss - Instantaneous northward gravity wave surface stress 260084: - 5wava - 5-wave geopotential height anomaly 260085: - sdsgso - Standard deviation of sub-grid scale orography 260086: - nswrt - Net short-wave radiation flux (top of atmosphere) 260087: - sdswrf - Surface downward short-wave radiation flux 260088: - suswrf - Surface upward short-wave radiation flux 260089: - snswrf - Surface net short-wave radiation flux 260090: - sparf - Surface photosynthetically active radiation flux 260091: - snswrfcs - Surface net short-wave radiation flux, clear sky 260092: - sduvrf - Surface downward UV radiation flux 260093: - uviucs - UV index (under clear sky) 260094: - uvi - UV index 260095: - nlwrs - Net long wave radiation flux (surface) 260096: - nlwrt - Net long wave radiation flux (top of atmosphere) 260097: - sdlwrf - Surface downward long-wave radiation flux 260098: - sulwrf - Surface upward long-wave radiation flux 260099: - snlwrf - Surface net long-wave radiation flux 260100: - snlwrfcs - Surface net long-wave radiation flux, clear sky 260101: - cice - Cloud Ice 260102: - cwat - Cloud water 260103: - cdca - Cloud amount 260104: - cdct - Cloud type 260105: - tmaxt - Thunderstorm maximum tops 260106: - thunc - Thunderstorm coverage 260107: - cdcb - Cloud base 260108: - cdct - Cloud top 260109: - ceil - Ceiling 260110: - cdlyr - Non-convective cloud cover 260111: - cwork - Cloud work function 260112: - cuefi - Convective cloud efficiency 260113: - tcond - Total condensate 260114: - tcolw - Total column-integrated cloud water 260115: - tcoli - Total column-integrated cloud ice 260116: - tcolc - Total column-integrated condensate 260117: - fice - Ice fraction of total condensate 260118: - cdcimr - Cloud ice mixing ratio 260119: - suns - Sunshine 260120: - _param_260120 - Horizontal extent of cumulonimbus (CB) 260121: - kx - K index 260122: - kox - KO index 260123: - totalx - Total totals index 260124: - sx - Sweat index 260125: - hlcy - Storm relative helicity 260126: - ehlx - Energy helicity index 260127: - lftx - Surface lifted index 260128: - 4lftx - Best (4-layer) lifted index 260129: - aerot - Aerosol type 260130: - tozne - Total ozone 260131: - o3mr - Ozone mixing ratio 260132: - tcioz - Total column integrated ozone 260133: - bswid - Base spectrum width 260134: - bref - Base reflectivity 260135: - brvel - Base radial velocity 260136: - veril - Vertically-integrated liquid 260137: - lmaxbr - Layer-maximum base reflectivity 260138: - rdp - Precipitation from radar 260139: - acces - Air concentration of Caesium 137 260140: - aciod - Air concentration of Iodine 131 260141: - acradp - Air concentration of radioactive pollutant 260142: - gdces - Ground deposition of Caesium 137 260143: - gdiod - Ground deposition of Iodine 131 260144: - gdradp - Ground deposition of radioactive pollutant 260145: - tiaccp - Time-integrated air concentration of caesium pollutant 260146: - tiacip - Time-integrated air concentration of iodine pollutant 260147: - tiacrp - Time-integrated air concentration of radioactive pollutant 260148: - volash - Volcanic ash 260149: - icit - Icing top 260150: - icib - Icing base 260151: - ici - Icing 260152: - turbt - Turbulence top 260153: - turbb - Turbulence base 260154: - turb - Turbulence 260155: - tke - Turbulent kinetic energy 260156: - pblreg - Planetary boundary layer regime 260157: - conti - Contrail intensity 260158: - contet - Contrail engine type 260159: - contt - Contrail top 260160: - contb - Contrail base 260161: - mxsalb - Maximum snow albedo 260162: - snfalb - Snow free albedo 260163: - _param_260163 - Icing 260164: - _param_260164 - In-cloud turbulence 260165: - rcat - Relative clear air turbulence (RCAT) 260166: - sldp - Supercooled large droplet probability 260167: - var190m0 - Arbitrary text string 260168: - tsec - Seconds prior to initial reference time (defined in Section 1) 260169: - ffldg - Flash flood guidance 260170: - ffldro - Flash flood runoff (Encoded as an accumulation over a floating subinterval of time) 260171: - rssc - Remotely sensed snow cover 260172: - esct - Elevation of snow covered terrain 260173: - swepon - Snow water equivalent percent of normal 260174: - bgrun - Baseflow-groundwater runoff 260175: - ssrun - Storm surface runoff 260176: - cppop - Conditional percent precipitation amount fractile for an overall period (Encoded as an accumulation) 260177: - pposp - Percent precipitation in a sub-period of an overall period 260178: - pop - Probability of 0.01 inch of precipitation (POP) 260180: - veg - Vegetation 260181: - watr - Water runoff 260182: - evapt - Evapotranspiration 260183: - mterh - Model terrain height 260184: - landu - Land use 260185: - soilw - Volumetric soil moisture content 260186: - gflux - Ground heat flux 260187: - mstav - Moisture availability 260188: - sfexc - Exchange coefficient 260189: - cnwat - Plant canopy surface water 260190: - bmixl - Blackadar mixing length scale 260191: - ccond - Canopy conductance 260192: - rsmin - Minimal stomatal resistance 260193: - rcs - Solar parameter in canopy conductance 260194: - rct - Temperature parameter in canopy conductance 260195: - rcsol - Soil moisture parameter in canopy conductance 260196: - rcq - Humidity parameter in canopy conductance 260197: - cisoilw - Column-integrated soil water 260198: - hflux - Heat flux 260199: - vsw - Volumetric soil moisture 260200: - vwiltm - Volumetric wilting point 260201: - uplst - Upper layer soil temperature 260202: - uplsm - Upper layer soil moisture 260203: - lowlsm - Lower layer soil moisture 260204: - botlst - Bottom layer soil temperature 260205: - soill - Liquid volumetric soil moisture (non-frozen) 260206: - rlyrs - Number of soil layers in root zone 260207: - smref - Transpiration stress-onset (soil moisture) 260208: - smdry - Direct evaporation cease (soil moisture) 260209: - poros - Soil porosity 260210: - liqvsm - Liquid volumetric soil moisture (non-frozen) 260211: - voltso - Volumetric transpiration stress-onset (soil moisture) 260212: - transo - Transpiration stress-onset (soil moisture) 260213: - voldec - Volumetric direct evaporation cease (soil moisture) 260214: - direc - Direct evaporation cease (soil moisture) 260215: - soilp - Soil porosity 260216: - vsosm - Volumetric saturation of soil moisture 260217: - satosm - Saturation of soil moisture 260218: - estp - Estimated precipitation 260219: - irrate - Instantaneous rain rate 260220: - ctoph - Cloud top height 260221: - ctophqi - Cloud top height quality indicator 260222: - estu - Estimated u component of wind 260223: - estv - Estimated v component of wind 260224: - npixu - Number of pixels used 260225: - solza - Solar zenith angle 260226: - raza - Relative azimuth angle 260227: - rfl06 - Reflectance in 0.6 micron channel 260228: - rfl08 - Reflectance in 0.8 micron channel 260229: - rfl16 - Reflectance in 1.6 micron channel 260230: - rfl39 - Reflectance in 3.9 micron channel 260231: - atmdiv - Atmospheric divergence 260232: - wvdir - Direction of wind waves 260233: - dirpw - Primary wave direction 260234: - perpw - Primary wave mean period 260235: - persw - Secondary wave mean period 260236: - dirc - Current direction 260237: - spc - Current speed 260238: - wz - Geometric vertical velocity 260241: - tsec - Seconds prior to initial reference time (defined in Section 1) 260242: - 2r - 2 metre relative humidity 260243: - ttrad - Temperature tendency by all radiation 260244: - rev - Relative Error Variance 260245: - lrghr - Large Scale Condensate Heating rate 260246: - cnvhr - Deep Convective Heating rate 260247: - thflx - Total Downward Heat Flux at Surface 260248: - ttdia - Temperature Tendency By All Physics 260249: - ttphy - Temperature Tendency By Non-radiation Physics 260250: - tsd1d - Standard Dev. of IR Temp. over 1x1 deg. area 260251: - shahr - Shallow Convective Heating rate 260252: - vdfhr - Vertical Diffusion Heating rate 260253: - thz0 - Potential temperature at top of viscous sublayer 260254: - tchp - Tropical Cyclone Heat Potential 260255: - aptmp - Apparent temperature 260256: - hindex - Haines Index 260257: - ccl - Cloud cover 260259: - eva - Evaporation 260260: - 10wdir - 10 metre wind direction 260261: - minrh - Minimum Relative Humidity 260262: - sdirswrf - Surface direct short-wave radiation flux 260263: - sdifswrf - Surface diffuse short-wave radiation flux 260264: - tidirswrf - Time-integrated surface direct short wave radiation flux 260265: - eva06 - Evaporation in the last 6 hours 260266: - eva24 - Evaporation in the last 24 hours 260267: - tp06 - Total precipitation in the last 6 hours 260268: - tp24 - Total precipitation in the last 24 hours 260269: - tipd - Total Icing Potential Diagnostic 260270: - ncip - Number concentration for ice particles 260271: - snot - Snow temperature 260272: - tclsw - Total column-integrated supercooled liquid water 260273: - tcolm - Total column-integrated melting ice 260274: - emnp - Evaporation - Precipitation 260275: - sbsno - Sublimation (evaporation from snow) 260276: - cnvmr - Deep Convective Moistening Rate 260277: - shamr - Shallow Convective Moistening Rate 260278: - vdfmr - Vertical Diffusion Moistening Rate 260279: - condp - Condensation Pressure of Parcali Lifted From Indicate Surface 260280: - lrgmr - Large scale moistening rate 260281: - qz0 - Specific humidity at top of viscous sublayer 260282: - qmax - Maximum specific humidity at 2m 260283: - qmin - Minimum specific humidity at 2m 260284: - arain - Liquid precipitation (rainfall) 260285: - snowt - Snow temperature, depth-avg 260286: - apcpn - Total precipitation (nearest grid point) 260287: - acpcpn - Convective precipitation (nearest grid point) 260288: - frzr - Freezing Rain 260289: - fscov - Fraction of snow cover 260290: - cat - Clear air turbulence (CAT) 260291: - mwt - Mountain wave turbulence (eddy dissipation rate) 260292: - crwc_conv - Specific rain water content (convective) 260293: - cswc_conv - Specific snow water content (convective) 260294: - glm - Glacier mask 260295: - lauv - Latitude of U Wind Component of Velocity 260296: - louv - Longitude of U Wind Component of Velocity 260297: - lavv - Latitude of V Wind Component of Velocity 260298: - lovv - Longitude of V Wind Component of Velocity 260299: - lapp - Latitude of Pressure Point 260300: - lopp - Longitude of Pressure Point 260301: - vedh - Vertical Eddy Diffusivity Heat exchange 260302: - covmz - Covariance between Meridional and Zonal Components of the wind. 260303: - covtz - Covariance between Temperature and Zonal Components of the wind. 260304: - covtm - Covariance between Temperature and Meridional Components of the wind. 260305: - vdfua - Vertical Diffusion Zonal Acceleration 260306: - vdfva - Vertical Diffusion Meridional Acceleration 260307: - gwdu - Gravity wave drag zonal acceleration 260308: - gwdv - Gravity wave drag meridional acceleration 260309: - cnvu - Convective zonal momentum mixing acceleration 260310: - cnvv - Convective meridional momentum mixing acceleration 260311: - wtend - Tendency of vertical velocity 260312: - omgalf - Omega (Dp/Dt) divide by density 260313: - cngwdu - Convective Gravity wave drag zonal acceleration 260314: - cngwdv - Convective Gravity wave drag meridional acceleration 260315: - lmv - Velocity Point Model Surface 260316: - pvmww - Potential Vorticity (Mass-Weighted) 260317: - mslet - MSLP (Eta model reduction) 260318: - ptype_sev1h - Precipitation type (most severe) in the last 1 hour 260319: - ptype_sev3h - Precipitation type (most severe) in the last 3 hours 260320: - ptype_freq1h - Precipitation type (most frequent) in the last 1 hour 260321: - ptype_freq3h - Precipitation type (most frequent) in the last 3 hours 260323: - mslma - MSLP (MAPS System Reduction) 260324: - tslsa - 3-hr pressure tendency (Std. Atmos. Reduction) 260325: - plpl - Pressure of level from which parcel was lifted 260326: - lpsx - X-gradient of Log Pressure 260327: - lpsy - Y-gradient of Log Pressure 260328: - hgtx - X-gradient of Height 260329: - hgty - Y-gradient of Height 260330: - layth - Layer Thickness 260331: - nlgsp - Natural Log of Surface Pressure 260332: - cnvumf - Convective updraft mass flux 260333: - cnvdmf - Convective downdraft mass flux 260334: - cnvdemf - Convective detrainment mass flux 260335: - lmh - Mass Point Model Surface 260336: - hgtn - Geopotential Height (nearest grid point) 260337: - presn - Pressure (nearest grid point) 260338: - ptype_sev6h - Precipitation type (most severe) in the last 6 hours 260339: - ptype_freq6h - Precipitation type (most frequent) in the last 6 hours 260340: - duvb - UV-B downward solar flux 260341: - cduvb - Clear sky UV-B downward solar flux 260342: - csdsf - Clear Sky Downward Solar Flux 260343: - swhr - Solar Radiative Heating Rate 260344: - csusf - Clear Sky Upward Solar Flux 260345: - cfnsf - Cloud Forcing Net Solar Flux 260346: - vbdsf - Visible Beam Downward Solar Flux 260347: - vddsf - Visible Diffuse Downward Solar Flux 260348: - nbdsf - Near IR Beam Downward Solar Flux 260349: - nddsf - Near IR Diffuse Downward Solar Flux 260350: - dtrf - Downward Total radiation Flux 260351: - utrf - Upward Total radiation Flux 260354: - lwhr - Long-Wave Radiative Heating Rate 260355: - csulf - Clear Sky Upward Long Wave Flux 260356: - csdlf - Clear Sky Downward Long Wave Flux 260357: - cfnlf - Cloud Forcing Net Long Wave Flux 260360: - sot - Soil temperature 260361: - sdswrfcs - Surface downward short-wave radiation flux, clear sky 260362: - suswrfcs - Surface upward short-wave radiation flux, clear sky 260363: - sdlwrfcs - Surface downward long-wave radiation flux, clear sky 260364: - sohf - Soil heat flux 260365: - percr - Percolation rate 260366: - mflux - Convective Cloud Mass Flux 260367: - sod - Soil depth 260368: - som - Soil moisture 260369: - ri - Richardson Number 260370: - cwdi - Convective Weather Detection Index 260372: - uphl - Updraft Helicity 260373: - lai - Leaf Area Index 260374: - pmtc - Particulate matter (coarse) 260375: - pmtf - Particulate matter (fine) 260376: - lpmtf - Particulate matter (fine) 260377: - lipmf - Integrated column particulate matter (fine) 260379: - ozcon - Ozone Concentration (PPB) 260380: - ozcat - Categorical Ozone Concentration 260381: - vdfoz - Ozone vertical diffusion 260382: - poz - Ozone production 260383: - toz - Ozone tendency 260384: - pozt - Ozone production from temperature term 260385: - pozo - Ozone production from col ozone term 260386: - refzr - Derived radar reflectivity backscatter from rain 260387: - refzi - Derived radar reflectivity backscatter from ice 260388: - refzc - Derived radar reflectivity backscatter from parameterized convection 260389: - refd - Derived radar reflectivity 260390: - refc - Maximum/Composite radar reflectivity 260391: - ltng - Lightning 260394: - srcono - Slight risk convective outlook 260395: - mrcono - Moderate risk convective outlook 260396: - hrcono - High risk convective outlook 260397: - torprob - Tornado probability 260398: - hailprob - Hail probability 260399: - windprob - Wind probability 260400: - storprob - Significant Tornado probability 260401: - shailpro - Significant Hail probability 260402: - swindpro - Significant Wind probability 260403: - tstmc - Categorical Thunderstorm (1-yes, 0-no) 260404: - mixly - Number of mixed layers next to surface 260405: - flght - Flight Category 260406: - cicel - Confidence - Ceiling 260407: - civis - Confidence - Visibility 260408: - ciflt - Confidence - Flight Category 260409: - lavni - Low-Level aviation interest 260410: - havni - High-Level aviation interest 260411: - sbsalb - Visible, Black Sky Albedo 260412: - swsalb - Visible, White Sky Albedo 260413: - nbsalb - Near IR, Black Sky Albedo 260414: - nwsalb - Near IR, White Sky Albedo 260415: - prsvr - Total Probability of Severe Thunderstorms (Days 2,3) 260416: - prsigsvr - Total Probability of Extreme Severe Thunderstorms (Days 2,3) 260417: - sipd - Supercooled Large Droplet (SLD) Potential 260418: - epsr - Radiative emissivity 260419: - tpfi - Turbulence Potential Forecast Index 260420: - vaftd - Volcanic Ash Forecast Transport and Dispersion 260421: - nlat - Latitude (-90 to +90) 260422: - elon - East Longitude (0 - 360) 260423: - adswrf_cs - Accumulated surface downward short-wave radiation flux, clear sky 260424: - mlyno - Model Layer number (From bottom up) 260425: - nlatn - Latitude (nearest neighbor) (-90 to +90) 260426: - elonn - East Longitude (nearest neighbor) (0 - 360) 260427: - auswrf_cs - Accumulated surface upward short-wave radiation flux, clear sky 260428: - adlwrf_cs - Accumulated surface downward long-wave radiation flux, clear sky 260429: - cpozp - Probability of Freezing Precipitation 260430: - perc - Percolation 260431: - ppffg - Probability of precipitation exceeding flash flood guidance values 260432: - cwr - Probability of Wetting Rain, exceeding in 0.10 in a given time period 260433: - etr - Evapotranspiration rate 260434: - acc_et24 - Time-integrated evapotranspiration rate in the last 24h 260435: - avg_et24 - Time-mean evapotranspiration rate in the last 24h 260436: - petr - Potential evapotranspiration rate 260437: - acc_pet24 - Time-integrated potential evapotranspiration rate in the last 24h 260438: - avg_pet24 - Time-mean potential evapotranspiration rate in the last 24h 260439: - vgtyp - Vegetation Type 260440: - avg_swv24 - Time-mean volumetric soil moisture 260441: - avg_tp24 - Time-mean total precipitation rate 260442: - wilt - Wilting Point 260443: - rorwe - Runoff rate water equivalent (surface plus subsurface) 260444: - acc_rod24 - Time-integrated water runoff and drainage rate in the last 24h 260445: - avg_rod24 - Time-mean water runoff and drainage rate in the last 24h 260447: - rdrip - Rate of water dropping from canopy to ground 260448: - icwat - Ice-free water surface 260449: - akhs - Surface exchange coefficients for T and Q divided by delta z 260450: - akms - Surface exchange coefficients for U and V divided by delta z 260451: - vegt - Vegetation canopy temperature 260452: - sstor - Surface water storage 260453: - lsoil - Liquid soil moisture content (non-frozen) 260454: - ewatr - Open water evaporation (standing water) 260455: - gwrec - Groundwater recharge 260456: - qrec - Flood plain recharge 260457: - sfcrh - Roughness length for heat 260458: - ndvi - Normalized Difference Vegetation Index 260459: - landn - Land-sea coverage (nearest neighbor) [land=1,sea=0] 260460: - amixl - Asymptotic mixing length scale 260461: - wvinc - Water vapor added by precip assimilation 260462: - wcinc - Water condensate added by precip assimilation 260463: - wvconv - Water Vapor Flux Convergence (Vertical Int) 260464: - wcconv - Water Condensate Flux Convergence (Vertical Int) 260465: - wvuflx - Water Vapor Zonal Flux (Vertical Int) 260466: - wvvflx - Water Vapor Meridional Flux (Vertical Int) 260467: - wcuflx - Water Condensate Zonal Flux (Vertical Int) 260468: - wcvflx - Water Condensate Meridional Flux (Vertical Int) 260469: - acond - Aerodynamic conductance 260470: - evcw - Canopy water evaporation 260471: - trans - Transpiration 260472: - avg_sd24 - Time-mean snow depth water equivalent 260473: - avg_skt24 - Time-mean skin temperature 260474: - sltyp - Surface Slope Type 260475: - smr - Snow melt rate 260476: - acc_smr24 - Time-integrated snow melt rate in the last 24h 260477: - avg_smr24 - Time-mean snow melt rate in the last 24h 260478: - evbs - Direct evaporation from bare soil 260479: - lspa - Land Surface Precipitation Accumulation 260480: - baret - Bare soil surface skin temperature 260481: - avsft - Average surface skin temperature 260482: - radt - Effective radiative skin temperature 260483: - fldcp - Field Capacity 260484: - usct - Scatterometer Estimated U Wind Component 260485: - vsct - Scatterometer Estimated V Wind Component 260486: - wstp - Wave Steepness 260487: - omlu - Ocean Mixed Layer U Velocity 260488: - omlv - Ocean Mixed Layer V Velocity 260489: - ubaro - Barotropic U velocity 260490: - vbaro - Barotropic V velocity 260491: - surge - Storm Surge 260492: - etsrg - Extra Tropical Storm Surge 260493: - elevhtml - Ocean Surface Elevation Relative to Geoid 260494: - sshg - Sea Surface Height Relative to Geoid 260495: - p2omlt - Ocean Mixed Layer Potential Density (Reference 2000m)
bytes: if isinstance(data, bytes): return data if not isinstance(data, str): data = str(data) return data.encode(encoding="utf-8", errors="surrogateescape") def ffi_decode(data: FFI.CData) -> str: buf = ffi.string(data) if isinstance(buf, str): return buf else: return buf.decode(encoding="utf-8", errors="surrogateescape") class MarsRequest: def __init__(self, verb: str | None = None, **kwargs): """ Create MetKit MarsRequest object. Parameters and values in the request can be specified through kwargs, noting that reserved words in Python must be suffixed with "_" e.g. "class_" """ crequest = ffi.new("metkit_marsrequest_t **") lib.metkit_marsrequest_new(crequest) self.__request = ffi.gc(crequest[0], lib.metkit_marsrequest_delete) if verb is not None: lib.metkit_marsrequest_set_verb(self.__request, ffi_encode(verb)) for param, values in kwargs.items(): self[param.rstrip("_")] = values def ctype(self) -> FFI.CData: return self.__request def verb(self) -> str: cverb = ffi.new("const char **") lib.metkit_marsrequest_verb(self.__request, cverb) return ffi_decode(cverb[0]) def expand(self, inherit: bool = True, strict: bool = False) -> "MarsRequest": """ Return expanded request Params ------ inherit: bool, if True, populates expanded request with default values strict: bool, if True, raise error instead of warning for invalid values Returns ------- Request, resulting from expansion """ expanded_request = MarsRequest() lib.metkit_marsrequest_expand( self.__request, inherit, strict, expanded_request.ctype() ) return expanded_request def validate(self): """ Check if request is valid against MARS language definition. Does not inherit missing parameters. Raises ------ Exception if request is incompatible with MARS language definition """ self.expand(False, True) def keys(self) -> Iterator[str]: """ Get iterator over parameters in request Returns ------- Iterator over parameter names """ it_c = ffi.new("metkit_paramiterator_t **") lib.metkit_marsrequest_params(self.__request, it_c) it = ffi.gc(it_c[0], lib.metkit_paramiterator_delete) while lib.metkit_paramiterator_next(it) == lib.METKIT_ITERATOR_SUCCESS: cparam = ffi.new("const char **") lib.metkit_paramiterator_current(it, cparam) param = ffi_decode(cparam[0]) yield param def num_values(self, param: str) -> int: """ Number of values for parameter Params ------ param: parameter name Returns ------- int """ cparam = ffi_encode(param) count = ffi.new("size_t *", 0) lib.metkit_marsrequest_count_values(self.__request, cparam, count) return count[0] def merge(self, other: "MarsRequest") -> "MarsRequest": """ Merge the values in another request with existing request and returns result as a new Request object. Does not modify inputs to merge. Both input requests must contain the same values and the resulting request object must be compatible with MARS language definition Params ------ other: Request, request to merge with self Returns ------- Request, containing the result of the merge Raises ------ ValueError if parameters in the two requests do not match MetKitException if resulting request is not compatible with MARS language definition """ if set(self.keys()) != set(other.keys()): raise ValueError("Can not merge requests with different parameters.") res = MarsRequest(self.verb(), **{k: v for k, v in self}) lib.metkit_marsrequest_merge(res.ctype(), other.ctype()) res.validate() return res def __iter__(self) -> Iterator[tuple[str, list[str]]]: for param in self.keys(): yield param, self[param] def __getitem__(self, param: str) -> str | list[str]: nvalues = self.num_values(param) values = [] for index in range(nvalues): cvalue = ffi.new("const char **") lib.metkit_marsrequest_value(self.__request, ffi_encode(param), index, cvalue) value = ffi_decode(cvalue[0]) if nvalues == 1: return value values.append(value) return values def __contains__(self, param: str) -> bool: has = ffi.new("bool *", False) lib.metkit_marsrequest_has_param(self.__request, ffi_encode(param), has) return has[0] def __setitem__(self, param: str, values: int | str | list[str]): if isinstance(values, (str, int)): values = [values] cvals = [] for value in values: if isinstance(value, int): value = str(value) cvals.append(ffi.new("const char[]", value.encode("ascii"))) lib.metkit_marsrequest_set( self.__request, ffi_encode(param), ffi.new("const char*[]", cvals), len(values), ) def __eq__(self, other: "MarsRequest") -> bool: if self.verb() != other.verb(): return False expanded = self.expand() other_expanded = other.expand() return dict(expanded) == dict(other_expanded) def parse_mars_request(file_or_str: IO | str, strict: bool = False) -> list[MarsRequest]: """ Function for parsing mars request from file object or string. Params ------ file_or_str: string or file-like object, containing mars request strict: bool, whether to raise error or warning when request is not compatible with MARS language definition. In the case of warning, when False, the incompatible parameters are unset from the request. Returns ------- list of Request """ crequest_iter = ffi.new("metkit_requestiterator_t **") if isinstance(file_or_str, str): lib.metkit_parse_marsrequests(ffi_encode(file_or_str), crequest_iter, strict) else: lib.metkit_parse_marsrequests( ffi_encode(file_or_str.read()), crequest_iter, strict ) request_iter = ffi.gc(crequest_iter[0], lib.metkit_requestiterator_delete) requests = [] while lib.metkit_requestiterator_next(request_iter) == lib.METKIT_ITERATOR_SUCCESS: new_request = MarsRequest() lib.metkit_requestiterator_current(request_iter, new_request.ctype()) requests.append(new_request) return requests class MetKitException(RuntimeError): """Raised when MetKit library throws exception""" pass class CFFIModuleLoadFailed(ImportError): """Raised when the shared library fails to load""" pass class PatchedLib: """ Patch a CFFI library with error handling Finds the header file associated with the MetKit C API and parses it, loads the shared library, and patches the accessors with automatic python-C error handling. """ def __init__(self): libName = findlibs.find("metkit") if libName is None: raise RuntimeError("MetKit library not found") ffi.cdef(self.__read_header()) self.__lib = ffi.dlopen(libName) # All of the executable members of the CFFI-loaded library are functions in the MetKit # C API. These should be wrapped with the correct error handling. Otherwise forward # these on directly. for f in dir(self.__lib): try: attr = getattr(self.__lib, f) setattr( self, f, self.__check_error(attr, f) if callable(attr) else attr ) except Exception as e: print(e) print("Error retrieving attribute", f, "from library") # Initialise the library, and set it up for python-appropriate behaviour self.metkit_initialise() # Check the library version versionstr = ffi.string(self.metkit_version()).decode("utf-8") if versionstr != __version__: warnings.warn(f"Metkit library version {versionstr} does not match python version {__version__}") def __read_header(self): with open(os.path.join(os.path.dirname(__file__), "metkit_c.h"), "r") as f: return f.read() def __check_error(self, fn, name: str): """ If calls into the MetKit library return errors, ensure that they get detected and reported by throwing an appropriate python exception. """ def wrapped_fn(*args, **kwargs): # debug retval = fn(*args, **kwargs) # Some functions dont return error codes. Ignore these. if name in ["metkit_version", "metkit_git_sha1"]: return retval # error codes: if retval not in ( self.__lib.METKIT_SUCCESS, self.__lib.METKIT_ITERATOR_SUCCESS, self.__lib.METKIT_ITERATOR_COMPLETE, ): err = ffi_decode(self.__lib.metkit_get_error_string(retval)) msg = "Error in function '{}': {}".format(name, err) raise MetKitException(msg) return retval return wrapped_fn try: lib = PatchedLib() except CFFIModuleLoadFailed as e: raise ImportError() from e metkit-1.19.2/python/pymetkit/src/pymetkit/metkit_c.h0000664000175000017500000000611715232567717023105 0ustar alastairalastair struct metkit_marsrequest_t; typedef struct metkit_marsrequest_t metkit_marsrequest_t; struct metkit_requestiterator_t; typedef struct metkit_requestiterator_t metkit_requestiterator_t; struct metkit_paramiterator_t; typedef struct metkit_paramiterator_t metkit_paramiterator_t; typedef enum metkit_error_values_t { METKIT_SUCCESS = 0, /* Operation succeded. */ METKIT_ERROR = 1, /* Operation failed. */ METKIT_ERROR_UNKNOWN = 2, /* Failed with an unknown error. */ METKIT_ERROR_USER = 3, /* Failed with an user error. */ METKIT_ERROR_ASSERT = 4 /* Failed with an assert() */ } metkit_error_t; typedef enum metkit_iterator_status_t { METKIT_ITERATOR_SUCCESS = 0, /* Operation succeded. */ METKIT_ITERATOR_COMPLETE = 1, /* All elements have been returned */ METKIT_ITERATOR_ERROR = 2 /* Operation failed. */ } metkit_iterator_status_t; const char* metkit_get_error_string(enum metkit_error_values_t err); const char* metkit_version(); const char* metkit_git_sha1(); metkit_error_t metkit_initialise(); metkit_error_t metkit_parse_marsrequests(const char* str, metkit_requestiterator_t** requests, bool strict); metkit_error_t metkit_marsrequest_new(metkit_marsrequest_t** request); metkit_error_t metkit_marsrequest_delete(const metkit_marsrequest_t* request); metkit_error_t metkit_marsrequest_set(metkit_marsrequest_t* request, const char* param, const char* values[], int numValues); metkit_error_t metkit_marsrequest_set_one(metkit_marsrequest_t* request, const char* param, const char* value); metkit_error_t metkit_marsrequest_set_verb(metkit_marsrequest_t* request, const char* verb); metkit_error_t metkit_marsrequest_verb(const metkit_marsrequest_t* request, const char** verb); metkit_error_t metkit_marsrequest_has_param(const metkit_marsrequest_t* request, const char* param, bool* has); metkit_error_t metkit_marsrequest_params(const metkit_marsrequest_t* request, metkit_paramiterator_t** params); metkit_error_t metkit_marsrequest_count_values(const metkit_marsrequest_t* request, const char* param, size_t* count); metkit_error_t metkit_marsrequest_value(const metkit_marsrequest_t* request, const char* param, int index, const char** value); metkit_error_t metkit_marsrequest_expand(const metkit_marsrequest_t* request, bool inherit, bool strict, metkit_marsrequest_t* expandedRequest); metkit_error_t metkit_marsrequest_merge(metkit_marsrequest_t* request, const metkit_marsrequest_t* otherRequest); metkit_error_t metkit_requestiterator_delete(const metkit_requestiterator_t* it); metkit_iterator_status_t metkit_requestiterator_next(metkit_requestiterator_t* it); metkit_iterator_status_t metkit_requestiterator_current(metkit_requestiterator_t* it, metkit_marsrequest_t* request); metkit_error_t metkit_paramiterator_delete(const metkit_paramiterator_t* it); metkit_iterator_status_t metkit_paramiterator_next(metkit_paramiterator_t* it); metkit_iterator_status_t metkit_paramiterator_current(const metkit_paramiterator_t* it, const char** param); metkit-1.19.2/python/pymetkit/README.md0000664000175000017500000000216515232567717017756 0ustar alastairalastair# pymetkit This repository contains an Python interface to the MetKit library for parsing MARS requests. ## Example The function for parsing a MARS request is `metkit.parse_mars_request` which accepts a string or file-like object as inputs. A list of `metkit.mars.Request` instances are returned, which is a dictionary containing the keys and values in the MARS request and the attribute `verb` for the verb in the MARS request. ### From String ``` from metkit import parse_mars_request request_str = "retrieve,class=od,date=20240124,time=12,param=129,step=12,target=test.grib" requests = parse_mars_request(requests) print(requests[0]) # verb: retrieve, request: {'class': ['od'], 'date': ['20240124'], 'time': ['1200'], 'param': ['129'], 'step': ['12'], 'target': ['test.grib'], 'domain': ['g'], 'expver': ['0001'], 'levelist': ['1000', '850', '700', '500', '400', '300'], 'levtype': ['pl'], 'stream': ['oper'], 'type': ['an']} ``` ### From File If the MARS request is contained inside a file, e.g. test_requests.txt: ``` from metkit import parse_mars_request requests = parse_mars_request(open("test_requests.txt", "r")) ``` metkit-1.19.2/python/metkitlib/0000775000175000017500000000000015232567717016611 5ustar alastairalastairmetkit-1.19.2/python/metkitlib/setup.cfg0000664000175000017500000000021515232567717020430 0ustar alastairalastair[metadata] description = "metkitlib" long_description = file: README.md long_description_content_type = text/markdown author = file: AUTHORS metkit-1.19.2/python/metkitlib/buildconfig0000664000175000017500000000157615232567717021032 0ustar alastairalastair# (C) Copyright 2024- 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. # to be source'd by wheelmaker's compile.sh *and* wheel-linux.sh # NOTE replace the whole thing with pyproject.toml? Less powerful, and quaint to use for sourcing ecbuild invocation # TODO we duplicate information -- pyproject.toml's `name` and `packages` are derivable from $NAME and must stay consistent NAME="metkit" CMAKE_PARAMS="-Deckit_ROOT=/tmp/metkit/prereqs/eckitlib -Deccodes_ROOT=/tmp/metkit/prereqs/eccodeslib -DENABLE_GRIB=1" PYPROJECT_DIR="python/metkitlib" DEPENDENCIES='["eckitlib", "eccodeslib"]' metkit-1.19.2/python/metkitlib/setup.py0000664000175000017500000000006215232567717020321 0ustar alastairalastairfrom setup_utils import plain_setup plain_setup() metkit-1.19.2/.vscode/0000775000175000017500000000000015232567717014645 5ustar alastairalastairmetkit-1.19.2/.vscode/launch.json0000664000175000017500000001317715232567717017023 0ustar alastairalastair{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Python: Current File", "type": "python", "request": "launch", "program": "${file}", "console": "integratedTerminal", "justMyCode": true }, { "name": "make params MARS yaml (deprecated method)", "type": "python", "request": "launch", "program": "${env:HOME}/git/metkit/share/metkit/make-params-yaml.py", "console": "integratedTerminal", "justMyCode": false, "env": { "ECFLOW_DIR": "/usr/local/apps/ecflow/5.8.1" // "${env:ECFLOW_DIR}" }, "args": [""], "cwd":"${env:HOME}/git/metkit/share/metkit/" }, { "name": "make paramIds yaml - 0001", "type": "python", "request": "launch", "program": "${env:HOME}/git/metkit/share/metkit/make-paramids-yaml.py", "console": "integratedTerminal", "justMyCode": false, "env": { "ECFLOW_DIR": "/usr/local/apps/ecflow/5.8.1" // "${env:ECFLOW_DIR}" }, "args": [""], "cwd":"${env:HOME}/git/metkit/share/metkit/" }, { "name": "make paramIds yaml - 0078", "type": "python", "request": "launch", "program": "${env:HOME}/git/metkit/share/metkit/make-paramids-yaml-esuite.py", "console": "integratedTerminal", "justMyCode": false, "env": { "ECFLOW_DIR": "/usr/local/apps/ecflow/5.8.1" // "${env:ECFLOW_DIR}" }, "args": [""], "cwd":"${env:HOME}/git/metkit/share/metkit/" }, { "name": "make params FDB yaml - 0001", "type": "python", "request": "launch", "program": "${env:HOME}/git/metkit/share/metkit/make-params-yaml-from-fields-catalogue.py", "console": "integratedTerminal", "justMyCode": false, "env": { "ECFLOW_DIR": "/usr/local/apps/ecflow/5.8.1", // "${env:ECFLOW_DIR}" "PRODUCT_CATALOGUE_HOST": "bol-db-products-prod-01", "PRODUCT_CATALOGUE_DB": "products", "PRODUCT_CATALOGUE_USER": "ecmwf_ro", "PRODUCT_CATALOGUE_PASSWORD": "ecmwf_ro", "PRODUCT_CATALOGUE_PORT": "5432", "VALIDATION_SERVICE_HOST": "https://products.ecmwf.int/requirements/" }, "args": [""], "cwd":"${env:HOME}/git/metkit/share/metkit/" }, { "name": "make params FDB yaml - 0078 - preprod", "type": "python", "request": "launch", "program": "${env:HOME}/git/metkit/share/metkit/make-params-yaml-from-fields-catalogue.py", "console": "integratedTerminal", "justMyCode": false, "env": { "ECFLOW_DIR": "/usr/local/apps/ecflow/5.8.1", // "${env:ECFLOW_DIR}" "PRODUCT_CATALOGUE_HOST": "k8s-bol-webapps-test-worker-016", "PRODUCT_CATALOGUE_DB": "products", "PRODUCT_CATALOGUE_USER": "products", "PRODUCT_CATALOGUE_PASSWORD": "products", "PRODUCT_CATALOGUE_PORT": "30544", "VALIDATION_SERVICE_HOST": "https://products-test.ecmwf.int/esuite/requirements/" }, "args": [""], "cwd":"${env:HOME}/git/metkit/share/metkit/" }, { "name": "make params FDB yaml - 0078 - prod", "type": "python", "request": "launch", "program": "${env:HOME}/git/metkit/share/metkit/make-params-yaml-from-fields-catalogue.py", "console": "integratedTerminal", "justMyCode": false, "env": { "ECFLOW_DIR": "/usr/local/apps/ecflow/5.8.1", // "${env:ECFLOW_DIR}" "PRODUCT_CATALOGUE_HOST": "k8s-bol-webapps-prod-worker-012", "PRODUCT_CATALOGUE_DB": "products", "PRODUCT_CATALOGUE_USER": "products", "PRODUCT_CATALOGUE_PASSWORD": "products", "PRODUCT_CATALOGUE_PORT": "30545", "VALIDATION_SERVICE_HOST": "https://products.ecmwf.int/esuite/requirements/" }, "args": [""], "cwd":"${env:HOME}/git/metkit/share/metkit/" }, { "name": "make fields DB yaml - 0001", "type": "python", "request": "launch", "program": "${env:HOME}/git/metkit/share/metkit/fieldsdb.py", "console": "integratedTerminal", "justMyCode": false, "env": { "ECFLOW_DIR": "/usr/local/apps/ecflow/5.8.1", // "${env:ECFLOW_DIR}" }, "args": [""], "cwd":"${env:HOME}/git/metkit/share/metkit/" }, { "name": "make fields DB yaml - 0078", "type": "python", "request": "launch", "program": "${env:HOME}/git/metkit/share/metkit/fieldsdb-esuite.py", "console": "integratedTerminal", "justMyCode": false, "env": { "ECFLOW_DIR": "/usr/local/apps/ecflow/5.8.1", // "${env:ECFLOW_DIR}" }, "args": [""], "cwd":"${env:HOME}/git/metkit/share/metkit/" }, ] } metkit-1.19.2/README.md0000664000175000017500000000430615232567717014566 0ustar alastairalastair [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/ecmwf/metkit/blob/develop/LICENSE) [![Static Badge](https://github.com/ecmwf/codex/raw/refs/heads/main/Project%20Maturity/graduated_badge.svg)](https://github.com/ecmwf/codex/raw/refs/heads/main/Project%20Maturity#graduated) > \[!IMPORTANT\] > This software is **Graduated** and subject to ECMWF's guidelines on [Software Maturity](https://github.com/ecmwf/codex/raw/refs/heads/main/Project%20Maturity). # metkit ## Requirements Runtime dependencies: - eckit -- http://github.com/ecmwf/eckit Build dependencies: - CMake --- For use and installation see http://www.cmake.org/ - ecbuild --- ECMWF library of CMake macros () ## Installation metkit employs an out-of-source build/install based on CMake. Make sure ecbuild is installed and the ecbuild executable script is found ( `which ecbuild` ). Now proceed with installation as follows: ```bash # Environment --- Edit as needed srcdir=$(pwd) builddir=build installdir=$HOME/local # 1. Create the build directory: mkdir $builddir cd $builddir # 2. Run CMake ecbuild --prefix=$installdir -- -DECKIT_PATH= $srcdir # 3. Compile / Install make -j10 make install ``` ## Release Config files should be updated before releasing a new version. Please refer to the documentation available on [Confluence](https://confluence.ecmwf.int/display/METK/MetKit+configuration) ## License Copyright 2021 European Centre for Medium-Range Weather Forecasts (ECMWF) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at > http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 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.metkit-1.19.2/metkit-import.cmake.in0000664000175000017500000000065015232567717017521 0ustar alastairalastairset( metkit_HAVE_GRIB @metkit_HAVE_GRIB@ ) set( metkit_HAVE_ODB @metkit_HAVE_ODB@ ) include( CMakeFindDependencyMacro ) find_dependency( eckit HINTS ${CMAKE_CURRENT_LIST_DIR}/../eckit @eckit_DIR@ ) if( metkit_HAVE_GRIB ) find_dependency( eccodes HINTS ${CMAKE_CURRENT_LIST_DIR}/../eccodes @metkit_DIR@ ) endif() if( metkit_HAVE_ODB ) find_dependency( odc HINTS ${CMAKE_CURRENT_LIST_DIR}/../odc @odc_DIR@ ) endif() metkit-1.19.2/.clang-format0000664000175000017500000000544015232567717015662 0ustar alastairalastairLanguage: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: false AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: true AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AllowShortEnumsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakAfterReturnType: Automatic BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: AfterColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false EmptyLineBeforeAccessModifier: Always EmptyLineAfterAccessModifier: Always FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeCategories: - Regex: '^<.*\.h>' Priority: 1 - Regex: '^<.*' Priority: 2 - Regex: '.*' Priority: 3 IncludeIsMainRegex: '([-_](test|unittest))?$' IndentCaseLabels: true IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 2 NamespaceIndentation: None PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Auto TabWidth: 4 UseTab: Never metkit-1.19.2/docs/0000775000175000017500000000000015232567717014234 5ustar alastairalastairmetkit-1.19.2/docs/requirements.txt0000664000175000017500000000013715232567717017521 0ustar alastairalastairSphinx~=8.1 breathe~=4.35 pydata-sphinx-theme myst-parser sphinxcontrib-mermaid sphinx-autoapi metkit-1.19.2/docs/conf.py0000664000175000017500000000674215232567717015544 0ustar alastairalastair# Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html import datetime import os # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "Metkit" copyright = f"{datetime.datetime.today().year}, ECMWF" author = "ECMWF" version = "local-dev" release = "local-dev" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [ "sphinx.ext.autosectionlabel", "sphinxcontrib.mermaid", "autoapi.extension", "sphinx.ext.viewcode", "sphinx.ext.napoleon", "sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.inheritance_diagram", "breathe", "myst_parser", ] templates_path = ["_templates"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "_internal"] autoapi_dirs = ["../python/pymetkit/src/pymetkit"] autoapi_type = "python" autoapi_generate_api_docs = True autoapi_add_toctree_entry = False autoapi_python_class_content = "both" autoapi_ignore = [ "*/_internal/*", ] add_module_names = False autoapi_keep_files = False # -- Napoleon settings napoleon_google = True # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "pydata_sphinx_theme" html_context = {"default_mode": "auto"} html_theme_options = { "switcher": { "json_url": "https://sites.ecmwf.int/docs/metkit/versions.json", "version_match": version, }, "show_toc_level": 2, "icon_links": [ { "name": "GitHub", "url": "https://github.com/ecmwf/metkit", "icon": "fa-brands fa-github", }, ], "navbar_align": "left", "navbar_start": ["navbar-logo"], "navbar_center": ["navbar-nav"], "navbar_end": ["navbar-icon-links", "theme-switcher", "version-switcher"], "navbar_persistent": ["search-button"], "secondary_sidebar_items": ["page-toc", "edit-this-page", "sourcelink"], # On local builds no version.json is present "check_switcher": False, "content_footer_items": [], "footer_start": ["copyright"], "footer_center": ["sphinx-version"], "footer_end": ["theme-version"], } html_sidebars = {"**": ["sidebar-nav-bs"]} html_static_path = ["_static"] # -- Breathe configuration --------------------------------------------------- breathe_projects = {"Metkit": os.environ.get("DOXYGEN_XML_DIR", "doxygen/xml")} breathe_default_project = "Metkit" # -- Doxygen HTML ------------------------------------------------------------ # The full Doxygen-generated HTML (indexes, tables, class/file lists) is copied # verbatim into the output under ``doxygen/`` so it can be surfaced under the # "Doxygen" section. ``DOXYGEN_HTML_EXTRA_DIR`` must point at a directory that # contains a ``doxygen/`` subfolder with the generated HTML (see build_docs.sh # and docs/CMakeLists.txt). _doxygen_html_extra_dir = os.environ.get("DOXYGEN_HTML_EXTRA_DIR") html_extra_path = [] if _doxygen_html_extra_dir and os.path.isdir(_doxygen_html_extra_dir): html_extra_path.append(_doxygen_html_extra_dir) # -- autosectionlabel configuration ------------------------------------------ autosectionlabel_prefix_document = True metkit-1.19.2/docs/_static/0000775000175000017500000000000015232567717015662 5ustar alastairalastairmetkit-1.19.2/docs/_static/.keep0000664000175000017500000000000015232567717016575 0ustar alastairalastairmetkit-1.19.2/docs/Doxyfile.in0000664000175000017500000040022515232567717016352 0ustar alastairalastair# Doxyfile 1.13.2 # This file describes the settings to be used by the documentation system # Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). # # Note: # # Use Doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] # Use Doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "Metkit" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @METKIT_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewers a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # With the PROJECT_ICON tag one can specify an icon that is included in the tabs # when the HTML document is shown. Doxygen will copy the logo to the output # directory. PROJECT_ICON = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT_DIR@ # If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format # and will distribute the generated files over these directories. Enabling this # option can be useful when feeding Doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise cause # performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to # control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO # Controls the number of sub-directories that will be created when # CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every # level increment doubles the number of directories, resulting in 4096 # directories at level 8 which is the default and also the maximum value. The # sub-directories are organized in 2 levels, the first level always has a fixed # number of 16 directories. # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. CREATE_SUBDIRS_LEVEL = 8 # If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by Doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English # (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, # Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with # English messages), Korean, Korean-en (Korean with English messages), Latvian, # Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, # Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, # Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which Doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where Doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but # less readable) file names. This can be useful if your file system doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the # first line (until the first dot, question mark or exclamation mark) of a # Javadoc-style comment as the brief description. If set to NO, the Javadoc- # style will behave just like regular Qt-style comments (thus requiring an # explicit @brief command for a brief description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by Doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first # line (until the first dot, question mark or exclamation mark) of a Qt-style # comment as the brief description. If set to NO, the Qt-style will behave just # like regular Qt-style comments (thus requiring an explicit \brief command for # a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and Doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # Doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as Doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". Note that you cannot put \n's in the value part of an alias # to insert newlines (in the resulting output). You can put ^^ in the value part # of an alias to insert a newline as if a physical newline was in the original # file. When you need a literal { or } or , in the value part of an alias you # have to escape them by means of a backslash (\), this can lead to conflicts # with the commands \{ and \} for these it is advised to use the version @{ and # @} or use a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make Doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by Doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by Doxygen, so you can # mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 6. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 6 # The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to # generate identifiers for the Markdown headings. Note: Every identifier is # unique. # Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a # sequence number starting at 0 and GITHUB use the lower case version of title # with any whitespace replaced by '-' and punctuation characters removed. # The default value is: DOXYGEN. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. MARKDOWN_ID_STYLE = DOXYGEN # When enabled Doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. Words listed in the # AUTOLINK_IGNORE_WORDS tag are excluded from automatic linking. # The default value is: YES. AUTOLINK_SUPPORT = YES # This tag specifies a list of words that, when matching the start of a word in # the documentation, will suppress auto links generation, if it is enabled via # AUTOLINK_SUPPORT. This list does not affect affect links explicitly created # using \# or the \link or commands. # This tag requires that the tag AUTOLINK_SUPPORT is set to YES. AUTOLINK_IGNORE_WORDS = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let Doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also makes the inheritance and # collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software) sources only. Doxygen will parse # them like normal C++ but will assume all classes use public instead of private # inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # Doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then Doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small # Doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run Doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use # during processing. When set to 0 Doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 0 # If the TIMESTAMP tag is set different from NO then each generated page will # contain the date or date and time when the page was generated. Setting this to # NO can help when comparing the output of multiple runs. # Possible values are: YES, NO, DATETIME and DATE. # The default value is: NO. TIMESTAMP = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # will also hide undocumented C++ concepts if enabled. This option has no effect # if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_UNDOC_NAMESPACES tag is set to YES, Doxygen will hide all # undocumented namespaces that are normally visible in the namespace hierarchy. # If set to NO, these namespaces will be included in the various overviews. This # option has no effect if EXTRACT_ALL is enabled. # The default value is: YES. HIDE_UNDOC_NAMESPACES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES Doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and macOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # Possible values are: SYSTEM, NO and YES. # The default value is: SYSTEM. CASE_SENSE_NAMES = SYSTEM # If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_HEADERFILE tag is set to YES then the documentation for a class # will show which file needs to be included to use the class. # The default value is: YES. SHOW_HEADERFILE = YES # If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # Doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by Doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by Doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents Doxygen's defaults, run Doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. See also section "Changing the # layout of pages" for information. # # Note that if you run Doxygen from a directory containing a file called # DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = # The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH # environment variable) so that external tools such as latex and gs can be # found. # Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the # path already specified by the PATH variable, and are added in the order # specified. # Note: This option is particularly useful for macOS version 14 (Sonoma) and # higher, when running Doxygen from Doxywizard, because in this case any user- # defined changes to the PATH are ignored. A typical example on macOS is to set # EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin # together with the standard path, the full search path used by doxygen when # launching external tools will then become # PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin EXTERNAL_TOOL_PATH = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by Doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in # a documented function twice, or documenting parameters that don't exist or # using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete # function parameter documentation. If set to NO, Doxygen will accept that some # parameters have no documentation without warning. # The default value is: YES. WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, Doxygen will only warn about wrong parameter # documentation, but not about the absence of documentation. If EXTRACT_ALL is # set to YES then this flag will automatically be disabled. See also # WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO # If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about # undocumented enumeration values. If set to NO, Doxygen will accept # undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: NO. WARN_IF_UNDOC_ENUM_VAL = NO # If WARN_LAYOUT_FILE option is set to YES, Doxygen will warn about issues found # while parsing the user defined layout file, such as missing or wrong elements. # See also LAYOUT_FILE for details. If set to NO, problems with the layout file # will be suppressed. # The default value is: YES. WARN_LAYOUT_FILE = YES # If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the Doxygen process Doxygen will return with a non-zero status. # If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves # like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not # write the warning messages in between other messages but write them at the end # of a run, in case a WARN_LOGFILE is defined the warning messages will be # besides being in the defined file also be shown at the end of a run, unless # the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case # the behavior will remain as with the setting FAIL_ON_WARNINGS. # Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # In the $text part of the WARN_FORMAT command it is possible that a reference # to a more specific place is given. To make it easier to jump to this place # (outside of Doxygen) the user can define a custom "cut" / "paste" string. # Example: # WARN_LINE_FORMAT = "'vi $file +$line'" # See also: WARN_FORMAT # The default value is: at line $line of file $file. WARN_LINE_FORMAT = "at line $line of file $file" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). In case the file specified cannot be opened for writing the # warning and error messages are written to standard error. When as file - is # specified the warning and error messages are written to standard output # (stdout). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @DOXYGEN_INPUT_DIR@ # This tag can be used to specify the character encoding of the source files # that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # This tag can be used to specify the character encoding of the source files # that Doxygen parses. The INPUT_FILE_ENCODING tag can be used to specify # character encoding on a per file pattern basis. Doxygen will compare the file # name with each pattern and apply the encoding instead of the default # INPUT_ENCODING if there is a match. The character encodings are a list of the # form: pattern=encoding (like *.php=ISO-8859-1). # See also: INPUT_ENCODING for further information on supported encodings. INPUT_FILE_ENCODING = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by Doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, # *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, # *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, # *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to # be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cxxm \ *.cpp \ *.cppm \ *.ccm \ *.c++ \ *.c++m \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.idl \ *.ddl \ *.odl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.ixx \ *.l \ *.cs \ *.d \ *.php \ *.php4 \ *.php5 \ *.phtml \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ *.py \ *.pyw \ *.f90 \ *.f95 \ *.f03 \ *.f08 \ *.f18 \ *.f \ *.for \ *.vhd \ *.vhdl \ *.ucf \ *.qsf \ *.ice # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which Doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that Doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that Doxygen will use the data processed and written to standard output # for further processing, therefore nothing else, like debug statements or used # commands (so in case of a Windows batch file always use @echo OFF), should be # written to standard output. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by Doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by Doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the Doxygen output. USE_MDFILE_AS_MAINPAGE = # If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- # directories of the project's root, is used as the documentation for that sub- # directory, except when the README.md starts with a \dir, \page or \mainpage # command. If set to NO, the README.md file needs to start with an explicit \dir # command in order to be used as directory documentation. # The default value is: YES. IMPLICIT_DIR_DOCS = YES # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common # extension is to allow longer lines before the automatic comment starts. The # setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can # be processed before the automatic comment starts. # Minimum value: 7, maximum value: 10000, default value: 72. FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # multi-line macros, enums or list initialized variables directly into the # documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of Doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by Doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) # that should be ignored while generating the index headers. The IGNORE_PREFIX # tag works for classes, function and member names. The entity will be placed in # the alphabetical list under the first letter of the entity name that remains # after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank Doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that Doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that Doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of Doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank Doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank Doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that Doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by Doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). # Note: Since the styling of scrollbars can currently not be overruled in # Webkit/Chromium, the styling will be left out of the default doxygen.css if # one or more extra stylesheets have been specified. So if scrollbar # customization is desired it has to be added explicitly. For an example see the # documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. # Possible values are: LIGHT always generates light mode output, DARK always # generates dark mode output, AUTO_LIGHT automatically sets the mode according # to the user preference, uses light mode if no preference is set (the default), # AUTO_DARK automatically sets the mode according to the user preference, uses # dark mode if no preference is set and TOGGLE allows a user to switch between # light and dark mode via a button. # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE = AUTO_LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be # dynamically folded and expanded in the generated HTML source code. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_CODE_FOLDING = YES # If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in # the top right corner of code and text fragments that allows the user to copy # its content to the clipboard. Note this only works if supported by the browser # and the web page is served via a secure context (see: # https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: # protocol. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COPY_CLIPBOARD = YES # Doxygen stores a couple of settings persistently in the browser (via e.g. # cookies). By default these settings apply to all HTML pages generated by # Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store # the settings under a project specific key, such that the user preferences will # be stored separately. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_PROJECT_COOKIE = # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, Doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag determines the URL of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDURL = # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # on Windows. In the beginning of 2021 Microsoft took the original page, with # a.o. the download links, offline (the HTML help workshop was already many # years in maintenance mode). You can download the HTML help workshop from the # web archives at Installation executable (see: # http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo # ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # Doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # The SITEMAP_URL tag is used to specify the full URL of the place where the # generated documentation will be placed on the server by the user during the # deployment of the documentation. The generated sitemap is called sitemap.xml # and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL # is specified no sitemap is generated. For information about the sitemap # protocol see https://www.sitemaps.org # This tag requires that the tag GENERATE_HTML is set to YES. SITEMAP_URL = # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = YES # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine tune the look of the index (see "Fine-tuning the output"). As an # example, the default style sheet generated by Doxygen has an example that # shows how to put an image at the root of the tree instead of the PROJECT_NAME. # Since the tree basically has the same information as the tab index, you could # consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES # When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the # FULL_SIDEBAR option determines if the side bar is limited to only the treeview # area (value NO) or if it should extend to the full height of the window (value # YES). Setting this to YES gives a layout similar to # https://docs.readthedocs.io with more room for contents, but less room for the # project logo, title, and description. If either GENERATE_TREEVIEW or # DISABLE_INDEX is set to NO, this option has no effect. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. FULL_SIDEBAR = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # Doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # When the SHOW_ENUM_VALUES tag is set doxygen will show the specified # enumeration values besides the enumeration mnemonics. # The default value is: NO. SHOW_ENUM_VALUES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email # addresses. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. OBFUSCATE_EMAILS = YES # If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # Doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # With MATHJAX_VERSION it is possible to specify the MathJax version to be used. # Note that the different versions of MathJax have different requirements with # regards to the different settings, so it is possible that also other MathJax # settings have to be changed when switching between the different MathJax # versions. # Possible values are: MathJax_2 and MathJax_3. # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for # the MathJax output. For more details about the output format see MathJax # version 2 (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 # (see: # http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best # compatibility. This is the name for Mathjax version 2, for MathJax version 3 # this will be translated into chtml), NativeMML (i.e. MathML. Only supported # for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This # is the name for Mathjax version 3, for MathJax version 2 this will be # translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. The default value is: # - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 # - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # for MathJax version 2 (see # https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # For example for MathJax version 3 (see # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): # MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled Doxygen will generate a search box for # the HTML output. The underlying search engine uses JavaScript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the JavaScript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /