Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/ci-config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
dependency_cmake_options: |
ecmwf/eccodes: "-DENABLE_ECKIT_GEO=ON"
dependencies: |
ecmwf/ecbuild
Deutsches-Klimarechenzentrum/libaec@refs/tags/v1.1.6
ecmwf/eccodes
ecmwf/eckit
ecmwf/eccodes
dependency_branch: develop
parallelism_factor: 8
4 changes: 3 additions & 1 deletion .github/ci-hpc-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ build:
- ninja
dependencies:
- ecmwf/ecbuild@develop
- ecmwf/eccodes@develop
- ecmwf/eckit@develop
- ecmwf/eccodes@develop
dependency_cmake_options:
- "ecmwf/eccodes: '-DENABLE_ECKIT_GEO=ON'"
parallel: 64
env:
- ECCODES_SAMPLES_PATH=$ECCODES_DIR/share/eccodes/samples
Expand Down
37 changes: 19 additions & 18 deletions .github/workflows/pymetkit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ jobs:
run: |
mkdir stack-dependencies-build
stack-dependencies-src/build.sh --build-path stack-dependencies-build --install-path dependencies --with-deps libaec,pybind11
- name: Get eckit
uses: actions/checkout@v5
with:
repository: ecmwf/eckit
ref: develop
path: eckit-src
- name: Install eckit
run: |
mkdir eckit-build
cmake \
-B eckit-build \
-S eckit-src \
-GNinja \
-DCMAKE_INSTALL_PREFIX=dependencies \
-DCMAKE_PREFIX_PATH=dependencies \
-DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build eckit-build -j -t install
- name: Get eccodes
uses: actions/checkout@v5
with:
Expand All @@ -59,25 +76,9 @@ jobs:
-DCMAKE_PREFIX_PATH=dependencies \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DENABLE_MEMFS=ON \
-DENABLE_AEC=ON
-DENABLE_AEC=ON \
-DENABLE_ECKIT_GEO=ON
cmake --build eccodes-build -j -t install
- name: Get eckit
uses: actions/checkout@v5
with:
repository: ecmwf/eckit
ref: develop
path: eckit-src
- name: Install eckit
run: |
mkdir eckit-build
cmake \
-B eckit-build \
-S eckit-src \
-GNinja \
-DCMAKE_INSTALL_PREFIX=dependencies \
-DCMAKE_PREFIX_PATH=dependencies \
-DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build eckit-build -j -t install
- name: Archive with permissions preserved
run: tar --zstd -cpf files.tar.zst dependencies/ ecbuild/
- name: Upload dependencies
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ ecbuild_add_option( FEATURE ODB

ecbuild_add_option( FEATURE GRIB2MARS
DEFAULT ON
CONDITION HAVE_GRIB
CONDITION HAVE_GRIB AND eckit_HAVE_ECKIT_GEO AND eccodes_HAVE_ECKIT_GEO
DESCRIPTION "Build the GRIB2MARS converter" )

ecbuild_add_option( FEATURE MARS2MARS
Expand All @@ -84,7 +84,7 @@ ecbuild_add_option( FEATURE MARS2MARS

ecbuild_add_option( FEATURE MARS2GRIB
DEFAULT ON
CONDITION HAVE_GRIB AND eckit_HAVE_ECKIT_GEO
CONDITION HAVE_GRIB AND eckit_HAVE_ECKIT_GEO AND eccodes_HAVE_ECKIT_GEO
DESCRIPTION "Build the MARS2GRIB encoder" )

# Pythonic pybind11 interface (pymetkit)
Expand Down
34 changes: 30 additions & 4 deletions src/metkit/grib2mars/api/Grib2Mars.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include "Grib2Mars.h"

#include <cstdlib>

#include "eckit/config/LocalConfiguration.h"
#include "eckit/exception/Exceptions.h"
#include "metkit/grib2mars/mappings/mappings.h"
Expand All @@ -38,18 +40,42 @@

namespace metkit::grib2mars {

namespace {

/// @brief Apply environment side effects implied by a set of options.
///
/// When `skipSection3` is enabled the encoder delegates geometry handling to
/// gridSpec/ecCodes, which requires ecCodes to be configured with eckit_geo
/// support enabled. This is controlled by the `ECCODES_ECKIT_GEO` environment
/// variable, so force it to "1" whenever `skipSection3` is requested.
inline void applyOptionSideEffects(const Options& opts) {
if (opts.skipSection3) {
::setenv("ECCODES_ECKIT_GEO", "1", 1);
}
}

} // namespace

// -----------------------------------------------------------------------------
// Grib2Mars construction
// -----------------------------------------------------------------------------

/// @brief Default construct a Grib2Mars converter.
Grib2Mars::Grib2Mars() : opts_{} {}
Grib2Mars::Grib2Mars() : opts_{} {
applyOptionSideEffects(opts_);
}

Grib2Mars::Grib2Mars(const Options& opts) : opts_{opts} {}
Grib2Mars::Grib2Mars(const Options& opts) : opts_{opts} {
applyOptionSideEffects(opts_);
}

Grib2Mars::Grib2Mars(const eckit::LocalConfiguration& opts) : opts_{detail::readOptions(opts)} {}
Grib2Mars::Grib2Mars(const eckit::LocalConfiguration& opts) : opts_{detail::readOptions(opts)} {
applyOptionSideEffects(opts_);
}

Grib2Mars::Grib2Mars(OptionList opts) : opts_{detail::readOptions(opts)} {}
Grib2Mars::Grib2Mars(OptionList opts) : opts_{detail::readOptions(opts)} {
applyOptionSideEffects(opts_);
}

/// @brief Convert an `eckit::LocalConfiguration` request.
template <>
Expand Down
19 changes: 17 additions & 2 deletions src/metkit/grib2mars/mappings/rules/grid.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,27 @@ namespace metkit::grib2mars::rules::impl {
template <class MarsDict, class MiscDict, class OptDict_t>
void extractGrid(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars, MiscDict& misc,
const OptDict_t& opts) {
using metkit::grib2mars::utils::dict_traits::get_or_throw;
using metkit::grib2mars::utils::dict_traits::set_or_throw;
using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException;

try {
(void)opts;
(void)misc;
if (get_or_throw<bool>(opts, "skipSection3")) {
if (!grib.has("gridSpec")) {
throw Grib2MarsGenericException("Missing GRIB key `gridSpec` required to extract MARS keyword `grid`",
Here());
}

const auto gridSpec = grib.getString("gridSpec");
set_or_throw<std::string>(mars, "grid", gridSpec);

if (grib.has("shapeOfTheEarth")) {
const long shapeOfTheEarth = grib.getLong("shapeOfTheEarth");
misc.set("shapeOfTheEarth", shapeOfTheEarth);
}

return;
}

if (!grib.has("gridType")) {
throw Grib2MarsGenericException(
Expand Down
41 changes: 40 additions & 1 deletion src/metkit/grib2mars/mappings/rules/truncation.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,50 @@ namespace metkit::grib2mars::rules::impl {
template <class MarsDict, class MiscDict, class OptDict_t>
void extractTruncation(const std::string& keyword, const metkit::codes::CodesHandle& grib, MarsDict& mars,
MiscDict& misc, const OptDict_t& opts) {
using metkit::grib2mars::utils::dict_traits::get_or_throw;
using metkit::grib2mars::utils::dict_traits::set_or_throw;
using metkit::grib2mars::utils::exceptions::Grib2MarsGenericException;

try {
(void)opts;
if (get_or_throw<bool>(opts, "skipSection3")) {
if (!grib.has("gridSpec")) {
throw Grib2MarsGenericException("Missing GRIB key `gridSpec` required to extract MARS keyword `grid`",
Here());
}

const auto gridSpec = grib.getString("gridSpec");
set_or_throw<std::string>(mars, "grid", gridSpec);

const long j = grib.getLong("J");
const long k = grib.getLong("K");
const long m = grib.getLong("M");

if (j != k || j != m) {
throw Grib2MarsGenericException("Grib keys `J/K/M` must be equal! J=" + std::to_string(j) +
", K=" + std::to_string(k) + ", M=" + std::to_string(m),
Here());
}

if (grib.has("JS")) {
const long js = grib.getLong("JS");
const long ks = grib.getLong("KS");
const long ms = grib.getLong("MS");

if (js != ks || js != ms) {
throw Grib2MarsGenericException("Grib keys `JS/KS/MS` must be equal! JS=" + std::to_string(js) +
", KS=" + std::to_string(ks) + ", MS=" + std::to_string(ms),
Here());
}

if (js > 0 && js <= j) {
// Set subSetTruncation based on JS, which is equal to KS and MS
set_or_throw<long>(misc, "subSetTruncation", js);
}
}

return;
}

if (!grib.has("gridType")) {
throw Grib2MarsGenericException(
"Missing GRIB key `gridType` required to extract MARS keyword `" + keyword + "`", Here());
Expand Down
34 changes: 30 additions & 4 deletions src/metkit/mars2grib/api/Mars2Grib.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
///
#include "Mars2Grib.h"

#include <cstdlib>

// other libraries
#include "eckit/exception/Exceptions.h"

Expand Down Expand Up @@ -397,13 +399,37 @@ std::unique_ptr<metkit::codes::CodesHandle> Mars2Grib::finaliseEncoding(const Ca
// Mars2Grib construction
// -----------------------------------------------------------------------------

Mars2Grib::Mars2Grib() : opts_{} {}
namespace {

/// @brief Apply environment side effects implied by a set of options.
///
/// When `skipSection3` is enabled the encoder delegates geometry handling to
/// gridSpec/ecCodes, which requires ecCodes to be configured with eckit_geo
/// support enabled. This is controlled by the `ECCODES_ECKIT_GEO` environment
/// variable, so force it to "1" whenever `skipSection3` is requested.
inline void applyOptionSideEffects(const Options& opts) {
if (opts.skipSection3) {
::setenv("ECCODES_ECKIT_GEO", "1", 1);
}
}

} // namespace

Mars2Grib::Mars2Grib(const Options& opts) : opts_{opts} {}
Mars2Grib::Mars2Grib() : opts_{} {
applyOptionSideEffects(opts_);
}

Mars2Grib::Mars2Grib(const eckit::LocalConfiguration& opts) : opts_{detail::readOptions(opts)} {}
Mars2Grib::Mars2Grib(const Options& opts) : opts_{opts} {
applyOptionSideEffects(opts_);
}

Mars2Grib::Mars2Grib(OptionList opts) : opts_{detail::readOptions(opts)} {}
Mars2Grib::Mars2Grib(const eckit::LocalConfiguration& opts) : opts_{detail::readOptions(opts)} {
applyOptionSideEffects(opts_);
}

Mars2Grib::Mars2Grib(OptionList opts) : opts_{detail::readOptions(opts)} {
applyOptionSideEffects(opts_);
}


// -----------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ template <std::size_t Stage, std::size_t Section, RepresentationType Variant>
constexpr bool representationApplicable() {

// Disable the concept for the Dummy variant
if constexpr (Variant == RepresentationType::Dummy) {
if constexpr (Variant == RepresentationType::Dummy || Variant == RepresentationType::DummySH) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ enum class RepresentationType : std::size_t {
Healpix,
Orca,
Fesom,
Dummy, // Used to skip section3 resolution and delegate to gridSpec
Dummy, // Used to skip section3 resolution and delegate to gridSpec
DummySH, // Special case for spherical harmonics
Default
};

Expand All @@ -114,7 +115,7 @@ using RepresentationList =
ValueList<RepresentationType::Latlon, RepresentationType::RegularGaussian, RepresentationType::ReducedGaussian,
RepresentationType::SphericalHarmonics, RepresentationType::GeneralUnstructured,
RepresentationType::Healpix, RepresentationType::Orca, RepresentationType::Fesom,
RepresentationType::Dummy, RepresentationType::Default>;
RepresentationType::Dummy, RepresentationType::DummySH, RepresentationType::Default>;


///
Expand Down Expand Up @@ -153,6 +154,7 @@ DEF(RepresentationType::Healpix, "healpix");
DEF(RepresentationType::Orca, "orca");
DEF(RepresentationType::Fesom, "fesom");
DEF(RepresentationType::Dummy, "dummy");
DEF(RepresentationType::DummySH, "dummySH");
DEF(RepresentationType::Default, "default");

#undef DEF
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ std::size_t representationMatcher(const MarsDict_t& mars, const OptDict_t& opt)

// This is used to fully delegate section3 setting to gridSpec
if (get_or_throw<bool>(opt, "skipSection3")) {
const auto marsGrid = get_or_throw<std::string>(mars, "grid");
const auto gridType =
std::unique_ptr<const eckit::geo::Grid>(eckit::geo::GridFactory::make_from_string(marsGrid))->type();

if (gridType == "sh") {
return static_cast<std::size_t>(RepresentationType::DummySH);
}
return static_cast<std::size_t>(RepresentationType::Dummy);
}

Expand Down
15 changes: 5 additions & 10 deletions src/metkit/mars2grib/backend/deductions/subSetTrunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@

// System includes
#include <algorithm>
#include <regex>
#include <string>

#include "eckit/geo/Grid.h"

// Core deduction includes
#include "metkit/config/LibMetkit.h"
#include "metkit/mars2grib/utils/generalUtils.h"
Expand Down Expand Up @@ -92,8 +93,6 @@ long resolve_SubSetTruncation_or_throw(const MarsDict_t& mars, const ParDict_t&
using metkit::mars2grib::utils::dict_traits::has;
using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException;

static const std::regex tGridRegex("^T(0|[1-9][0-9]*)$");

try {

// subSetTruncation must not be larger than any pentagonalResolutionParameter
Expand All @@ -102,13 +101,9 @@ long resolve_SubSetTruncation_or_throw(const MarsDict_t& mars, const ParDict_t&

const long truncation = [&]() {
if (get_or_throw<bool>(opt, "skipSection3")) {
const std::string grid = get_or_throw<std::string>(mars, "grid");
std::smatch tGridMatch;
if (!std::regex_match(grid, tGridMatch, tGridRegex) || tGridMatch.size() != 2) {
throw Mars2GribDeductionException("Cannot extract truncation from MARS grid '" + grid + "'",
Here());
}
return std::stol(tGridMatch[1].str());
const std::string grid = get_or_throw<std::string>(mars, "grid");
const eckit::geo::Grid* gridSpec = eckit::geo::GridFactory::make_from_string(grid);
return static_cast<long>(gridSpec->truncation());
}
return get_or_throw<long>(mars, "truncation");
}();
Expand Down
Loading
Loading