diff options
-rw-r--r-- | c++/codec-netcdf/.nix/derivation.nix | 33 | ||||
-rw-r--r-- | c++/codec-netcdf/SConscript | 38 | ||||
-rw-r--r-- | c++/codec-netcdf/SConstruct | 66 | ||||
-rw-r--r-- | c++/codec-netcdf/netcdf.h | 93 | ||||
-rw-r--r-- | c++/codec-netcdf/netcdf.tmpl.h | 106 | ||||
-rw-r--r-- | default.nix | 7 | ||||
-rw-r--r-- | tests/.nix/derivation.nix | 1 | ||||
-rw-r--r-- | tests/codec-netcdf.cpp | 25 | ||||
-rw-r--r-- | tests/data/simple.nc | bin | 0 -> 68 bytes |
9 files changed, 369 insertions, 0 deletions
diff --git a/c++/codec-netcdf/.nix/derivation.nix b/c++/codec-netcdf/.nix/derivation.nix new file mode 100644 index 0000000..770942e --- /dev/null +++ b/c++/codec-netcdf/.nix/derivation.nix @@ -0,0 +1,33 @@ +{ lib +, stdenv +, scons +, clang +, clang-tools +, version +, forstio +, netcdf +}: + +let + +in stdenv.mkDerivation { + pname = "forstio-codec-netcdf"; + inherit version; + src = ./..; + + enableParallelBuilding = true; + + nativeBuildInputs = [ + scons + clang-tools + ]; + + buildInputs = [ + forstio.core + forstio.async + forstio.codec + netcdf + ]; + + outputs = ["out" "dev"]; +} diff --git a/c++/codec-netcdf/SConscript b/c++/codec-netcdf/SConscript new file mode 100644 index 0000000..a469f77 --- /dev/null +++ b/c++/codec-netcdf/SConscript @@ -0,0 +1,38 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +codec_netcdf_env = env.Clone(); + +codec_netcdf_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +codec_netcdf_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += codec_netcdf_env.sources; +env.headers += codec_netcdf_env.headers; + +## Shared lib +objects_shared = [] +codec_netcdf_env.add_source_files(objects_shared, codec_netcdf_env.sources, shared=True); +codec_netcdf_env.library_shared = codec_netcdf_env.SharedLibrary('#build/forstio-codec-netcdf', [objects_shared]); + +## Static lib +objects_static = [] +codec_netcdf_env.add_source_files(objects_static, codec_netcdf_env.sources, shared=False); +codec_netcdf_env.library_static = codec_netcdf_env.StaticLibrary('#build/forstio-codec-netcdf', [objects_static]); + +# Set Alias +env.Alias('library_codec_netcdf', [codec_netcdf_env.library_shared, codec_netcdf_env.library_static]); + +env.targets += ['library_codec_netcdf']; + +# Install +env.Install('$prefix/lib/', [codec_netcdf_env.library_shared, codec_netcdf_env.library_static]); +env.Install('$prefix/include/forstio/codec/netcdf/', [codec_netcdf_env.headers]); diff --git a/c++/codec-netcdf/SConstruct b/c++/codec-netcdf/SConstruct new file mode 100644 index 0000000..edd5f57 --- /dev/null +++ b/c++/codec-netcdf/SConstruct @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=['forstio-codec']) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/c++/codec-netcdf/netcdf.h b/c++/codec-netcdf/netcdf.h new file mode 100644 index 0000000..d579b7f --- /dev/null +++ b/c++/codec-netcdf/netcdf.h @@ -0,0 +1,93 @@ +#pragma once + +#include <forstio/core/string_literal.h> +#include <forstio/core/error.h> +#include <forstio/codec/codec.h> + +#include <netcdf.h> + +namespace saw { +namespace encode { +/** + * Template type hint + */ +struct Netcdf {}; +} + +/** + * Class representing the files system netcdf file + */ +template<typename Schema> +class data<Schema, encode::Netcdf> { +private: + std::string path_; +public: + data(const std::string& path): + path_{path} + {} + + data(std::string&& path): + path_{std::move(path)} + {} + + std::string_view get_path_view() { + return path_; + } + + const std::string& get_path() const { + return path_; + } +}; + +template<typename Schema> +class codec<Schema, encode::Netcdf>{ + static_assert(always_false<Schema,encode::Netcdf>, "NetCDF only supports Structs as a root object"); +}; + +} +#include "netcdf.tmpl.h" +namespace saw { + +template<typename... Vals, string_literal... Keys> +class codec<schema::Struct<schema::Member<Vals,Keys>...>, encode::Netcdf> { +private: + using Schema = schema::Struct<schema::Member<Vals,Keys>...>; +public: + SAW_FORBID_COPY(codec); + SAW_DEFAULT_MOVE(codec); + + /** + * Default constructor + */ + codec() = default; + + /** + * Encoder function + */ + template<typename FromEncoding> + error_or<void> encode() { + return void_t{}; + } + + /** + * Decoder function + */ + template<typename ToEncoding> + error_or<void> decode(data<Schema, encode::Netcdf>& from_decode, data<Schema,ToEncoding>& to_decode) { + int ncid{}; + int rc{}; + + rc = nc_open(from_decode.get_path().c_str(), NC_NOWRITE, &ncid); + if(rc){ + // Don't know how to get the error, so fail critically. + return make_error<err::critical>(); + } + + auto eov = impl::netcdf_decode<Schema, ToEncoding>::decode(to_decode, ncid); + + nc_close(ncid); + + return eov; + } +}; +} diff --git a/c++/codec-netcdf/netcdf.tmpl.h b/c++/codec-netcdf/netcdf.tmpl.h new file mode 100644 index 0000000..921c84e --- /dev/null +++ b/c++/codec-netcdf/netcdf.tmpl.h @@ -0,0 +1,106 @@ +#pragma once + +namespace saw { +namespace impl { +template<typename Schema> +struct netcdf_is_group { + static constexpr bool value = false; +}; + +template<typename... T, string_literal... Lits> +struct netcdf_is_group<schema::Struct<schema::Member<T,Lits>...>> { + static constexpr bool value = true; +}; + +template<typename Schema, typename RootSchema, typename ToEncode> +struct netcdf_decode; + +template<typename RootSchema, typename ToDecode> +struct netcdf_decode<schema::Int32, RootSchema, ToDecode> { + using Schema = schema::Int32; + static error_or<void> decode(data<Schema, ToDecode>& to, int from, int nc_varid){ + int rc{}; + + nc_type nc_vartype{}; + int nc_dimnum{}; + std::array<int,NC_MAX_VAR_DIMS> nc_dimids; + + rc = nc_inq_var(from, nc_varid, nullptr, &nc_vartype, &nc_dimnum, &nc_dimids[0], nullptr); + if(rc != NC_NOERR){ + return make_error<err::critical>(); + } + if(nc_vartype != NC_INT){ + return make_error<err::critical>(); + } + if(nc_dimnum != 0){ + return make_error<err::critical>(); + } + + int32_t val{}; + rc = nc_get_var_int(from, nc_varid, &val); + if(rc != NC_NOERR){ + return make_error<err::critical>(); + } + + to.set(val); + + return void_t {}; + } +}; + +template<typename... T, string_literal... Lits, typename RootSchema, typename ToDecode> +struct netcdf_decode<schema::Struct<schema::Member<T,Lits>...>, RootSchema, ToDecode> { + using Schema = schema::Struct<schema::Member<T,Lits>...>; + + template<std::size_t i> + static error_or<void> decode_member(data<Schema,ToDecode>& to, int from){ + using Type = typename parameter_pack_type<i,T...>::type; + constexpr string_literal Literal = parameter_key_pack_type<i, Lits...>::literal; + { + /** + * We have to ask for the internal id of the netcdf structure + */ + if constexpr (netcdf_is_group<Type>::value){ + int nc_group_id{}; + int rc {}; + rc = nc_inq_ncid(from, Literal.data.data(), &nc_group_id); + if(rc != NC_NOERR) { + return make_error<err::critical>(); + } + auto eov = netcdf_decode<Type, RootSchema, ToDecode>::decode(to.template get<Literal>(), nc_group_id); + if(eov.is_error()){ + return eov; + } + }else{ + int nc_varid{}; + int rc {}; + rc = nc_inq_varid(from, Literal.data.data(), &nc_varid); + if(rc != NC_NOERR) { + return make_error<err::critical>(); + } + auto eov = netcdf_decode<Type, RootSchema, ToDecode>::decode(to.template get<Literal>(), from, nc_varid); + } + + } + if constexpr ((i+1) < sizeof...(T)){ + auto eov = decode_member<i+1>(from, to, ncid); + if(eov.is_error()){ + return eov; + } + } + + return void_t{}; + } + + + static error_or<void> decode(data<Schema, ToDecode>& to, int from){ + if constexpr (sizeof...(T) > 0){ + auto eov = decode_member<0>(to, from); + return eov; + } + + return void_t{}; + } +}; +} +} diff --git a/default.nix b/default.nix index 397cf74..16e7c73 100644 --- a/default.nix +++ b/default.nix @@ -40,6 +40,13 @@ in rec { inherit stdenv; inherit clang-tools; }; + + codec-netcdf = pkgs.callPackage c++/codec-netcdf/.nix/derivation.nix { + inherit version; + inherit forstio; + inherit stdenv; + inherit clang-tools; + }; io = pkgs.callPackage c++/io/.nix/derivation.nix { inherit version; diff --git a/tests/.nix/derivation.nix b/tests/.nix/derivation.nix index 198095e..e096e70 100644 --- a/tests/.nix/derivation.nix +++ b/tests/.nix/derivation.nix @@ -19,6 +19,7 @@ stdenv.mkDerivation { forstio.async forstio.codec forstio.codec-json + forstio.codec-netcdf forstio.core forstio.io forstio.test diff --git a/tests/codec-netcdf.cpp b/tests/codec-netcdf.cpp new file mode 100644 index 0000000..df0037b --- /dev/null +++ b/tests/codec-netcdf.cpp @@ -0,0 +1,25 @@ +#include <forstio/test/suite.h> +#include <forstio/codec/netcdf/netcdf.h> + +namespace { +namespace schema { +using namespace saw::schema; +using TestStruct = Struct< + Member<Int32, "rh"> +>; +} + +SAW_TEST("NetCDF read"){ + using namespace saw; + + data<TestStruct, encode::Netcdf> net{"./data/simple.nc"}; + + data<TestStruct, encode::KelSimple> kel; + + codec<TestStruct, encode::Netcdf> codec; + + auto eov = codec.decode(net, kel); + SAW_EXPECT(eov.is_value(), "Decoding failed"); + SAW_EXPECT(kel.get<"rh">.get() == 5, "Value incorrect"); +} +} diff --git a/tests/data/simple.nc b/tests/data/simple.nc Binary files differnew file mode 100644 index 0000000..4fc2473 --- /dev/null +++ b/tests/data/simple.nc |