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:
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;
-}
-