htcp

Properties of water suspended in clouds
git clone git://git.meso-star.fr/htcp.git
Log | Files | Refs | README | LICENSE

commit d91d6dce28e4fcea7d68deebdcc45010522a8390
parent 00f2bbf79ad5aaa0609843879bfd7131f458926a
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 27 Apr 2018 11:11:20 +0200

Transform the test_nc test into an utility

Rename the program in les2htgop. Define a specific CMake for this
program. Add the -v option that prints its version.

Diffstat:
Mcmake/CMakeLists.txt | 24++++++++++++++----------
Acmake/les2htcop/CMakeLists.txt | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/les2htcop.c | 725+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/les2htcop.h.in | 24++++++++++++++++++++++++
Dsrc/test_nc.c | 709-------------------------------------------------------------------------------
5 files changed, 841 insertions(+), 719 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -20,6 +20,8 @@ enable_testing() set(HTNC_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) option(NO_TEST "Do not build tests" OFF) +add_subdirectory(les2htcop) + ################################################################################ # Check dependencies ################################################################################ @@ -55,15 +57,16 @@ rcmake_prepend_path(HTNC_FILES_INC ${HTNC_SOURCE_DIR}) rcmake_prepend_path(HTNC_FILES_INC_API ${HTNC_SOURCE_DIR}) rcmake_prepend_path(HTNC_FILES_DOC ${PROJECT_SOURCE_DIR}/../) -add_library(htnc SHARED ${HTNC_FILES_SRC} ${HTNC_FILES_INC} ${HTNC_FILES_INC_API}) -target_link_libraries(htnc RSys ${NETCDF_C_LIBRARIES}) - -set_target_properties(htnc PROPERTIES - DEFINE_SYMBOL HTNC_SHARED_BUILD - VERSION ${VERSION} - SOVERSION ${VERSION_MAJOR}) - -rcmake_setup_devel(htnc HTNC ${VERSION} high_tune/htnc.h) +#add_library(htnc SHARED ${HTNC_FILES_SRC} ${HTNC_FILES_INC} ${HTNC_FILES_INC_API}) +#target_link_libraries(htnc RSys ${NETCDF_C_LIBRARIES}) +# +#set_target_properties(htnc PROPERTIES +# DEFINE_SYMBOL HTNC_SHARED_BUILD +# VERSION ${VERSION} +# SOVERSION ${VERSION_MAJOR}) +# +#rcmake_setup_devel(htnc HTNC ${VERSION} high_tune/htnc.h) +# ################################################################################ # Add tests @@ -80,12 +83,13 @@ if(NOT NO_TEST) add_test(${_name} ${_name}) endfunction() - new_test(test_nc) endif() + ################################################################################ # Define output & install directories ################################################################################ + #install(TARGETS htgop # ARCHIVE DESTINATION bin # LIBRARY DESTINATION lib diff --git a/cmake/les2htcop/CMakeLists.txt b/cmake/les2htcop/CMakeLists.txt @@ -0,0 +1,78 @@ +# Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cmake_minimum_required(VERSION 2.8) +project(les2htcop C) +enable_testing() + +set(LES2HTCOP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../../src) +option(NO_TEST "Do not build tests" OFF) + +################################################################################ +# Check dependencies +################################################################################ +get_filename_component(_current_source_dir ${CMAKE_CURRENT_LIST_FILE} PATH) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_current_source_dir}/../) + +find_package(RCMake 0.3 REQUIRED) +find_package(RSys 0.6 REQUIRED) +find_package(NetCDF REQUIRED) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) +include(rcmake) +include(rcmake_runtime) + +include_directories( + ${RSys_INCLUDE_DIR} + ${NETCDF_C_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}) + +################################################################################ +# Generate files +################################################################################ +set(VERSION_MAJOR 0) +set(VERSION_MINOR 0) +set(VERSION_PATCH 0) +set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +configure_file(${LES2HTCOP_SOURCE_DIR}/les2htcop.h.in + ${CMAKE_CURRENT_BINARY_DIR}/les2htcop.h @ONLY) + +################################################################################ +# Configure and define targets +################################################################################ +set(LES2HTCOP_FILES_SRC les2htcop.c) +set(LES2HTCOP_FILES_INC les2htcop.h.in) +set(LES2HTCOP_FILES_DOC COPYING) + +# Prepend each file in the `HTNC_FILES_<SRC|INC>' list by `LES2HTCOP_SOURCE_DIR' +rcmake_prepend_path(LES2HTCOP_FILES_SRC ${LES2HTCOP_SOURCE_DIR}) +rcmake_prepend_path(LES2HTCOP_FILES_INC ${LES2HTCOP_SOURCE_DIR}) +rcmake_prepend_path(LES2HTCOP_FILES_DOC ${PROJECT_SOURCE_DIR}/../) + +add_executable(les2htcop ${LES2HTCOP_FILES_SRC}) +target_link_libraries(les2htcop RSys ${NETCDF_C_LIBRARIES}) + +set_target_properties(les2htcop PROPERTIES + DEFINE_SYMBOL LES2HTCOP_SHARED_BUILD + VERSION ${VERSION} + SOVERSION ${VERSION_MAJOR}) + +################################################################################ +# Define output & install directories +################################################################################ +install(TARGETS les2htcop RUNTIME DESTINATION bin) +install(FILES ${LES2HTCOP_FILES_DOC} DESTINATION share/doc/les2htcop) + diff --git a/src/les2htcop.c b/src/les2htcop.c @@ -0,0 +1,725 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* getopt/close support */ + +#include "les2htcop.h" + +#include <rsys/rsys.h> +#include <rsys/math.h> +#include <rsys/mem_allocator.h> + +#include <alloca.h> +#include <netcdf.h> +#include <string.h> +#include <unistd.h> /* getopt */ +#include <fcntl.h> /* open */ +#include <sys/stat.h> /* S_IRUSR & S_IWUSR */ + +/******************************************************************************* + * Command arguments + ******************************************************************************/ +struct args { + const char* output; + const char* input; + int force_overwrite; + int check; + int no_output; + int quit; /* Quit the application */ +}; +#define ARGS_DEFAULT__ {NULL,NULL,0,0,0,0} +static const struct args ARGS_DEFAULT = ARGS_DEFAULT__; + +static void +print_help(const char* cmd) +{ + ASSERT(cmd); + + printf( +"Usage: %s -i INPUT [OPTIONS]\n" +"Convert the LES data stored into INPUT from NetCDF to the htcop fileformat.\n\n", + cmd); + printf( +" -c advanced check of the validity of the submitted LES file\n" +" with respect to the converter pre-requisites. Note that\n" +" this option can increase significantly the conversion\n" +" time.\n"); + printf( +" -f overwrite the OUTPUT file if it already exists.\n"); + printf( +" -h display this help and exit.\n"); + printf( +" -i INPUT path of the LES file to convert.\n"); + printf( +" -o OUTPUT write results to OUTPUT. If not defined, write results to\n" +" standard output.\n"); + printf( +" -q do not write results to OUTPUT.\n"); + printf( +" -v display version information and exit.\n"); + printf("\n"); + printf( +"%s (C) 2018 |Meso|Star>. This is free software released under the GNU GPL\n" +"license, version 3 or later. You are free to change or redistribute it under\n" +"certain conditions <http://gnu.org/licenses/gpl.html>.\n", cmd); +} + +static void +args_release(struct args* args) +{ + ASSERT(args); + *args = ARGS_DEFAULT; +} + +static res_T +args_init(struct args* args, const int argc, char** argv) +{ + int opt; + res_T res = RES_OK; + ASSERT(args && argc && argv); + + while((opt = getopt(argc, argv, "cfhi:o:qv")) != -1) { + switch(opt) { + case 'c': args->check = 1; break; + case 'f': args->force_overwrite = 1; break; + case 'h': + print_help(argv[0]); + args_release(args); + args->quit = 1; + goto exit; + case 'i': args->input = optarg; break; + case 'o': args->output = optarg; break; + case 'q': args->no_output = 1; break; + case 'v': + printf("%s %d.%d.%d\n", + argv[0], + LES2HTLES_VERSION_MAJOR, + LES2HTLES_VERSION_MINOR, + LES2HTLES_VERSION_PATCH); + args->quit = 1; + goto exit; + default: RES_BAD_ARG; break; + } + if(res != RES_OK) { + if(optarg) { + fprintf(stderr, "%s: invalid option argumet '%s' -- '%c'\n", + argv[0], optarg, opt); + } + goto error; + } + } + + if(!args->input) { + fprintf(stderr, "Missing input file.\n"); + res = RES_BAD_ARG; + goto error; + } + + if(args->no_output) args->output = NULL; + +exit: + return res; +error: + args_release(args); + goto exit; +} + +/******************************************************************************* + * Grid header + ******************************************************************************/ +struct grid { + int32_t nx, ny, nz, ntimes; /* Definition */ + int8_t is_z_irregular; + double lower[3]; /* Lower position of the grid */ + double vxsz_x; /* Size of the voxel is X */ + double vxsz_y; /* Size of the voxel in Y */ + double* vxsz_z; /* Size of the voxel in Z */ +}; + +#define GRID_NULL__ {0,0,0,0,0,{0,0,0},0,0,NULL} +static const struct grid GRID_NULL = GRID_NULL__; + +static void +grid_release(struct grid* grid) +{ + CHK(grid); + if(grid->vxsz_z) mem_rm(grid->vxsz_z); +} + +/******************************************************************************* + * NetCDF helper functions and macros + ******************************************************************************/ +#define INVALID_ID -1 + +#define NC(Func) { \ + const int err__ = nc_ ## Func; \ + if(err__ != NC_NOERR) { \ + fprintf(stderr, "error:%i:%s\n", __LINE__, ncerr_to_str(err__)); \ + abort(); \ + } \ + } (void)0 + +static INLINE const char* +ncerr_to_str(const int ncerr) +{ + const char* str = "NC_ERR_<UNKNOWN>"; + switch(ncerr) { + case NC_EBADGRPID: str = "NC_EBADGRPID"; break; + case NC_EBADID: str = "NC_EBADID"; break; + case NC_EBADNAME: str = "NC_EBADNAME"; break; + case NC_ECHAR: str = "NC_ECHAR"; break; + case NC_EDIMMETA: str = "NC_EDIMMETA"; break; + case NC_EHDFERR: str = "NC_EHDFERR"; break; + case NC_ENOMEM: str = "NC_ENOMEM"; break; + case NC_ENOTATT: str = "NC_ENOTATT"; break; + case NC_ENOTVAR: str = "NC_ENOTVAR"; break; + case NC_ERANGE: str = "NC_ERANGE"; break; + case NC_NOERR: str = "NC_NOERR"; break; + } + return str; +} + +static INLINE const char* +nctype_to_str(const nc_type type) +{ + const char* str = "NC_TYPE_<UNKNOWN>"; + switch(type) { + case NC_NAT: str = "NC_NAT"; break; + case NC_BYTE: str = "NC_BYTE"; break; + case NC_CHAR: str = "NC_CHAR"; break; + case NC_SHORT: str = "NC_SHORT"; break; + case NC_LONG: str = "NC_LONG"; break; + case NC_FLOAT: str = "NC_FLOAT"; break; + case NC_DOUBLE: str = "NC_DOUBLE"; break; + case NC_UBYTE: str = "NC_UBYTE"; break; + case NC_USHORT: str = "NC_USHORT"; break; + case NC_UINT: str = "NC_UINT"; break; + case NC_INT64: str = "NC_INT64"; break; + case NC_UINT64: str = "NC_UINT64"; break; + case NC_STRING: str = "NC_STRING"; break; + default: FATAL("Unreachable code.\n"); break; + } + return str; +} + +static INLINE size_t +sizeof_nctype(const nc_type type) +{ + size_t sz; + switch(type) { + case NC_BYTE: + case NC_CHAR: + case NC_UBYTE: + sz = 1; break; + case NC_SHORT: + case NC_USHORT: + sz = 2; break; + case NC_FLOAT: + case NC_INT: + case NC_UINT: + sz = 4; break; + case NC_DOUBLE: + case NC_INT64: + case NC_UINT64: + sz = 8; break; + default: FATAL("Unreachable cde.\n"); break; + } + return sz; +} + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +open_output_stream(const char* path, const int force_overwrite, FILE** stream) +{ + int fd = -1; + FILE* fp = NULL; + res_T res = RES_OK; + ASSERT(path); + + if(force_overwrite) { + fp = fopen(path, "w"); + if(!fp) { + fprintf(stderr, "Could not open the output file '%s'.\n", path); + goto error; + } + } else { + fd = open(path, O_CREAT|O_WRONLY|O_EXCL|O_TRUNC, S_IRUSR|S_IWUSR); + if(fd >= 0) { + fp = fdopen(fd, "w"); + if(fp == NULL) { + fprintf(stderr, "Could not open the output file '%s'.\n", path); + goto error; + } + } else if(errno == EEXIST) { + fprintf(stderr, + "The output file '%s' already exists. Use -f to overwrite it.\n", path); + goto error; + } else { + fprintf(stderr, + "Unexpected error while opening the output file `'%s'.\n", path); + goto error; + } + } + +exit: + *stream = fp; + return res; +error: + res = RES_IO_ERR; + if(fp) { + CHK(fclose(fp) == 0); + fp = NULL; + } else if(fd >= 0) { + CHK(close(fd) == 0); + } + goto exit; +} + + +static res_T +setup_definition + (int nc, int32_t* nx, int32_t* ny, int32_t* nz, int32_t* ntimes) +{ + size_t len[4]; + int dimids[4]; + int id; + int ndims; + int err = NC_NOERR; + res_T res = RES_OK; + ASSERT(nx && ny && nz && ntimes); + + err = nc_inq_varid(nc, "RCT", &id); + if(err != NC_NOERR) { + fprintf(stderr, "Could not inquire the 'RCT' variable -- %s\n", + ncerr_to_str(err)); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_varndims(nc, id, &ndims)); + if(ndims != 4) { + fprintf(stderr, "The dimension of the 'RCT' variable must be 4.\n"); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_vardimid(nc, id, dimids)); + NC(inq_dimlen(nc, dimids[0], len+0)); + NC(inq_dimlen(nc, dimids[1], len+1)); + NC(inq_dimlen(nc, dimids[2], len+2)); + NC(inq_dimlen(nc, dimids[3], len+3)); + + *nx = (int32_t)len[3]; + *ny = (int32_t)len[2]; + *nz = (int32_t)len[1]; + *ntimes = (int32_t)len[0]; + + if(*nx < 1 || *ny < 1 || *nz < 1 || *ntimes < 1) { + fprintf(stderr, + "The spatial and time definitions cannot be null.\n" + " #x = %i; #y = %i; #z = %i; #time = %i\n", + *nx, *ny, *nz, *ntimes); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +static res_T +setup_regular_dimension + (const int nc, + const char* var_name, + double* voxel_size, + double* lower_pos, + const int check) +{ + char* mem = NULL; + size_t len, start, i; + double vxsz; + nc_type type; + int varid, dimid, ndims; + int err = NC_NOERR; + res_T res = RES_OK; + ASSERT(nc && var_name && voxel_size && lower_pos); + + err = nc_inq_varid(nc, var_name, &varid); + if(err != NC_NOERR) { + fprintf(stderr, "Could not inquire the '%s' variable -- %s\n", + var_name, ncerr_to_str(err)); + res = RES_BAD_ARG; + goto error; + } + NC(inq_varndims(nc, varid, &ndims)); + if(ndims != 1) { + fprintf(stderr, "The dimension of the '%s' variable must be 1 .\n", var_name); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_vardimid(nc, varid, &dimid)); + NC(inq_dimlen(nc, dimid, &len)); + ASSERT(len > 0); + + NC(inq_vartype(nc, varid, &type)); + if(type != NC_DOUBLE && type != NC_FLOAT) { + fprintf(stderr, "The type of the '%s' variable must be float or double.\n", + var_name); + res = RES_BAD_ARG; + goto error; + } + + #define AT(Mem, Id) (type == NC_FLOAT ? ((float*)Mem)[Id] : ((double*)Mem)[Id]) + if(!check) { + char* raw = alloca(8); + start = 0; + len = 1; + NC(get_vara(nc, varid, &start, &len, raw)); + vxsz = AT(raw, 0) * 2; /* Assume that the grid starts at 0 */ + } else { + mem = mem_alloc(len*sizeof_nctype(type)); + if(!mem) { + fprintf(stderr, "Could not allocate memory for the '%s' variable.\n", var_name); + res = RES_MEM_ERR; + goto error; + } + start = 0; + NC(get_vara(nc, varid, &start, &len, mem)); + vxsz = AT(mem, 0) * 2; /* Assume that the grid starts from 0 */ + } + + if(vxsz <= 0) { + fprintf(stderr, "The '%s' variable can't have negative or null values.\n", + var_name); + res = RES_BAD_ARG; + goto error; + } + + if(check) { + /* Check that the dimension is regular */ + FOR_EACH(i, 1, len) { + if(!eq_eps(AT(mem, i) - AT(mem, i-1), vxsz, 1.e-6)) { + fprintf(stderr, "The voxel size of the '%s' variable must be regular.\n", + var_name); + res = RES_BAD_ARG; + goto error; + } + } + } + #undef AT + + *voxel_size = vxsz; + *lower_pos = 0; +exit: + if(mem) mem_rm(mem); + return res; +error: + goto exit; +} + +/* Return 1 if Z is irregular */ +static int +setup_Z_dimension + (const int nc, + double** voxel_size, + double* lower_pos, + int8_t* is_z_irregular, + const int check) +{ + enum { Z, X, Y, NDIMS }; + char* mem = NULL; + char* mem2 = NULL; + double* pvxsz = NULL; + size_t len[NDIMS], start[NDIMS], dim_len[NDIMS], iz; + size_t i; + double vxsz; + nc_type type; + int varid, dimids[NDIMS], irregular, ndims; + int err = NC_NOERR; + res_T res = RES_OK; + ASSERT(nc && voxel_size && lower_pos && is_z_irregular); + + err = nc_inq_varid(nc, "VLEV", &varid); + if(err != NC_NOERR) { + fprintf(stderr, "Could not inquire the 'VLEV' variable -- %s\n", + ncerr_to_str(err)); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_varndims(nc, varid, &ndims)); + if(ndims != NDIMS) { + fprintf(stderr, "The dimension of the 'VLEV' variable must be %i .\n", + NDIMS); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_vardimid(nc, varid, dimids)); + FOR_EACH(i, 0, NDIMS) NC(inq_dimlen(nc, dimids[i], len+i)); + + NC(inq_vartype(nc, varid, &type)); + if(type != NC_DOUBLE && type != NC_FLOAT) { + fprintf(stderr, "The type of 'VLEV' variable must be float or double.\n"); + res = RES_BAD_ARG; + goto error; + } + + mem = mem_alloc(len[Z]*sizeof_nctype(type)); + if(!mem) { + fprintf(stderr, "Could not allocate memory for the 'VLEV' variable.\n"); + res = RES_MEM_ERR; + goto error; + } + + start[X] = 0; dim_len[X] = 1; + start[Y] = 0; dim_len[Y] = 1; + start[Z] = 0; dim_len[Z] = len[Z]; + + NC(get_vara(nc, varid, start, dim_len, mem)); + + #define AT(Mem, Id) (type == NC_FLOAT ? ((float*)Mem)[Id] : ((double*)Mem)[Id]) + + /* Assume that the Z dimension starts from 0 */ + vxsz = AT(mem, 0) * 2.0; + if(vxsz <= 0) { + fprintf(stderr, "The 'VLEV' variable can't have negative or null values.\n"); + res = RES_BAD_ARG; + goto error; + } + + /* Check if the Z dimension is regular */ + FOR_EACH(iz, 1, len[Z]) { + if(!eq_eps(AT(mem, iz) - AT(mem, iz-1), vxsz, 1.e-6)) break; + } + irregular = (iz != len[Z]); + + /* Setup the size of the voxel */ + pvxsz = mem_alloc(sizeof(double) * (irregular ? len[Z] : 1)); + if(!pvxsz) { + fprintf(stderr, + "Could not allocate memory for the voxel size along the Z dimension.\n"); + res = RES_MEM_ERR; + goto error; + } + pvxsz[0] = vxsz; + + if(irregular) { + FOR_EACH(iz, 1, len[Z]) { + pvxsz[iz] = (AT(mem, iz) - (AT(mem, iz-1) + pvxsz[iz-1]*0.5)) * 2.0; + if(pvxsz[iz] <= 0) { + fprintf(stderr, + "The 'VLEV' variable can't have negative or null values.\n"); + res = RES_BAD_ARG; + goto error; + } + } + } + + /* Check that the others columns have the same voxel size */ + if(check) { + size_t ix, iy; + + mem2 = mem_alloc(len[Z]*sizeof_nctype(type)); + if(!mem2) { + fprintf(stderr, + "Could not allocate memory to check the 'VLEV' variable.\n"); + res = RES_MEM_ERR; + goto error; + } + + FOR_EACH(iy, 0, len[Y]) { + start[Y] = iy; + FOR_EACH(ix, 1, len[X]) { + start[X] = ix; + NC(get_vara(nc, varid, start, dim_len, mem2)); + FOR_EACH(iz, 0, len[Z]) { + if(!eq_eps(AT(mem2, iz), AT(mem, iz), 1.e-6)) { + fprintf(stderr, + "The Z columns of the 'VLEV' variable must be the same.\n"); + res = RES_BAD_ARG; + goto error; + } + } + } + } + } + #undef AT + + *lower_pos = 0; + *voxel_size = pvxsz; + *is_z_irregular = (int8_t)irregular; + +exit: + if(mem2) mem_rm(mem2); + if(mem) mem_rm(mem); + return res; +error: + goto exit; +} + +static res_T +write_data(int nc, const char* var_name, FILE* stream) +{ + enum { TIME, Z, X, Y, NDIMS }; + char* mem = NULL; + size_t start[NDIMS], dim_len[NDIMS], len[NDIMS], i, grid_len; + nc_type type; + int varid, ndims, dimids[NDIMS]; + int err = NC_NOERR; + res_T res = RES_OK; + CHK(var_name); + + err = nc_inq_varid(nc, var_name, &varid); + if(err != NC_NOERR) { + fprintf(stderr, "Could not inquire the '%s' variable -- %s\n", + var_name, ncerr_to_str(err)); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_varndims(nc, varid, &ndims)); + if(ndims != NDIMS) { + fprintf(stderr, "The dimension of the '%s' variable must be %i .\n", + var_name, NDIMS); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_vardimid(nc, varid, dimids)); + FOR_EACH(i, 0, NDIMS) NC(inq_dimlen(nc, dimids[i], len+i)); + + NC(inq_vartype(nc, varid, &type)); + if(type != NC_DOUBLE && type != NC_FLOAT) { + fprintf(stderr, + "The type of the '%s' variable cannot be %s. Expecting floating point data.\n", + var_name, nctype_to_str(type)); + res = RES_BAD_ARG; + goto error; + } + + grid_len = len[X]*len[Y]*len[Z]; + mem = mem_alloc(grid_len*sizeof_nctype(type)); + if(!mem) { + fprintf(stderr, "Could not allocate memory for the '%s' variable.\n", + var_name); + res = RES_MEM_ERR; + goto error; + } + + start[X] = 0; dim_len[X] = len[X]; + start[Y] = 0; dim_len[Y] = len[Y]; + start[Z] = 0; dim_len[Z] = len[Z]; + + FOR_EACH(i, 0, len[TIME]) { + start[TIME] = i; + dim_len[TIME] = 1; + NC(get_vara(nc, varid, start, dim_len, mem)); + if(fwrite(mem, sizeof_nctype(type), grid_len, stream) != grid_len) { + fprintf(stderr, "Error writing data of the '%s' variable -- '%s'.\n", + var_name, strerror(errno)); + res = RES_IO_ERR; + goto error; + } + } +exit: + if(mem) mem_rm(mem); + return res; +error: + goto exit; +} + +/******************************************************************************* + * Program + ******************************************************************************/ +int +main(int argc, char** argv) +{ + FILE* stream = stdout; + struct grid grid = GRID_NULL; + struct args args = ARGS_DEFAULT; + int err = 0; + int err_nc = NC_NOERR; + int nc = INVALID_ID; + res_T res = RES_OK; + + res = args_init(&args, argc, argv); + if(res != RES_OK) goto error; + if(args.quit) goto exit; + + if(args.output) { + res = open_output_stream(args.output, args.force_overwrite, &stream); + if(res != RES_OK) goto error; + } + + err_nc = nc_open(args.input, NC_WRITE|NC_MMAP, &nc); + if(err_nc != NC_NOERR) { + fprintf(stderr, "Error opening file `%s' -- %s.\n", + args.input, ncerr_to_str(err_nc)); + goto exit; + } + + #define CALL(Func) { if(RES_OK != (res = Func)) goto error; } (void)0 + CALL(setup_definition(nc, &grid.nx, &grid.ny, &grid.nz, &grid.ntimes)); + CALL(setup_regular_dimension + (nc, "W_E_direction", &grid.vxsz_x, grid.lower+0, args.check)); + CALL(setup_regular_dimension + (nc, "S_N_direction", &grid.vxsz_y, grid.lower+1, args.check)); + CALL(setup_Z_dimension + (nc, &grid.vxsz_z, grid.lower+2, &grid.is_z_irregular, args.check)); + #undef CALL + + #define WRITE(Var, N, Name) { \ + if(fwrite(Var, sizeof(*Var), (N), stream) != (N)) { \ + fprintf(stderr, "Error writing the %s.\n", Name); \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + if(!args.no_output) { + WRITE(&grid.is_z_irregular, 1, "'irregular' Z flag"); + WRITE(&grid.nx, 1, "X definition"); + WRITE(&grid.ny, 1, "Y definition"); + WRITE(&grid.ny, 1, "Z definition"); + WRITE(&grid.ntimes, 1, "time definition"); + WRITE(grid.lower, 3, "lower position"); + WRITE(&grid.vxsz_x, 1, "X voxel size"); + WRITE(&grid.vxsz_y, 1, "Y voxel size"); + WRITE(grid.vxsz_z, grid.is_z_irregular?(size_t)grid.nz:1, "Z voxel size(s)"); + + write_data(nc, "RVT", stream); + write_data(nc, "RCT", stream); + } + #undef WRITE + +exit: + grid_release(&grid); + if(nc != INVALID_ID) NC(close(nc)); + if(mem_allocated_size() != 0) { + fprintf(stderr, "Memory leaks: %lu Bytes\n", + (unsigned long)mem_allocated_size()); + err = -1; + } + return err; +error: + err = -1; + goto exit; +} + diff --git a/src/les2htcop.h.in b/src/les2htcop.h.in @@ -0,0 +1,24 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef LES2HTLES_H +#define LES2HTLES_H + +#define LES2HTLES_VERSION_MAJOR @VERSION_MAJOR@ +#define LES2HTLES_VERSION_MINOR @VERSION_MINOR@ +#define LES2HTLES_VERSION_PATCH @VERSION_PATCH@ + +#endif /* LES2HTLES_H */ + diff --git a/src/test_nc.c b/src/test_nc.c @@ -1,709 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#define _POSIX_C_SOURCE 200112L /* getopt/close support */ - -#include <rsys/rsys.h> -#include <rsys/math.h> -#include <rsys/mem_allocator.h> - -#include <alloca.h> -#include <netcdf.h> -#include <string.h> -#include <unistd.h> /* getopt */ -#include <fcntl.h> /* open */ -#include <sys/stat.h> /* S_IRUSR & S_IWUSR */ - -/******************************************************************************* - * Command arguments - ******************************************************************************/ -struct args { - const char* output; - const char* input; - int force_overwrite; - int check; - int no_output; - int quit; /* Quit the application */ -}; -#define ARGS_DEFAULT__ {NULL,NULL,0,0,0,0} -static const struct args ARGS_DEFAULT = ARGS_DEFAULT__; - -static void -print_help(const char* cmd) -{ - ASSERT(cmd); - - printf( -"Usage: %s -i INPUT [OPTIONS]\n" -"Convert the LES data stored into INPUT from NetCDF to the htLES fileformat.\n\n", - cmd); - printf( -" -c advanced check of the validity of the submitted LES file\n" -" with respect to the converter pre-requisites. Note that\n" -" this option can increase significantly the conversion\n" -" time.\n"); - printf( -" -f overwrite the OUTPUT file if it already exists.\n"); - printf( -" -h display this help and exit.\n"); - printf( -" -i INPUT path of the LES file to convert.\n"); - printf( -" -o OUTPUT write results to OUTPUT. If not defined, write results to\n" -" standard output.\n"); - printf( -" -q do not write results to OUTPUT.\n"); - printf("\n"); - printf( -"%s (C) 2018 |Meso|Star>. This is free software released under the GNU GPL\n" -"license, version 3 or later. You are free to change or redistribute it under\n" -"certain conditions <http://gnu.org/licenses/gpl.html>.\n", cmd); -} - -static void -args_release(struct args* args) -{ - ASSERT(args); - *args = ARGS_DEFAULT; -} - -static res_T -args_init(struct args* args, const int argc, char** argv) -{ - int opt; - res_T res = RES_OK; - ASSERT(args && argc && argv); - - while((opt = getopt(argc, argv, "cfhi:o:q")) != -1) { - switch(opt) { - case 'c': args->check = 1; break; - case 'f': args->force_overwrite = 1; break; - case 'h': - print_help(argv[0]); - args_release(args); - args->quit = 1; - goto exit; - case 'i': args->input = optarg; break; - case 'o': args->output = optarg; break; - case 'q': args->no_output = 1; break; - default: RES_BAD_ARG; break; - } - if(res != RES_OK) { - if(optarg) { - fprintf(stderr, "%s: invalid option argumet '%s' -- '%c'\n", - argv[0], optarg, opt); - } - goto error; - } - } - - if(!args->input) { - fprintf(stderr, "Missing input file.\n"); - res = RES_BAD_ARG; - goto error; - } - - if(args->no_output) args->output = NULL; - -exit: - return res; -error: - args_release(args); - goto exit; -} - -/******************************************************************************* - * Grid header - ******************************************************************************/ -struct grid { - int32_t nx, ny, nz, ntimes; /* Definition */ - int8_t is_z_irregular; - double lower[3]; /* Lower position of the grid */ - double vxsz_x; /* Size of the voxel is X */ - double vxsz_y; /* Size of the voxel in Y */ - double* vxsz_z; /* Size of the voxel in Z */ -}; - -#define GRID_NULL__ {0,0,0,0,0,{0,0,0},0,0,NULL} -static const struct grid GRID_NULL = GRID_NULL__; - -static void -grid_release(struct grid* grid) -{ - CHK(grid); - if(grid->vxsz_z) mem_rm(grid->vxsz_z); -} - -/******************************************************************************* - * NetCDF helper functions and macros - ******************************************************************************/ -#define INVALID_ID -1 - -#define NC(Func) { \ - const int err__ = nc_ ## Func; \ - if(err__ != NC_NOERR) { \ - fprintf(stderr, "error:%i:%s\n", __LINE__, ncerr_to_str(err__)); \ - abort(); \ - } \ - } (void)0 - -static INLINE const char* -ncerr_to_str(const int ncerr) -{ - const char* str = "NC_ERR_<UNKNOWN>"; - switch(ncerr) { - case NC_EBADGRPID: str = "NC_EBADGRPID"; break; - case NC_EBADID: str = "NC_EBADID"; break; - case NC_EBADNAME: str = "NC_EBADNAME"; break; - case NC_ECHAR: str = "NC_ECHAR"; break; - case NC_EDIMMETA: str = "NC_EDIMMETA"; break; - case NC_EHDFERR: str = "NC_EHDFERR"; break; - case NC_ENOMEM: str = "NC_ENOMEM"; break; - case NC_ENOTATT: str = "NC_ENOTATT"; break; - case NC_ENOTVAR: str = "NC_ENOTVAR"; break; - case NC_ERANGE: str = "NC_ERANGE"; break; - case NC_NOERR: str = "NC_NOERR"; break; - } - return str; -} - -static INLINE const char* -nctype_to_str(const nc_type type) -{ - const char* str = "NC_TYPE_<UNKNOWN>"; - switch(type) { - case NC_NAT: str = "NC_NAT"; break; - case NC_BYTE: str = "NC_BYTE"; break; - case NC_CHAR: str = "NC_CHAR"; break; - case NC_SHORT: str = "NC_SHORT"; break; - case NC_LONG: str = "NC_LONG"; break; - case NC_FLOAT: str = "NC_FLOAT"; break; - case NC_DOUBLE: str = "NC_DOUBLE"; break; - case NC_UBYTE: str = "NC_UBYTE"; break; - case NC_USHORT: str = "NC_USHORT"; break; - case NC_UINT: str = "NC_UINT"; break; - case NC_INT64: str = "NC_INT64"; break; - case NC_UINT64: str = "NC_UINT64"; break; - case NC_STRING: str = "NC_STRING"; break; - default: FATAL("Unreachable code.\n"); break; - } - return str; -} - -static INLINE size_t -sizeof_nctype(const nc_type type) -{ - size_t sz; - switch(type) { - case NC_BYTE: - case NC_CHAR: - case NC_UBYTE: - sz = 1; break; - case NC_SHORT: - case NC_USHORT: - sz = 2; break; - case NC_FLOAT: - case NC_INT: - case NC_UINT: - sz = 4; break; - case NC_DOUBLE: - case NC_INT64: - case NC_UINT64: - sz = 8; break; - default: FATAL("Unreachable cde.\n"); break; - } - return sz; -} - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static res_T -open_output_stream(const char* path, const int force_overwrite, FILE** stream) -{ - int fd = -1; - FILE* fp = NULL; - res_T res = RES_OK; - ASSERT(path); - - if(force_overwrite) { - fp = fopen(path, "w"); - if(!fp) { - fprintf(stderr, "Could not open the output file '%s'.\n", path); - goto error; - } - } else { - fd = open(path, O_CREAT|O_WRONLY|O_EXCL|O_TRUNC, S_IRUSR|S_IWUSR); - if(fd >= 0) { - fp = fdopen(fd, "w"); - if(fp == NULL) { - fprintf(stderr, "Could not open the output file '%s'.\n", path); - goto error; - } - } else if(errno == EEXIST) { - fprintf(stderr, - "The output file '%s' already exists. Use -f to overwrite it.\n", path); - goto error; - } else { - fprintf(stderr, - "Unexpected error while opening the output file `'%s'.\n", path); - goto error; - } - } - -exit: - *stream = fp; - return res; -error: - res = RES_IO_ERR; - if(fp) { - CHK(fclose(fp) == 0); - fp = NULL; - } else if(fd >= 0) { - CHK(close(fd) == 0); - } - goto exit; -} - - -static res_T -setup_definition - (int nc, int32_t* nx, int32_t* ny, int32_t* nz, int32_t* ntimes) -{ - size_t len[4]; - int dimids[4]; - int id; - int ndims; - int err = NC_NOERR; - res_T res = RES_OK; - ASSERT(nx && ny && nz && ntimes); - - err = nc_inq_varid(nc, "RCT", &id); - if(err != NC_NOERR) { - fprintf(stderr, "Could not inquire the 'RCT' variable -- %s\n", - ncerr_to_str(err)); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_varndims(nc, id, &ndims)); - if(ndims != 4) { - fprintf(stderr, "The dimension of the 'RCT' variable must be 4.\n"); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_vardimid(nc, id, dimids)); - NC(inq_dimlen(nc, dimids[0], len+0)); - NC(inq_dimlen(nc, dimids[1], len+1)); - NC(inq_dimlen(nc, dimids[2], len+2)); - NC(inq_dimlen(nc, dimids[3], len+3)); - - *nx = (int32_t)len[3]; - *ny = (int32_t)len[2]; - *nz = (int32_t)len[1]; - *ntimes = (int32_t)len[0]; - - if(*nx < 1 || *ny < 1 || *nz < 1 || *ntimes < 1) { - fprintf(stderr, - "The spatial and time definitions cannot be null.\n" - " #x = %i; #y = %i; #z = %i; #time = %i\n", - *nx, *ny, *nz, *ntimes); - res = RES_BAD_ARG; - goto error; - } - -exit: - return res; -error: - goto exit; -} - -static res_T -setup_regular_dimension - (const int nc, - const char* var_name, - double* voxel_size, - double* lower_pos, - const int check) -{ - char* mem = NULL; - size_t len, start, i; - double vxsz; - nc_type type; - int varid, dimid, ndims; - int err = NC_NOERR; - res_T res = RES_OK; - ASSERT(nc && var_name && voxel_size && lower_pos); - - err = nc_inq_varid(nc, var_name, &varid); - if(err != NC_NOERR) { - fprintf(stderr, "Could not inquire the '%s' variable -- %s\n", - var_name, ncerr_to_str(err)); - res = RES_BAD_ARG; - goto error; - } - NC(inq_varndims(nc, varid, &ndims)); - if(ndims != 1) { - fprintf(stderr, "The dimension of the '%s' variable must be 1 .\n", var_name); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_vardimid(nc, varid, &dimid)); - NC(inq_dimlen(nc, dimid, &len)); - ASSERT(len > 0); - - NC(inq_vartype(nc, varid, &type)); - if(type != NC_DOUBLE && type != NC_FLOAT) { - fprintf(stderr, "The type of the '%s' variable must be float or double.\n", - var_name); - res = RES_BAD_ARG; - goto error; - } - - #define AT(Mem, Id) (type == NC_FLOAT ? ((float*)Mem)[Id] : ((double*)Mem)[Id]) - if(!check) { - char* raw = alloca(8); - start = 0; - len = 1; - NC(get_vara(nc, varid, &start, &len, raw)); - vxsz = AT(raw, 0) * 2; /* Assume that the grid starts at 0 */ - } else { - mem = mem_alloc(len*sizeof_nctype(type)); - if(!mem) { - fprintf(stderr, "Could not allocate memory for the '%s' variable.\n", var_name); - res = RES_MEM_ERR; - goto error; - } - start = 0; - NC(get_vara(nc, varid, &start, &len, mem)); - vxsz = AT(mem, 0) * 2; /* Assume that the grid starts from 0 */ - } - - if(vxsz <= 0) { - fprintf(stderr, "The '%s' variable can't have negative or null values.\n", - var_name); - res = RES_BAD_ARG; - goto error; - } - - if(check) { - /* Check that the dimension is regular */ - FOR_EACH(i, 1, len) { - if(!eq_eps(AT(mem, i) - AT(mem, i-1), vxsz, 1.e-6)) { - fprintf(stderr, "The voxel size of the '%s' variable must be regular.\n", - var_name); - res = RES_BAD_ARG; - goto error; - } - } - } - #undef AT - - *voxel_size = vxsz; - *lower_pos = 0; -exit: - if(mem) mem_rm(mem); - return res; -error: - goto exit; -} - -/* Return 1 if Z is irregular */ -static int -setup_Z_dimension - (const int nc, - double** voxel_size, - double* lower_pos, - int8_t* is_z_irregular, - const int check) -{ - enum { Z, X, Y, NDIMS }; - char* mem = NULL; - char* mem2 = NULL; - double* pvxsz = NULL; - size_t len[NDIMS], start[NDIMS], dim_len[NDIMS], iz; - size_t i; - double vxsz; - nc_type type; - int varid, dimids[NDIMS], irregular, ndims; - int err = NC_NOERR; - res_T res = RES_OK; - ASSERT(nc && voxel_size && lower_pos && is_z_irregular); - - err = nc_inq_varid(nc, "VLEV", &varid); - if(err != NC_NOERR) { - fprintf(stderr, "Could not inquire the 'VLEV' variable -- %s\n", - ncerr_to_str(err)); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_varndims(nc, varid, &ndims)); - if(ndims != NDIMS) { - fprintf(stderr, "The dimension of the 'VLEV' variable must be %i .\n", - NDIMS); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_vardimid(nc, varid, dimids)); - FOR_EACH(i, 0, NDIMS) NC(inq_dimlen(nc, dimids[i], len+i)); - - NC(inq_vartype(nc, varid, &type)); - if(type != NC_DOUBLE && type != NC_FLOAT) { - fprintf(stderr, "The type of 'VLEV' variable must be float or double.\n"); - res = RES_BAD_ARG; - goto error; - } - - mem = mem_alloc(len[Z]*sizeof_nctype(type)); - if(!mem) { - fprintf(stderr, "Could not allocate memory for the 'VLEV' variable.\n"); - res = RES_MEM_ERR; - goto error; - } - - start[X] = 0; dim_len[X] = 1; - start[Y] = 0; dim_len[Y] = 1; - start[Z] = 0; dim_len[Z] = len[Z]; - - NC(get_vara(nc, varid, start, dim_len, mem)); - - #define AT(Mem, Id) (type == NC_FLOAT ? ((float*)Mem)[Id] : ((double*)Mem)[Id]) - - /* Assume that the Z dimension starts from 0 */ - vxsz = AT(mem, 0) * 2.0; - if(vxsz <= 0) { - fprintf(stderr, "The 'VLEV' variable can't have negative or null values.\n"); - res = RES_BAD_ARG; - goto error; - } - - /* Check if the Z dimension is regular */ - FOR_EACH(iz, 1, len[Z]) { - if(!eq_eps(AT(mem, iz) - AT(mem, iz-1), vxsz, 1.e-6)) break; - } - irregular = (iz != len[Z]); - - /* Setup the size of the voxel */ - pvxsz = mem_alloc(sizeof(double) * (irregular ? len[Z] : 1)); - if(!pvxsz) { - fprintf(stderr, - "Could not allocate memory for the voxel size along the Z dimension.\n"); - res = RES_MEM_ERR; - goto error; - } - pvxsz[0] = vxsz; - - if(irregular) { - FOR_EACH(iz, 1, len[Z]) { - pvxsz[iz] = (AT(mem, iz) - (AT(mem, iz-1) + pvxsz[iz-1]*0.5)) * 2.0; - if(pvxsz[iz] <= 0) { - fprintf(stderr, - "The 'VLEV' variable can't have negative or null values.\n"); - res = RES_BAD_ARG; - goto error; - } - } - } - - /* Check that the others columns have the same voxel size */ - if(check) { - size_t ix, iy; - - mem2 = mem_alloc(len[Z]*sizeof_nctype(type)); - if(!mem2) { - fprintf(stderr, - "Could not allocate memory to check the 'VLEV' variable.\n"); - res = RES_MEM_ERR; - goto error; - } - - FOR_EACH(iy, 0, len[Y]) { - start[Y] = iy; - FOR_EACH(ix, 1, len[X]) { - start[X] = ix; - NC(get_vara(nc, varid, start, dim_len, mem2)); - FOR_EACH(iz, 0, len[Z]) { - if(!eq_eps(AT(mem2, iz), AT(mem, iz), 1.e-6)) { - fprintf(stderr, - "The Z columns of the 'VLEV' variable must be the same.\n"); - res = RES_BAD_ARG; - goto error; - } - } - } - } - } - #undef AT - - *lower_pos = 0; - *voxel_size = pvxsz; - *is_z_irregular = (int8_t)irregular; - -exit: - if(mem2) mem_rm(mem2); - if(mem) mem_rm(mem); - return res; -error: - goto exit; -} - -static res_T -write_data(int nc, const char* var_name, FILE* stream) -{ - enum { TIME, Z, X, Y, NDIMS }; - char* mem = NULL; - size_t start[NDIMS], dim_len[NDIMS], len[NDIMS], i, grid_len; - nc_type type; - int varid, ndims, dimids[NDIMS]; - int err = NC_NOERR; - res_T res = RES_OK; - CHK(var_name); - - err = nc_inq_varid(nc, var_name, &varid); - if(err != NC_NOERR) { - fprintf(stderr, "Could not inquire the '%s' variable -- %s\n", - var_name, ncerr_to_str(err)); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_varndims(nc, varid, &ndims)); - if(ndims != NDIMS) { - fprintf(stderr, "The dimension of the '%s' variable must be %i .\n", - var_name, NDIMS); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_vardimid(nc, varid, dimids)); - FOR_EACH(i, 0, NDIMS) NC(inq_dimlen(nc, dimids[i], len+i)); - - NC(inq_vartype(nc, varid, &type)); - if(type != NC_DOUBLE && type != NC_FLOAT) { - fprintf(stderr, - "The type of the '%s' variable cannot be %s. Expecting floating point data.\n", - var_name, nctype_to_str(type)); - res = RES_BAD_ARG; - goto error; - } - - grid_len = len[X]*len[Y]*len[Z]; - mem = mem_alloc(grid_len*sizeof_nctype(type)); - if(!mem) { - fprintf(stderr, "Could not allocate memory for the '%s' variable.\n", - var_name); - res = RES_MEM_ERR; - goto error; - } - - start[X] = 0; dim_len[X] = len[X]; - start[Y] = 0; dim_len[Y] = len[Y]; - start[Z] = 0; dim_len[Z] = len[Z]; - - FOR_EACH(i, 0, len[TIME]) { - start[TIME] = i; - dim_len[TIME] = 1; - NC(get_vara(nc, varid, start, dim_len, mem)); - if(fwrite(mem, sizeof_nctype(type), grid_len, stream) != grid_len) { - fprintf(stderr, "Error writing data of the '%s' variable -- '%s'.\n", - var_name, strerror(errno)); - res = RES_IO_ERR; - goto error; - } - } -exit: - if(mem) mem_rm(mem); - return res; -error: - goto exit; -} - -/******************************************************************************* - * Program - ******************************************************************************/ -int -main(int argc, char** argv) -{ - FILE* stream = stdout; - struct grid grid = GRID_NULL; - struct args args = ARGS_DEFAULT; - int err = 0; - int err_nc = NC_NOERR; - int nc = INVALID_ID; - res_T res = RES_OK; - - res = args_init(&args, argc, argv); - if(res != RES_OK) goto error; - if(args.quit) goto exit; - - if(args.output) { - res = open_output_stream(args.output, args.force_overwrite, &stream); - if(res != RES_OK) goto error; - } - - err_nc = nc_open(args.input, NC_WRITE|NC_MMAP, &nc); - if(err_nc != NC_NOERR) { - fprintf(stderr, "Error opening file `%s' -- %s.\n", - args.input, ncerr_to_str(err_nc)); - goto exit; - } - - #define CALL(Func) { if(RES_OK != (res = Func)) goto error; } (void)0 - CALL(setup_definition(nc, &grid.nx, &grid.ny, &grid.nz, &grid.ntimes)); - CALL(setup_regular_dimension - (nc, "W_E_direction", &grid.vxsz_x, grid.lower+0, args.check)); - CALL(setup_regular_dimension - (nc, "S_N_direction", &grid.vxsz_y, grid.lower+1, args.check)); - CALL(setup_Z_dimension - (nc, &grid.vxsz_z, grid.lower+2, &grid.is_z_irregular, args.check)); - #undef CALL - - if(!args.no_output) { - CHK(fwrite(&grid.is_z_irregular, sizeof(int8_t), 1, stream) == 1); - CHK(fwrite(&grid.nx, sizeof(int32_t), 1, stream) == 1); - CHK(fwrite(&grid.ny, sizeof(int32_t), 1, stream) == 1); - CHK(fwrite(&grid.nz, sizeof(int32_t), 1, stream) == 1); - CHK(fwrite(&grid.ntimes, sizeof(int32_t), 1, stream) == 1); - CHK(fwrite(grid.lower, sizeof(double), 3, stream) == 3); - CHK(fwrite(&grid.vxsz_x, sizeof(double), 1, stream) == 1); - CHK(fwrite(&grid.vxsz_y, sizeof(double), 1, stream) == 1); - if(!grid.is_z_irregular) { - CHK(fwrite(grid.vxsz_z, sizeof(double), 1, stream) == 1); - } else { - CHK(fwrite(grid.vxsz_z, sizeof(double), (size_t)grid.nz, stream) - == (size_t)grid.nz); - } - write_data(nc, "RVT", stream); - write_data(nc, "RCT", stream); - } - -exit: - grid_release(&grid); - if(nc != INVALID_ID) NC(close(nc)); - if(mem_allocated_size() != 0) { - fprintf(stderr, "Memory leaks: %lu Bytes\n", - (unsigned long)mem_allocated_size()); - err = -1; - } - return err; -error: - err = -1; - goto exit; -} -