htcp

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

commit 9334b1ae918dadaef0d73bd137bac2265bb0a495
parent 543e2b5a84cf11da087ba47f92c9945cfa1edee0
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 26 Apr 2018 18:37:09 +0200

Make test_nc a program that convers LES into a specific fileformat

Diffstat:
Msrc/test_nc.c | 628++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 557 insertions(+), 71 deletions(-)

diff --git a/src/test_nc.c b/src/test_nc.c @@ -13,14 +13,147 @@ * 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 2 /* getopt 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> + +/******************************************************************************* + * 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__; -#define NCID_INVALID -1 +static void +print_help(const char* cmd) +{ + ASSERT(cmd); -/* - * Simple program used to play with the NetCDF API and the LES fileformat - */ + 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, "chi: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; + } + +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) free(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) @@ -85,88 +218,441 @@ sizeof_nctype(const nc_type type) case NC_INT64: case NC_UINT64: sz = 8; break; - default: FATAL("Unreachable code.\n"); break; + default: FATAL("Unreachable cde.\n"); break; } return sz; } -#define NC(Func) { \ - const int err__ = nc_ ## Func; \ - if(err__ != NC_NOERR) { \ - fprintf(stderr, "error:%s:%i:%s:%s\n", \ - __FILE__, __LINE__, FUNC_NAME, ncerr_to_str(err__)); \ - abort(); \ - } \ - } (void)0 +/******************************************************************************* + * Helper functions + ******************************************************************************/ +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); -int -main(int argc, char** argv) + 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) { - int ncid = NCID_INVALID; - int id = NCID_INVALID; + char* mem = NULL; + size_t len, start, i; + double vxsz; nc_type type; - int ndims; - int dimid[BUFSIZ]; - size_t startid[BUFSIZ]; - int natts; - size_t len[BUFSIZ]; - char str[BUFSIZ]; - int err; - void* mem = NULL; - - const char* att_name[] = { - "W_E_direction", - "S_N_direction", - "VLEV", - "RVT", - "RCT" - }; - const size_t natt_names = sizeof(att_name)/sizeof(att_name[0]); - size_t iatt; - size_t idim; - size_t ival; - - if(argc < 2) { - fprintf(stderr, "Usage: %s FILENAME\n", argv[0]); - goto error; - } - - NC(open(argv[1], NC_WRITE|NC_MMAP, &ncid)); - - FOR_EACH(iatt, 0, natt_names) { - NC(inq_varid(ncid, att_name[iatt], &id)); - NC(inq_var(ncid, id, str, &type, &ndims, dimid, &natts)); - printf("%s: type: %s; #dims: %d; #atts: %d\n", - str, nctype_to_str(type), ndims, natts); - - FOR_EACH(idim, 0, ndims) { - NC(inq_dim(ncid, dimid[idim], str, len)); - printf("\t%s: len: %lu\n", str, (unsigned long)len[0]); + 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_varid(ncid, "RVT", &id)); - NC(inq_var(ncid, id, NULL, &type, &ndims, dimid, NULL)); - CHK(type == NC_FLOAT); - CHK(ndims == 4); - NC(inq_dim(ncid, dimid[0], NULL, len+0)); - NC(inq_dim(ncid, dimid[1], NULL, len+1)); - NC(inq_dim(ncid, dimid[2], NULL, len+2)); - NC(inq_dim(ncid, dimid[3], NULL, len+3)); - startid[0] = 0; - startid[1] = 0; - startid[2] = 0; - startid[3] = 0; + 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; + } - CHK(mem = mem_alloc(len[0]*len[1]*len[2]*len[3]*sizeof_nctype(type))); - NC(get_vara(ncid, id, startid, len, mem)); - FOR_EACH(ival, 0, len[0]*len[1]*len[2]*len[3]) { - printf("%g\n", ((float*)mem)[ival]); + 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); - if(ncid != NCID_INVALID) NC(close(ncid)); + 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) { + if(access(args.output, F_OK) == 0) { + fprintf(stderr, "The OUTPUT file '%s' already exists.\n", args.output); + res = RES_IO_ERR; + goto error; + } + stream = fopen(args.output, "w"); + if(!stream) { + fprintf(stderr, "Could not create the OUTPUT file '%s'.\n", args.output); + res = RES_IO_ERR; + 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)); return err; error: err = -1;