diff --git a/CMakeLists.txt b/CMakeLists.txt index 71f8b6cd1..e3aa5456a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # This is the CMake build file for the main directory. # Jim Edwards -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.18) project(PIO LANGUAGES C) # The project version number (modern style) @@ -121,9 +121,9 @@ option(PIO_USE_MPIIO "Enable support for MPI-IO auto detect" ON) option(PIO_USE_MPISERIAL "Enable mpi-serial support (instead of MPI)" OFF) option(PIO_USE_PNETCDF_VARD "Use pnetcdf put_vard" OFF) option(WITH_PNETCDF "Require the use of PnetCDF" ON) +option(PIO_USE_GDAL "Enable support for Geospatial Data Abstraction Library" OFF) option(BUILD_SHARED_LIBS "Build shared libraries" OFF) - if(APPLE) # The linker on macOS does not include `common symbols` by default # Passing the -c flag includes them and fixes an error with undefined symbols (err_buffer, resultlen) @@ -139,6 +139,23 @@ else() endif() +# Find GDAL library if enabled +if(PIO_USE_GDAL) + message(STATUS "Searching for GDAL library...") + + # Find GDAL - will use GDAL_ROOT if provided via -DGDAL_ROOT=/path + find_package(GDAL REQUIRED) + + message(STATUS "Found GDAL ${GDAL_VERSION}") + message(STATUS " GDAL include: ${GDAL_INCLUDE_DIRS}") + message(STATUS " GDAL library: ${GDAL_LIBRARIES}") + + set(PIO_ENABLE_GDAL 1) +else() + message(STATUS "GDAL support disabled") + set(PIO_ENABLE_GDAL 0) +endif() + # Set a variable that appears in the config.h.in file. if(PIO_ENABLE_LOGGING) set(ENABLE_LOGGING 1) @@ -232,6 +249,12 @@ if (DEFINED PNETCDF_DIR) CACHE STRING "Location of the PnetCDF library installation") endif () +# Old GDAL_DIR variable --> GDAL_PATH (for backwards compatibility) +if (DEFINED GDAL_DIR) + set (GDAL_PATH ${GDAL_DIR} + CACHE STRING "Location of the GDAL library installation") +endif () + #============================================================================== # HELPFUL GLOBAL VARIABLES #============================================================================== diff --git a/cmake_config.h.in b/cmake_config.h.in index 68b9e8dc4..b5838fa8c 100644 --- a/cmake_config.h.in +++ b/cmake_config.h.in @@ -46,4 +46,6 @@ /* Does PIO support using pnetcdf for I/O? */ #cmakedefine _PNETCDF +#cmakedefine PIO_ENABLE_GDAL + #endif /* _PIO_CONFIG_ */ diff --git a/configure.ac b/configure.ac index be8ec4290..b432f9bfd 100644 --- a/configure.ac +++ b/configure.ac @@ -182,6 +182,24 @@ test "x$enable_pnetcdf" = xno || enable_pnetcdf=yes AC_MSG_RESULT([$enable_pnetcdf]) AM_CONDITIONAL(BUILD_PNETCDF, [test "x$enable_pnetcdf" = xyes]) +# Does the user want to enable gdal? +AC_MSG_CHECKING([whether gdal is to be used]) +AC_ARG_ENABLE([gdal], + [AS_HELP_STRING([--enable-gdal], + [Enable gdal use.])]) +test "x$enable_gdal" = xyes || enable_gdal=no +AC_MSG_RESULT([$enable_gdal]) +AM_CONDITIONAL(PIO_ENABLE_GDAL, [test "x$enable_gdal" = xyes]) +#Do we have gdal +if test "x$enable_gdal" = xyes; then + AC_CHECK_LIB([gdal], [OGR_Fld_GetType], [], []) + if test "x$ac_cv_lib_gdal_OGR_Fld_GetType" = xyes ; then + AC_DEFINE([PIO_ENABLE_GDAL], [1], [gdal library available]) + else + AC_MSG_ERROR([GDAL not found. Set CPPFLAGS/LDFLAGS or remove --enable-gdal.]) + fi +fi + # Does the user want to build documentation? AC_MSG_CHECKING([whether documentation should be build (requires doxygen)]) AC_ARG_ENABLE([docs], @@ -440,6 +458,7 @@ AX_SET_META([PIO_HAS_PAR_FILTERS], [$have_par_filters],[yes]) AX_SET_META([PIO_HAS_NETCDF4], [$have_netcdf4],[yes]) AX_SET_META([PIO_HAS_NETCDF4_PAR], [$have_netcdf_par],[yes]) AX_SET_META([PIO_HAS_NETCDF_INTEGRATION], [$enable_netcdf_integration],[yes]) +AX_SET_META([PIO_HAS_GDAL],[$enable_gdal],[yes]) # Create output variables from various shell variables, for use in # generating libpio.settings. @@ -460,6 +479,7 @@ AC_SUBST([HAS_NETCDF4], [$have_netcdf4]) AC_SUBST([HAS_NETCDF4_PAR], [$have_netcdf_par]) AC_SUBST([HAS_NETCDF_INTEGRATION], [$enable_netcdf_integration]) AC_SUBST([HAS_PIO_FORTRAN], [$enable_fortran]) +AC_SUBST([PIO_USE_GDAL], [$enable_gdal]) # Create the build summary file. AC_CONFIG_FILES([libpio.settings diff --git a/libpio.settings.in b/libpio.settings.in index 444a9e1cb..1cfcc38c6 100644 --- a/libpio.settings.in +++ b/libpio.settings.in @@ -35,3 +35,4 @@ NetCDF Integration: @HAS_NETCDF_INTEGRATION@ PIO Logging: @HAS_LOGGING@ MPIEXEC: @WITH_MPIEXEC@ Fortran: @HAS_PIO_FORTRAN@ +GDAL @PIO_USE_GDAL@ \ No newline at end of file diff --git a/src/clib/CMakeLists.txt b/src/clib/CMakeLists.txt index 18c64b53b..16b797f50 100644 --- a/src/clib/CMakeLists.txt +++ b/src/clib/CMakeLists.txt @@ -29,6 +29,10 @@ if (NETCDF_INTEGRATION) set (src ${src} ../ncint/nc_get_vard.c ../ncint/ncintdispatch.c ../ncint/ncint_pio.c ../ncint/nc_put_vard.c) endif () +if (PIO_USE_GDAL) + set (src ${src} pio_gdal.c) +endif() + add_library (pioc ${src}) # Always use -fPIC @@ -188,6 +192,14 @@ if(PIO_USE_MPISERIAL) endif() +#===== GDAL ===== +if (PIO_USE_GDAL) + target_include_directories (pioc + PUBLIC ${GDAL_INCLUDE_DIRS}) + target_link_libraries (pioc + PUBLIC ${GDAL_LIBRARIES}) +endif () + include(CheckTypeSize) check_type_size("size_t" SIZEOF_SIZE_T) check_type_size("long long" SIZEOF_LONG_LONG) diff --git a/src/clib/Makefile.am b/src/clib/Makefile.am index 978e3aecd..9ffb0e95c 100644 --- a/src/clib/Makefile.am +++ b/src/clib/Makefile.am @@ -26,3 +26,6 @@ pio_put_nc.c pio_spmd.c pio_get_vard.c pio_put_vard.c pio_error.c \ pio_internal.h uthash.h pio_error.h parallel_sort.h pioc_async.c EXTRA_DIST = CMakeLists.txt topology.c pio_meta.h.in +if PIO_ENABLE_GDAL + libpioc_la_SOURCES += pio_gdal.c +endif diff --git a/src/clib/pio.h b/src/clib/pio.h index b15d61b0b..f6b2780f4 100644 --- a/src/clib/pio.h +++ b/src/clib/pio.h @@ -18,7 +18,9 @@ #include #include - +#ifdef PIO_ENABLE_GDAL +#include +#endif #define NETCDF_VERSION_LE(Maj, Min, Pat) \ (((NC_VERSION_MAJOR == Maj) && (NC_VERSION_MINOR == Min) && (NC_VERSION_PATCH <= Pat)) || \ ((NC_VERSION_MAJOR == Maj) && (NC_VERSION_MINOR < Min)) || (NC_VERSION_MAJOR < Maj)) @@ -320,7 +322,7 @@ typedef struct io_desc_t bool needssort; /** If the decomp has repeated values it can only be used for reading - since it doesn't make sense to write a single value from more than one location. */ + since it doesn't make sense to write a single value from more than one location. */ bool readonly; /** The maximum number of bytes of this iodesc before flushing. */ @@ -346,7 +348,7 @@ typedef struct io_desc_t PIO_Offset llen; /** Actual length of the iobuffer on this task for a case where values - are repeated in the compmap - used for darray read only. */ + are repeated in the compmap - used for darray read only. */ PIO_Offset rllen; /** Maximum llen participating. */ @@ -609,6 +611,14 @@ typedef struct file_desc_t * feature. One consequence is that PIO_IOTYPE_NETCDF4C files will * not have deflate automatically turned on for each var. */ int ncint_file; +#ifdef PIO_ENABLE_GDAL + /** GDAL specific vars - M.Long */ + GDALDatasetH *hDS; + int dateVarID; // Index of field with type OFTDate + int timeVarID; // Index of field with type OFTTime + int datetimeVarID; // Index of field with type OFTDatetime +#endif + } file_desc_t; /** @@ -627,7 +637,11 @@ enum PIO_IOTYPE PIO_IOTYPE_NETCDF4C = 3, /** NetCDF4 (HDF5) parallel */ - PIO_IOTYPE_NETCDF4P = 4 + PIO_IOTYPE_NETCDF4P = 4, + + /** GDAL (serial only) */ + PIO_IOTYPE_GDAL = 5 + }; /** @@ -806,57 +820,57 @@ extern "C" { /* Init decomposition with 1-based compmap array. */ int PIOc_InitDecomp_ReadOnly(int iosysid, int pio_type, int ndims, const int *gdimlen, int maplen, - const PIO_Offset *compmap, int *ioidp, const int *rearr, - const PIO_Offset *iostart, const PIO_Offset *iocount); + const PIO_Offset *compmap, int *ioidp, const int *rearr, + const PIO_Offset *iostart, const PIO_Offset *iocount); int PIOc_InitDecomp(int iosysid, int pio_type, int ndims, const int *gdimlen, int maplen, - const PIO_Offset *compmap, int *ioidp, const int *rearr, - const PIO_Offset *iostart, const PIO_Offset *iocount); + const PIO_Offset *compmap, int *ioidp, const int *rearr, + const PIO_Offset *iostart, const PIO_Offset *iocount); int PIOc_InitDecomp_bc(int iosysid, int basetype, int ndims, const int *gdimlen, - const long int *start, const long int *count, int *ioidp); + const long int *start, const long int *count, int *ioidp); /* Init decomposition with 0-based compmap array. */ int PIOc_init_decomp(int iosysid, int pio_type, int ndims, const int *gdimlen, int maplen, - const PIO_Offset *compmap, int *ioidp, int rearranger, - const PIO_Offset *iostart, const PIO_Offset *iocount); + const PIO_Offset *compmap, int *ioidp, int rearranger, + const PIO_Offset *iostart, const PIO_Offset *iocount); /* Free resources associated with a decomposition. */ int PIOc_freedecomp(int iosysid, int ioid); int PIOc_readmap(const char *file, int *ndims, int **gdims, PIO_Offset *fmaplen, - PIO_Offset **map, MPI_Comm comm); + PIO_Offset **map, MPI_Comm comm); int PIOc_readmap_from_f90(const char *file,int *ndims, int **gdims, PIO_Offset *maplen, - PIO_Offset **map, int f90_comm); + PIO_Offset **map, int f90_comm); int PIOc_writemap(const char *file, int ndims, const int *gdims, PIO_Offset maplen, - PIO_Offset *map, MPI_Comm comm); + PIO_Offset *map, MPI_Comm comm); int PIOc_writemap_from_f90(const char *file, int ndims, const int *gdims, - PIO_Offset maplen, const PIO_Offset *map, int f90_comm); + PIO_Offset maplen, const PIO_Offset *map, int f90_comm); /* Write a decomposition file. */ int PIOc_write_decomp(const char *file, int iosysid, int ioid, MPI_Comm comm); /* Write a decomposition file using netCDF. */ int PIOc_write_nc_decomp(int iosysid, const char *filename, int cmode, int ioid, - char *title, char *history, int fortran_order); + char *title, char *history, int fortran_order); /* Read a netCDF decomposition file. */ int PIOc_read_nc_decomp(int iosysid, const char *filename, int *ioid, MPI_Comm comm, - int pio_type, char *title, char *history, int *fortran_order); + int pio_type, char *title, char *history, int *fortran_order); /* Initializing IO system for async. */ int PIOc_init_async(MPI_Comm world, int num_io_procs, int *io_proc_list, int component_count, - int *num_procs_per_comp, int **proc_list, MPI_Comm *io_comm, MPI_Comm *comp_comm, - int rearranger, int *iosysidp); + int *num_procs_per_comp, int **proc_list, MPI_Comm *io_comm, MPI_Comm *comp_comm, + int rearranger, int *iosysidp); /* Initializing IO system for async - alternative interface. */ int PIOc_init_async_from_comms(MPI_Comm world, int component_count, MPI_Comm *comp_comm, - MPI_Comm io_comm, int rearranger, int *iosysidp); + MPI_Comm io_comm, int rearranger, int *iosysidp); /* How many IO tasks in this iosysid? */ int PIOc_get_numiotasks(int iosysid, int *numiotasks); /* Initialize PIO for intracomm mode. */ int PIOc_Init_Intracomm(MPI_Comm comp_comm, int num_iotasks, int stride, int base, int rearr, - int *iosysidp); + int *iosysidp); /** Shut down an iosystem and free all associated resources. Use * PIOc_free_iosystem() instead. */ @@ -885,10 +899,10 @@ extern "C" { /* Set the options for the rearranger. */ int PIOc_set_rearr_opts(int iosysid, int comm_type, int fcd, - bool enable_hs_c2i, bool enable_isend_c2i, - int max_pend_req_c2i, - bool enable_hs_i2c, bool enable_isend_i2c, - int max_pend_req_i2c); + bool enable_hs_c2i, bool enable_isend_c2i, + int max_pend_req_c2i, + bool enable_hs_i2c, bool enable_isend_i2c, + int max_pend_req_i2c); /* Increment record number. */ int PIOc_advanceframe(int ncid, int varid); @@ -898,11 +912,11 @@ extern "C" { /* Write a distributed array. */ int PIOc_write_darray(int ncid, int varid, int ioid, PIO_Offset arraylen, void *array, - void *fillvalue); + void *fillvalue); /* Write multiple darrays. */ int PIOc_write_darray_multi(int ncid, const int *varids, int ioid, int nvars, PIO_Offset arraylen, - void *array, const int *frame, void **fillvalue, bool flushtodisk); + void *array, const int *frame, void **fillvalue, bool flushtodisk); /* Read distributed array. */ int PIOc_read_darray(int ncid, int varid, int ioid, PIO_Offset arraylen, void *array); @@ -940,9 +954,9 @@ extern "C" { int PIOc_set_hint(int iosysid, const char *hint, const char *hintval); int PIOc_set_chunk_cache(int iosysid, int iotype, PIO_Offset size, PIO_Offset nelems, - float preemption); + float preemption); int PIOc_get_chunk_cache(int iosysid, int iotype, PIO_Offset *sizep, PIO_Offset *nelemsp, - float *preemptionp); + float *preemptionp); /* Dimensions. */ int PIOc_inq_dim(int ncid, int dimid, char *name, PIO_Offset *lenp); @@ -955,14 +969,14 @@ extern "C" { /* Variables. */ int PIOc_inq_varid(int ncid, const char *name, int *varidp); int PIOc_inq_var(int ncid, int varid, char *name, nc_type *xtypep, int *ndimsp, int *dimidsp, - int *nattsp); + int *nattsp); int PIOc_inq_varname(int ncid, int varid, char *name); int PIOc_inq_vartype(int ncid, int varid, nc_type *xtypep); int PIOc_inq_varndims(int ncid, int varid, int *ndimsp); int PIOc_inq_vardimid(int ncid, int varid, int *dimidsp); int PIOc_inq_varnatts(int ncid, int varid, int *nattsp); int PIOc_def_var(int ncid, const char *name, nc_type xtype, int ndims, - const int *dimidsp, int *varidp); + const int *dimidsp, int *varidp); int PIOc_set_fill(int ncid, int fillmode, int *old_modep); int PIOc_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value); int PIOc_inq_var_fill(int ncid, int varid, int *no_fill, void *fill_valuep); @@ -977,17 +991,17 @@ extern "C" { /* These variable settings only apply to netCDF-4 files. */ int PIOc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, - int deflate_level); + int deflate_level); int PIOc_inq_var_deflate(int ncid, int varid, int *shufflep, int *deflatep, - int *deflate_levelp); + int *deflate_levelp); int PIOc_def_var_chunking(int ncid, int varid, int storage, const PIO_Offset *chunksizesp); int PIOc_inq_var_chunking(int ncid, int varid, int *storagep, PIO_Offset *chunksizesp); int PIOc_def_var_endian(int ncid, int varid, int endian); int PIOc_inq_var_endian(int ncid, int varid, int *endianp); int PIOc_set_var_chunk_cache(int ncid, int varid, PIO_Offset size, PIO_Offset nelems, - float preemption); + float preemption); int PIOc_get_var_chunk_cache(int ncid, int varid, PIO_Offset *sizep, PIO_Offset *nelemsp, - float *preemptionp); + float *preemptionp); /* Attributes - misc. */ int PIOc_rename_att(int ncid, int varid, const char *name, const char *newname); @@ -995,7 +1009,7 @@ extern "C" { /* Attributes - inquiry functions. */ int PIOc_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, - PIO_Offset *lenp); + PIO_Offset *lenp); int PIOc_inq_attid(int ncid, int varid, const char *name, int *idp); int PIOc_inq_attlen(int ncid, int varid, const char *name, PIO_Offset *lenp); int PIOc_inq_atttype(int ncid, int varid, const char *name, nc_type *xtypep); @@ -1005,27 +1019,27 @@ extern "C" { int PIOc_put_att(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const void *op); int PIOc_put_att_text(int ncid, int varid, const char *name, PIO_Offset len, const char *op); int PIOc_put_att_schar(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const signed char *op); + const signed char *op); int PIOc_put_att_short(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const short *op); + const short *op); int PIOc_put_att_int(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const int *op); + const int *op); int PIOc_put_att_long(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const long *op); + const long *op); int PIOc_put_att_float(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const float *op); + const float *op); int PIOc_put_att_double(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const double *op); + const double *op); int PIOc_put_att_uchar(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const unsigned char *op); + const unsigned char *op); int PIOc_put_att_ushort(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const unsigned short *op); + const unsigned short *op); int PIOc_put_att_uint(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const unsigned int *op); + const unsigned int *op); int PIOc_put_att_longlong(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, - const long long *op); + const long long *op); int PIOc_put_att_ulonglong(int ncid, int varid, const char *name, nc_type xtype, - PIO_Offset len, const unsigned long long *op); + PIO_Offset len, const unsigned long long *op); /* Attributes - reading. */ int PIOc_get_att(int ncid, int varid, const char *name, void *ip); @@ -1097,179 +1111,179 @@ extern "C" { int PIOc_put_var1_float(int ncid, int varid, const PIO_Offset *index, const float *op); int PIOc_put_var1_double(int ncid, int varid, const PIO_Offset *index, const double *op); int PIOc_put_var1_uchar(int ncid, int varid, const PIO_Offset *index, - const unsigned char *op); + const unsigned char *op); int PIOc_put_var1_ushort(int ncid, int varid, const PIO_Offset *index, - const unsigned short *op); + const unsigned short *op); int PIOc_put_var1_uint(int ncid, int varid, const PIO_Offset *index, - const unsigned int *op); + const unsigned int *op); int PIOc_put_var1_longlong(int ncid, int varid, const PIO_Offset *index, const long long *op); int PIOc_put_var1_ulonglong(int ncid, int varid, const PIO_Offset *index, - const unsigned long long *op); + const unsigned long long *op); /* Data reads - vara. */ int PIOc_get_vara(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, void *buf); int PIOc_get_vara_text(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - char *buf); + char *buf); int PIOc_get_vara_schar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - signed char *buf); + signed char *buf); int PIOc_get_vara_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - short *buf); + short *buf); int PIOc_get_vara_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - int *buf); + int *buf); int PIOc_get_vara_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - float *buf); + float *buf); int PIOc_get_vara_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - long *buf); + long *buf); int PIOc_get_vara_double(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, double *buf); + const PIO_Offset *count, double *buf); int PIOc_get_vara_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - unsigned char *buf); + unsigned char *buf); int PIOc_get_vara_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - unsigned short *buf); + unsigned short *buf); int PIOc_get_vara_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - unsigned int *buf); + unsigned int *buf); int PIOc_get_vara_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - long long *buf); + long long *buf); int PIOc_get_vara_ulonglong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - unsigned long long *buf); + unsigned long long *buf); /* Data writes - vara. */ int PIOc_put_vara(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const void *buf); + const void *buf); int PIOc_put_vara_text(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const char *op); + const PIO_Offset *count, const char *op); int PIOc_put_vara_schar(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const signed char *op); + const PIO_Offset *count, const signed char *op); int PIOc_put_vara_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const short *op); + const short *op); int PIOc_put_vara_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const int *op); + const int *op); int PIOc_put_vara_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const long *op); + const long *op); int PIOc_put_vara_float(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const float *op); + const PIO_Offset *count, const float *op); int PIOc_put_vara_double(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const double *op); + const double *op); int PIOc_put_vara_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const unsigned char *op); + const unsigned char *op); int PIOc_put_vara_ushort(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const unsigned short *op); + const PIO_Offset *count, const unsigned short *op); int PIOc_put_vara_uint(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const unsigned int *op); + const PIO_Offset *count, const unsigned int *op); int PIOc_put_vara_longlong(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const long long *op); + const PIO_Offset *count, const long long *op); int PIOc_put_vara_ulonglong(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const unsigned long long *op); + const PIO_Offset *count, const unsigned long long *op); /* Data reads - vars. */ int PIOc_get_vars(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, void *buf); + const PIO_Offset *stride, void *buf); int PIOc_get_vars_text(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, char *buf); + const PIO_Offset *stride, char *buf); int PIOc_get_vars_schar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, signed char *buf); + const PIO_Offset *stride, signed char *buf); int PIOc_get_vars_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, short *buf); + const PIO_Offset *stride, short *buf); int PIOc_get_vars_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, int *buf); + const PIO_Offset *stride, int *buf); int PIOc_get_vars_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, long *buf); + const PIO_Offset *stride, long *buf); int PIOc_get_vars_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, float *buf); + const PIO_Offset *stride, float *buf); int PIOc_get_vars_double(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, double *buf); + const PIO_Offset *stride, double *buf); int PIOc_get_vars_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, unsigned char *buf); + const PIO_Offset *stride, unsigned char *buf); int PIOc_get_vars_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, unsigned short *buf); + const PIO_Offset *stride, unsigned short *buf); int PIOc_get_vars_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, unsigned int *buf); + const PIO_Offset *stride, unsigned int *buf); int PIOc_get_vars_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, long long *buf); + const PIO_Offset *stride, long long *buf); int PIOc_get_vars_ulonglong(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const PIO_Offset *stride, - unsigned long long *buf); + const PIO_Offset *count, const PIO_Offset *stride, + unsigned long long *buf); /* Data writes - vars. */ int PIOc_put_vars(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, const void *buf); + const PIO_Offset *stride, const void *buf); int PIOc_put_vars_text(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const PIO_Offset *stride, const char *op); + const PIO_Offset *count, const PIO_Offset *stride, const char *op); int PIOc_put_vars_schar(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const PIO_Offset *stride, - const signed char *op); + const PIO_Offset *count, const PIO_Offset *stride, + const signed char *op); int PIOc_put_vars_short(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const PIO_Offset *stride, const short *op); + const PIO_Offset *count, const PIO_Offset *stride, const short *op); int PIOc_put_vars_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, const int *op); + const PIO_Offset *stride, const int *op); int PIOc_put_vars_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, const float *op); + const PIO_Offset *stride, const float *op); int PIOc_put_vars_double(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const PIO_Offset *stride, const double *op); + const PIO_Offset *count, const PIO_Offset *stride, const double *op); int PIOc_put_vars_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, const long *op); + const PIO_Offset *stride, const long *op); int PIOc_put_vars_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, const unsigned char *op); + const PIO_Offset *stride, const unsigned char *op); int PIOc_put_vars_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, const unsigned short *op); + const PIO_Offset *stride, const unsigned short *op); int PIOc_put_vars_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, const unsigned int *op); + const PIO_Offset *stride, const unsigned int *op); int PIOc_put_vars_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, const long long *op); + const PIO_Offset *stride, const long long *op); int PIOc_put_vars_ulonglong(int ncid, int varid, const PIO_Offset *start, - const PIO_Offset *count, const PIO_Offset *stride, - const unsigned long long *op); + const PIO_Offset *count, const PIO_Offset *stride, + const unsigned long long *op); /* Data reads - vard. */ int PIOc_get_vard(int ncid, int varid, int decompid, const PIO_Offset recnum, void *buf); int PIOc_get_vard_text(int ncid, int varid, int decompid, const PIO_Offset recnum, - char *buf); + char *buf); int PIOc_get_vard_schar(int ncid, int varid, int decompid, const PIO_Offset recnum, - signed char *buf); + signed char *buf); int PIOc_get_vard_short(int ncid, int varid, int decompid, const PIO_Offset recnum, - short *buf); + short *buf); int PIOc_get_vard_int(int ncid, int varid, int decompid, const PIO_Offset recnum, - int *buf); + int *buf); int PIOc_get_vard_float(int ncid, int varid, int decompid, const PIO_Offset recnum, - float *buf); + float *buf); int PIOc_get_vard_double(int ncid, int varid, int decompid, const PIO_Offset recnum, - double *buf); + double *buf); int PIOc_get_vard_uchar(int ncid, int varid, int decompid, const PIO_Offset recnum, - unsigned char *buf); + unsigned char *buf); int PIOc_get_vard_ushort(int ncid, int varid, int decompid, const PIO_Offset recnum, - unsigned short *buf); + unsigned short *buf); int PIOc_get_vard_uint(int ncid, int varid, int decompid, const PIO_Offset recnum, - unsigned int *buf); + unsigned int *buf); int PIOc_get_vard_longlong(int ncid, int varid, int decompid, const PIO_Offset recnum, - long long *buf); + long long *buf); int PIOc_get_vard_ulonglong(int ncid, int varid, int decompid, const PIO_Offset recnum, - unsigned long long *buf); + unsigned long long *buf); /* Data writes - vard. */ int PIOc_put_vard(int ncid, int varid, int decompid, const PIO_Offset recnum, - const void *buf); + const void *buf); int PIOc_put_vard_text(int ncid, int varid, int decompid, const PIO_Offset recnum, - const char *op); + const char *op); int PIOc_put_vard_schar(int ncid, int varid, int decompid, const PIO_Offset recnum, - const signed char *op); + const signed char *op); int PIOc_put_vard_short(int ncid, int varid, int decompid, const PIO_Offset recnum, - const short *op); + const short *op); int PIOc_put_vard_int(int ncid, int varid, int decompid, const PIO_Offset recnum, - const int *op); + const int *op); int PIOc_put_vard_float(int ncid, int varid, int decompid, const PIO_Offset recnum, - const float *op); + const float *op); int PIOc_put_vard_double(int ncid, int varid, int decompid, const PIO_Offset recnum, - const double *op); + const double *op); int PIOc_put_vard_uchar(int ncid, int varid, int decompid, const PIO_Offset recnum, - const unsigned char *op); + const unsigned char *op); int PIOc_put_vard_ushort(int ncid, int varid, int decompid, const PIO_Offset recnum, - const unsigned short *op); + const unsigned short *op); int PIOc_put_vard_uint(int ncid, int varid, int decompid, const PIO_Offset recnum, - const unsigned int *op); + const unsigned int *op); int PIOc_put_vard_longlong(int ncid, int varid, int decompid, const PIO_Offset recnum, - const long long *op); + const long long *op); int PIOc_put_vard_ulonglong(int ncid, int varid, int decompid, const PIO_Offset recnum, - const unsigned long long *op); + const unsigned long long *op); #ifdef NC_HAS_PAR_FILTERS int PIOc_def_var_filter(int ncid, int varid,unsigned int id, size_t nparams, unsigned int *params); @@ -1284,12 +1298,12 @@ extern "C" { /* These functions are for the netCDF integration layer. */ int nc_def_iosystem(MPI_Comm comp_comm, int num_iotasks, int stride, int base, int rearr, - int *iosysidp); + int *iosysidp); int nc_def_async(MPI_Comm world, int num_io_procs, int *io_proc_list, - int component_count, int *num_procs_per_comp, int **proc_list, - MPI_Comm *io_comm, MPI_Comm *comp_comm, int rearranger, - int *iosysidp); + int component_count, int *num_procs_per_comp, int **proc_list, + MPI_Comm *io_comm, MPI_Comm *comp_comm, int rearranger, + int *iosysidp); /* Set the default IOsystem ID. */ int nc_set_iosystem(int iosysid); @@ -1302,9 +1316,9 @@ extern "C" { /* Define a decomposition for distributed arrays. */ int nc_def_decomp(int iosysid, int pio_type, int ndims, const int *gdimlen, - int maplen, const size_t *compmap, int *ioidp, - int rearranger, const size_t *iostart, - const size_t *iocount); + int maplen, const size_t *compmap, int *ioidp, + int rearranger, const size_t *iostart, + const size_t *iocount); /* Release resources associated with a decomposition. */ int nc_free_decomp(int ioid); @@ -1312,54 +1326,68 @@ extern "C" { /* Data reads - read a distributed array. */ int nc_get_vard(int ncid, int varid, int decompid, const size_t recnum, void *buf); int nc_get_vard_text(int ncid, int varid, int decompid, const size_t recnum, - char *buf); + char *buf); int nc_get_vard_schar(int ncid, int varid, int decompid, const size_t recnum, - signed char *buf); + signed char *buf); int nc_get_vard_short(int ncid, int varid, int decompid, const size_t recnum, - short *buf); + short *buf); int nc_get_vard_int(int ncid, int varid, int decompid, const size_t recnum, - int *buf); + int *buf); int nc_get_vard_float(int ncid, int varid, int decompid, const size_t recnum, - float *buf); + float *buf); int nc_get_vard_double(int ncid, int varid, int decompid, const size_t recnum, - double *buf); + double *buf); int nc_get_vard_uchar(int ncid, int varid, int decompid, const size_t recnum, - unsigned char *buf); + unsigned char *buf); int nc_get_vard_ushort(int ncid, int varid, int decompid, const size_t recnum, - unsigned short *buf); + unsigned short *buf); int nc_get_vard_uint(int ncid, int varid, int decompid, const size_t recnum, - unsigned int *buf); + unsigned int *buf); int nc_get_vard_longlong(int ncid, int varid, int decompid, const size_t recnum, - long long *buf); + long long *buf); int nc_get_vard_ulonglong(int ncid, int varid, int decompid, const size_t recnum, - unsigned long long *buf); + unsigned long long *buf); /* Data writes - Write a distributed array. */ int nc_put_vard(int ncid, int varid, int decompid, const size_t recnum, - const void *buf); + const void *buf); int nc_put_vard_text(int ncid, int varid, int decompid, const size_t recnum, - const char *op); + const char *op); int nc_put_vard_schar(int ncid, int varid, int decompid, const size_t recnum, - const signed char *op); + const signed char *op); int nc_put_vard_short(int ncid, int varid, int decompid, const size_t recnum, - const short *op); + const short *op); int nc_put_vard_int(int ncid, int varid, int decompid, const size_t recnum, - const int *op); + const int *op); int nc_put_vard_float(int ncid, int varid, int decompid, const size_t recnum, - const float *op); + const float *op); int nc_put_vard_double(int ncid, int varid, int decompid, const size_t recnum, - const double *op); + const double *op); int nc_put_vard_uchar(int ncid, int varid, int decompid, const size_t recnum, - const unsigned char *op); + const unsigned char *op); int nc_put_vard_ushort(int ncid, int varid, int decompid, const size_t recnum, - const unsigned short *op); + const unsigned short *op); int nc_put_vard_uint(int ncid, int varid, int decompid, const size_t recnum, - const unsigned int *op); + const unsigned int *op); int nc_put_vard_longlong(int ncid, int varid, int decompid, const size_t recnum, - const long long *op); + const long long *op); int nc_put_vard_ulonglong(int ncid, int varid, int decompid, const size_t recnum, - const unsigned long long *op); - + const unsigned long long *op); +#ifdef PIO_ENABLE_GDAL + /* These functions are for the GDAL integration layer. MSL - 9/7/2023 */ + int GDALc_inq_fieldid(int fileid, const char *name, int *varidp); + int GDALc_inq_timeid(int fileid, int *timeid); // Is there a field of type OFTDate, OFTTime, or OFTDateTime? + int GDALc_openfile(int iosysid, int *fileIDp, GDALDatasetH *hDSp, int *iotype, const char *fname, bool mode); + int GDALc_sync(int fileid); + int GDALc_shp_get_int_field(int fileid); + int GDALc_shp_get_float_field(int fileid, int varid, const size_t *startp, const size_t *countp, io_desc_t *iodesc, float *ip, int rank); + int GDALc_shp_get_double_field(int fileid, int varid, const size_t *startp, const size_t *countp, double *ip, int rank); + int GDALc_shp_write_float_field(int fileid, int varid, const size_t *startp, const size_t *countp, float *ip); + int GDALc_def_field(int ncid, const char *name, nc_type xtype, int ndims, int *varidp); + int pio_read_darray_shp(file_desc_t *file, io_desc_t *iodesc, int vid, void *iobuf); + int pio_gdal_read_features_par(int fileid, int varid, io_desc_t *ddesc, float *ip); + int pio_read_darray_shp_par(file_desc_t *file, io_desc_t *iodesc, int vid, void *iobuf); +#endif #if defined(__cplusplus) } #endif diff --git a/src/clib/pio_darray.c b/src/clib/pio_darray.c index e7274da3e..d6cbf435b 100644 --- a/src/clib/pio_darray.c +++ b/src/clib/pio_darray.c @@ -181,7 +181,7 @@ PIOc_write_darray_multi(int ncid, const int *varids, int ioid, int nvars, /* Run these on all tasks if async is not in use, but only on * non-IO tasks if async is in use. */ - if (!ios->async || !ios->ioproc) + if ((!ios->async || !ios->ioproc) && (file->iotype != PIO_IOTYPE_GDAL)) { /* Get the number of dims for this var. */ PLOG((3, "about to call PIOc_inq_varndims varids[0] = %d", varids[0])); @@ -197,6 +197,12 @@ PIOc_write_darray_multi(int ncid, const int *varids, int ioid, int nvars, } + if ((!ios->async || !ios->ioproc) && (file->iotype == PIO_IOTYPE_GDAL)) + { + // Do we even need to sort out fndims here? (MSL<<>>) + fndims = 1; + } + /* If async is in use, and this is not an IO task, bcast the * parameters. */ if (ios->async) @@ -268,10 +274,11 @@ PIOc_write_darray_multi(int ncid, const int *varids, int ioid, int nvars, * large as the largest used to accommodate this serial io * method. */ rlen = 0; - if (iodesc->llen > 0 || - ((file->iotype == PIO_IOTYPE_NETCDF || - file->iotype == PIO_IOTYPE_NETCDF4C) && ios->iomain)) - rlen = iodesc->maxiobuflen * nvars; + if (iodesc->llen >0 || + ((file->iotype==PIO_IOTYPE_NETCDF || + file->iotype == PIO_IOTYPE_NETCDF4C) && + ios->iomain)) + rlen = iodesc->maxiobuflen * nvars; /* Allocate iobuf. */ if (rlen > 0) @@ -332,6 +339,12 @@ PIOc_write_darray_multi(int ncid, const int *varids, int ioid, int nvars, DARRAY_DATA, frame))) return pio_err(ios, file, ierr, __FILE__, __LINE__); + break; + case PIO_IOTYPE_GDAL: + if ((ierr = write_darray_multi_serial(file, nvars, fndims, varids, iodesc, + DARRAY_DATA, frame))) + return pio_err(ios, file, ierr, __FILE__, __LINE__); + break; default: return pio_err(NULL, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__); @@ -697,7 +710,7 @@ PIOc_write_darray(int ncid, int varid, int ioid, PIO_Offset arraylen, void *arra /* __FILE__, __LINE__); */ /* If we don't know the fill value for this var, get it. */ - if (!vdesc->fillvalue) + if (!vdesc->fillvalue && file->iotype != PIO_IOTYPE_GDAL) if ((ierr = find_var_fillvalue(file, varid, vdesc))) return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__); @@ -949,6 +962,14 @@ PIOc_read_darray(int ncid, int varid, int ioid, PIO_Offset arraylen, if ((ierr = pio_read_darray_nc(file, iodesc, varid, iobuf))) return pio_err(ios, file, ierr, __FILE__, __LINE__); break; +#ifdef PIO_ENABLE_GDAL + case PIO_IOTYPE_GDAL: + if ((ierr = pio_read_darray_shp_par(file, iodesc, varid, iobuf))) + return pio_err(ios, file, ierr, __FILE__, __LINE__); +// if ((ierr = pio_gdal_read_features_par(file->pio_ncid, varid, iodesc, iobuf))) +// return pio_err(ios, file, ierr, __FILE__, __LINE__); + break; +#endif default: return pio_err(NULL, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__); } diff --git a/src/clib/pio_darray_int.c b/src/clib/pio_darray_int.c index 880ad61a0..4faf656e1 100644 --- a/src/clib/pio_darray_int.c +++ b/src/clib/pio_darray_int.c @@ -995,9 +995,15 @@ recv_and_write_data(file_desc_t *file, const int *varids, const int *frame, #endif /* LOGGING */ /* Call the netCDF functions to write the data. */ - if (needtowrite) + if (needtowrite) { +#ifdef PIO_ENABLE_GDAL + if (file->iotype == PIO_IOTYPE_GDAL) + ierr = GDALc_shp_write_float_field(file->pio_ncid, varids[nv], start, count, bufptr); + else +#endif if ((ierr = nc_put_vara(file->fh, varids[nv], start, count, bufptr))) return check_netcdf2(ios, NULL, ierr, __FILE__, __LINE__); + } } /* next var */ diff --git a/src/clib/pio_file.c b/src/clib/pio_file.c index e9edb6af4..fdb691c01 100644 --- a/src/clib/pio_file.c +++ b/src/clib/pio_file.c @@ -282,6 +282,13 @@ PIOc_closefile(int ncid) } ierr = ncmpi_close(file->fh); break; +#endif +#ifdef PIO_ENABLE_GDAL + case PIO_IOTYPE_GDAL: + if (ios->io_rank == 0) { + GDALClose((void*)file->hDS); + } + break; #endif default: return pio_err(ios, file, PIO_EBADIOTYPE, __FILE__, __LINE__); @@ -470,6 +477,10 @@ PIOc_sync(int ncid) case PIO_IOTYPE_PNETCDF: flush_output_buffer(file, true, 0); break; +#endif +#ifdef PIO_ENABLE_GDAL + case PIO_IOTYPE_GDAL: + break; #endif default: return pio_err(ios, file, PIO_EBADIOTYPE, __FILE__, __LINE__); diff --git a/src/clib/pio_gdal.c b/src/clib/pio_gdal.c new file mode 100644 index 000000000..e57048e5b --- /dev/null +++ b/src/clib/pio_gdal.c @@ -0,0 +1,1244 @@ +#include +#include +#include +#ifdef PIO_ENABLE_GDAL +#include + +/** + * The PIO library maintains its own set of ncids. This is the next + * ncid number that will be assigned. + */ +extern int pio_next_ncid; + +static int +GDALc_inq_file_metadata(file_desc_t *file, GDALDatasetH hDS, int iotype, int *nvars, + int **rec_var, int **pio_type, int **pio_type_size, + MPI_Datatype **mpi_type, int **mpi_type_size, int **ndims) +{ + int mpierr; + int ret; + + /* Check inputs. */ + pioassert(rec_var && pio_type && pio_type_size && mpi_type && mpi_type_size, + "pointers must be provided", __FILE__, __LINE__); + + if (hDS == NULL) { + return pio_err(file->iosystem, file, -999, __FILE__, __LINE__); + } + /* How many vars in the file? */ + if (iotype == PIO_IOTYPE_GDAL) + { + OGRLayerH hLayer = OGR_DS_GetLayer( hDS, 0 ); + OGR_L_ResetReading(hLayer); + + OGRFeatureDefnH hFD = OGR_L_GetLayerDefn(hLayer); + *nvars = OGR_FD_GetFieldCount(hFD); + if (nvars == 0) // empty file + return pio_err(NULL, file, PIO_ENOMEM, __FILE__, __LINE__); + + /* Allocate storage for info about each var. */ + if (*nvars) + { + if (!(*rec_var = malloc(*nvars * sizeof(int)))) + return PIO_ENOMEM; + if (!(*pio_type = malloc(*nvars * sizeof(int)))) + return PIO_ENOMEM; + if (!(*pio_type_size = malloc(*nvars * sizeof(int)))) + return PIO_ENOMEM; + if (!(*mpi_type = malloc(*nvars * sizeof(MPI_Datatype)))) + return PIO_ENOMEM; + if (!(*mpi_type_size = malloc(*nvars * sizeof(int)))) + return PIO_ENOMEM; + if (!(*ndims = malloc(*nvars * sizeof(int)))) + return PIO_ENOMEM; + } + + /* Learn about each variable in the file. */ + for (int v = 0; v < *nvars; v++) + { + int var_ndims; /* Number of dims for this var. */ + nc_type my_type; + + /* Find type of the var and number of dims in this var. Also + * learn about type. */ + size_t type_size; + + // + var_ndims = 1; // FIXED FOR NOW. For data-read purposes, it's a 1D stream across the number of + // elements. + (*ndims)[v] = var_ndims; + //>> if ((ret = nc_inq_var(ncid, v, NULL, &my_type, &var_ndims, NULL, NULL))) + //>> return pio_err(NULL, file, ret, __FILE__, __LINE__); + OGRFieldType Fld = OGR_Fld_GetType(OGR_FD_GetFieldDefn(hFD,v)); + + bool typeOK = true; // assume we're good + switch (Fld) { + case OFTReal: + (*pio_type)[v] = (int)PIO_DOUBLE; + break; + case OFTInteger: + (*pio_type)[v] = (int)PIO_INT; + break; + // This needs to be done. How do we deal with timestamps etc in GDAL vector fields? + //>>case OFTDate: + //>> break; + //>> case OFTTime: + //>> break; + //>> case OFTDate: + //>> break; + //>> case OFTDateTime: + default: + typeOK = false; + break; +// return pio_err(file->iosystem, file, PIO_EBADIOTYPE, __FILE__, __LINE__); + } + //>> if ((ret = nc_inq_type(ncid, (*pio_type)[v], NULL, &type_size))) + //>> return check_netcdf(file, ret, __FILE__, __LINE__); + //>> (*pio_type_size)[v] = type_size; + + if (!typeOK) // Not a usable type + continue; + + /* Get the MPI type corresponding with the PIO type. */ + if ((ret = find_mpi_type((*pio_type)[v], &(*mpi_type)[v], NULL))) + return pio_err(NULL, file, ret, __FILE__, __LINE__); + + /* Get the size of the MPI type. */ + if ((*mpi_type)[v] == MPI_DATATYPE_NULL) + (*mpi_type_size)[v] = 0; + else + if ((mpierr = MPI_Type_size((*mpi_type)[v], &(*mpi_type_size)[v]))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + + } /* next var */ + } /* If PIO_TYPE_GDAL */ + return PIO_NOERR; +} + +/** + * The PIO-C interface for the GDAL function OGR_L_FindFieldIndex() + * + * This routine is called collectively by all tasks in the communicator + * ios.union_comm. + * + * @param ncid the ncid of the open file, obtained from + * GDALc_openfile() or GDALc_createfile(). + * @param name the field name. + * @param varidp a pointer that will get the variable id + * @return PIO_NOERR for success, error code otherwise. <> + * @ingroup PIO_inq_var_c + * @author Michael Long (adapted from Jim Edwards, Ed Hartnett) + */ +int +GDALc_inq_fieldid(int fileid, const char *name, int *fieldidp) +{ + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + int ierr; /* Return code from function calls. */ + int mpierr = MPI_SUCCESS, mpierr2; /* Return code from MPI function codes. */ + + /* Get file info based on fileid. */ + if ((ierr = pio_get_file(fileid, &file))) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + ios = file->iosystem; + + /* Caller must provide name. */ + if (!name || strlen(name) > NC_MAX_NAME) + return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__); + + PLOG((1, "GDALc_inq_fieldid fileid = %d name = %s", fileid, name)); + + if (ios->async) + { + if (!ios->ioproc) + { + int msg = PIO_MSG_INQ_VARID; + + if (ios->compmain == MPI_ROOT) + mpierr = MPI_Send(&msg, 1,MPI_INT, ios->ioroot, 1, ios->union_comm); + + if (!mpierr) + mpierr = MPI_Bcast(&fileid, 1, MPI_INT, ios->compmain, ios->intercomm); + int namelen; + namelen = strlen(name); + if (!mpierr) + mpierr = MPI_Bcast(&namelen, 1, MPI_INT, ios->compmain, ios->intercomm); + if (!mpierr) + mpierr = MPI_Bcast((void *)name, namelen + 1, MPI_CHAR, ios->compmain, ios->intercomm); + } + + /* Handle MPI errors. */ + if ((mpierr2 = MPI_Bcast(&mpierr, 1, MPI_INT, ios->comproot, ios->my_comm))) + check_mpi(NULL, file, mpierr2, __FILE__, __LINE__); + if (mpierr) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + } + + /* If this is an IO task, then call the GDAL function. */ + if (ios->ioproc) + { + GDALDatasetH *hDS = file->hDS; + if (file->do_io) { // We assume that its a GDAL file + OGRLayerH hLayer = OGR_DS_GetLayer( hDS, 0 ); + OGR_L_ResetReading(hLayer); + if (hLayer == NULL) { + printf("Layer is NULL"); + return -1; + } + *fieldidp = OGR_L_FindFieldIndex(hLayer,name,1); + +// pioassert(*fieldidp > 0, "variable not found", __FILE__, __LINE__); + + } + } + + /* Broadcast and check the return code. */ + if ((mpierr = MPI_Bcast(&ierr, 1, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if (ierr) + return check_netcdf(file, ierr, __FILE__, __LINE__); + + /* Broadcast results to all tasks. Ignore NULL parameters. */ + if (fieldidp) + if ((mpierr = MPI_Bcast(fieldidp, 1, MPI_INT, ios->ioroot, ios->my_comm))) + check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + + if (*fieldidp != 0) + return PIO_NOERR; + else + return -1; +} + +int +GDALc_openfile(int iosysid, int *fileIDp, GDALDatasetH *hDSp,int *iotype, const char *filename, bool mode) +//GDALc_openfile(int iosysid, GDALDatasetH *hDSp, int *iotype, const char *filename, bool mode) +{ + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + int imode; /* Internal mode val for netcdf4 file open. */ + int nvars = 0; + int *rec_var = NULL; + int *pio_type = NULL; + int *pio_type_size = NULL; + MPI_Datatype *mpi_type = NULL; + int *mpi_type_size = NULL; + int *ndims = NULL; + int mpierr = MPI_SUCCESS, mpierr2; /** Return code from MPI function codes. */ + int ierr = PIO_NOERR; /* Return code from function calls. */ + GDALDatasetH hDS; + +#ifdef USE_MPE + pio_start_mpe_log(OPEN); +#endif /* USE_MPE */ + + /* Get the IO system info from the iosysid. */ + if (!(ios = pio_get_iosystem_from_id(iosysid))) + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__); + + /* User must provide valid input for these parameters. */ + if (!hDSp || !iotype || !filename) + return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__); + if (*iotype != PIO_IOTYPE_GDAL ) + return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__); + +// PLOG((2, "PIOc_openfile_retry iosysid = %d iotype = %d filename = %s mode = %d retry = %d", +// iosysid, *iotype, filename, mode, retry)); + + /* Allocate space for the file info. */ + if (!(file = calloc(sizeof(*file), 1))) + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__); + + /* Fill in some file values. */ + file->fh = -1; + file->iotype = *iotype; + file->iosystem = ios; + file->writable = (mode & PIO_WRITE) ? 1 : 0; + + /* Set to true if this task should participate in IO (only true + * for one task with netcdf serial files. */ + if (file->iotype == PIO_IOTYPE_GDAL && ios->io_rank == 0) + file->do_io = 1; + + /* If async is in use, and this is not an IO task, bcast the parameters. */ + if (ios->async) + { + int msg = PIO_MSG_OPEN_FILE; + size_t len = strlen(filename); + + if (!ios->ioproc) + { + /* Send the message to the message handler. */ + if (ios->compmain == MPI_ROOT) + mpierr = MPI_Send(&msg, 1, MPI_INT, ios->ioroot, 1, ios->union_comm); + + /* Send the parameters of the function call. */ + if (!mpierr) + mpierr = MPI_Bcast(&len, 1, MPI_INT, ios->compmain, ios->intercomm); + if (!mpierr) + mpierr = MPI_Bcast((void *)filename, len + 1, MPI_CHAR, ios->compmain, ios->intercomm); + if (!mpierr) + mpierr = MPI_Bcast(&file->iotype, 1, MPI_INT, ios->compmain, ios->intercomm); + if (!mpierr) + mpierr = MPI_Bcast(&mode, 1, MPI_INT, ios->compmain, ios->intercomm); +//>> if (!mpierr) +//>> mpierr = MPI_Bcast(&use_ext_ncid, 1, MPI_INT, ios->compmain, ios->intercomm); + } + + /* Handle MPI errors. */ + if ((mpierr2 = MPI_Bcast(&mpierr, 1, MPI_INT, ios->comproot, ios->my_comm))) + return check_mpi(NULL, file, mpierr2, __FILE__, __LINE__); + if (mpierr) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + } + + /* If this is an IO task, then call the netCDF function. */ + if (1) //ios->ioproc) + { + switch (file->iotype) + { + case PIO_IOTYPE_GDAL: + if (1)//ios->io_rank == 0) + { + *hDSp = GDALOpenEx(filename, GDAL_OF_VECTOR | GDAL_OF_UPDATE, NULL, NULL, NULL); + //*hDSp = OGROpen( filename, mode, NULL ); + if( hDSp != NULL ) + ierr = GDALc_inq_file_metadata(file, *hDSp, PIO_IOTYPE_GDAL, + &nvars, &rec_var, &pio_type, + &pio_type_size, &mpi_type, + &mpi_type_size, &ndims); + PLOG((2, "GDALc_openfile:OGROpen for filename = %s mode = %d " + "ierr = %d", filename, mode, ierr)); + } + break; + + default: + return pio_err(ios, file, PIO_EBADIOTYPE, __FILE__, __LINE__); + } + + } + + /* Broadcast and check the return code. */ + if (ios->ioroot == ios->union_rank) + PLOG((2, "Bcasting error code ierr %d ios->ioroot %d ios->my_comm %d", + ierr, ios->ioroot, ios->my_comm)); + if ((mpierr = MPI_Bcast(&ierr, 1, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + PLOG((2, "Bcast openfile_retry error code ierr = %d", ierr)); + + /* If there was an error, free allocated memory and deal with the error. */ + if (ierr) + { + free(file); + return PIO_NOERR;// check_netcdf2(ios, NULL, ierr, __FILE__, __LINE__); + } + + /* Broadcast writability to all tasks. */ + if ((mpierr = MPI_Bcast(&file->writable, 1, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + + /* Broadcast some values to all tasks from io root. */ +//>> if (ios->async) +//>> { +//>> PLOG((3, "open bcasting pio_next_ncid %d ios->ioroot %d", pio_next_ncid, ios->ioroot)); +//>> if ((mpierr = MPI_Bcast(&pio_next_ncid, 1, MPI_INT, ios->ioroot, ios->my_comm))) +//>> return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); +//>> } + + if ((mpierr = MPI_Bcast(&nvars, 1, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + + /* Non io tasks need to allocate to store info about variables. */ + if (nvars && !rec_var) + { + if (!(rec_var = malloc(nvars * sizeof(int)))) + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__); + if (!(pio_type = malloc(nvars * sizeof(int)))) + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__); + if (!(pio_type_size = malloc(nvars * sizeof(int)))) + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__); + if (!(mpi_type = malloc(nvars * sizeof(MPI_Datatype)))) + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__); + if (!(mpi_type_size = malloc(nvars * sizeof(int)))) + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__); + if (!(ndims = malloc(nvars * sizeof(int)))) + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__); + } + if (nvars) + { + if ((mpierr = MPI_Bcast(rec_var, nvars, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(pio_type, nvars, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(pio_type_size, nvars, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(mpi_type, nvars*(int)(sizeof(MPI_Datatype)/sizeof(int)), MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(mpi_type_size, nvars, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(ndims, nvars, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + } + + file->hDS = *hDSp; + + /* Create the ncid/fileid that the user will see. This is necessary + * because otherwise ids will be reused if files are opened + * on multiple iosystems. */ + file->pio_ncid = pio_next_ncid++; + *fileIDp=file->pio_ncid; + + /* Add this file to the list of currently open files. */ + PLOG((2, "call pio_add_to_file_list()")); + pio_add_to_file_list(file); + + /* Add info about the variables to the file_desc_t struct. */ + for (int v = 0; v < nvars; v++) + if ((ierr = add_to_varlist(v, rec_var[v], pio_type[v], pio_type_size[v], + mpi_type[v], mpi_type_size[v], ndims[v], + &file->varlist))) + return pio_err(ios, NULL, ierr, __FILE__, __LINE__); + file->nvars = nvars; + + /* Free resources. */ + if (nvars) + { + if (rec_var) + free(rec_var); + if (pio_type) + free(pio_type); + if (pio_type_size) + free(pio_type_size); + if (mpi_type) + free(mpi_type); + if (mpi_type_size) + free(mpi_type_size); + if (ndims) + free(ndims); + } + +#ifdef USE_MPE + pio_stop_mpe_log(OPEN, __func__); +#endif /* USE_MPE */ + PLOG((2, "Opened file %s file->pio_ncid = %d file->fh = %d ierr = %d", + filename, file->pio_ncid, file->fh, ierr)); + + return ierr; +} + +int +GDALc_sync(int fileid) { + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + int mpierr = MPI_SUCCESS, mpierr2; /* Return code from MPI function codes. */ + int ierr = PIO_NOERR; /* Return code from function calls. */ +} + +int GDALc_inq_timeid(int fileid, int *timeid) { // Is there a field of type OFTDate, OFTTime, or OFTDateTime? + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + int ierr = PIO_NOERR; /* Return code from function calls. */ + int mpierr = MPI_SUCCESS, mpierr2; /* Return code from MPI function codes. */ + + /* Get file info based on ncid. */ + if ((ierr = pio_get_file(fileid, &file))) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + ios = file->iosystem; + if (file->hDS == NULL) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + + GDALDatasetH *hDS = file->hDS; + OGRLayerH hLayer = OGR_DS_GetLayer( hDS, 0 ); + if (hLayer == NULL) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + OGR_L_ResetReading(hLayer); + + OGRFeatureDefnH hFD = OGR_L_GetLayerDefn(hLayer); + if (hFD == NULL) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + int nFld = OGR_FD_GetFieldCount(hFD); + OGRFieldDefnH hTMP = OGR_FD_GetFieldDefn(hFD,1); + hTMP = OGR_FD_GetFieldDefn(hFD,0); + + for (int i=0;i<(file->nvars)-1;i++) { + OGRFieldDefnH hFlD = OGR_FD_GetFieldDefn(hFD,i); + OGRFieldType Fld = OGR_Fld_GetType(hFlD); + } + +// OGR_FD_Destroy(hFD); + + return 0; + } + +/** + * Read an array of data from a file to the (serial) IO library. This + * function is only used with netCDF classic and netCDF-4 serial + * iotypes. + * + * @param file a pointer to the open file descriptor for the file + * that will be written to + * @param iodesc a pointer to the defined iodescriptor for the buffer + * @param vid the variable id to be read. + * @param iobuf the buffer to be written from this mpi task. May be + * null. for example we have 8 ionodes and a distributed array with + * global size 4, then at least 4 nodes will have a null iobuf. In + * practice the box rearranger trys to have at least blocksize bytes + * on each io task and so if the total number of bytes to write is + * less than blocksize * numiotasks then some iotasks will have a NULL + * iobuf. + * @returns 0 for success, error code otherwise. + * @ingroup PIO_read_darray_c + * @author Jim Edwards, Ed Hartnett + */ +int +pio_read_darray_shp(file_desc_t *file, io_desc_t *iodesc, int vid, + void *iobuf) +{ + iosystem_desc_t *ios; /* Pointer to io system information. */ + var_desc_t *vdesc; /* Information about the variable. */ + int ndims; /* Number of dims in decomposition. */ + int fndims; /* Number of dims for this var in file. */ + MPI_Status status; + int mpierr; /* Return code from MPI functions. */ + int ierr; + + /* Check inputs. */ + pioassert(file && file->iosystem && iodesc && vid >= 0 && vid <= PIO_MAX_VARS, + "invalid input", __FILE__, __LINE__); + + PLOG((2, "pio_read_darray_shp vid = %d", vid)); + ios = file->iosystem; + +#ifdef TIMING + /* Start timer if desired. */ + if ((ierr = pio_start_timer("PIO:read_darray_shp"))) + return pio_err(ios, NULL, ierr, __FILE__, __LINE__); +#endif /* TIMING */ + + /* Get var info for this var. */ + if ((ierr = get_var_desc(vid, &file->varlist, &vdesc))) + return pio_err(NULL, file, ierr, __FILE__, __LINE__); + + /* Get the number of dims in our decomposition. */ + ndims = iodesc->ndims; + + /* Get number of dims for this var. */ + fndims = vdesc->ndims; + + /* If setframe was not called, use a default value of 0. This is + * required for backward compatibility. */ + if (fndims == ndims + 1 && vdesc->record < 0) + vdesc->record = 0; + PLOG((3, "fndims %d ndims %d vdesc->record %d vdesc->ndims %d", fndims, + ndims, vdesc->record, vdesc->ndims)); + + /* Confirm that we are being called with the correct ndims. */ + pioassert((fndims == ndims && vdesc->record < 0) || + (fndims == ndims + 1 && vdesc->record >= 0), + "unexpected record", __FILE__, __LINE__); + + if (ios->ioproc) + { + io_region *region; + size_t start[fndims]; + size_t count[fndims]; + size_t tmp_start[fndims * iodesc->maxregions]; + size_t tmp_count[fndims * iodesc->maxregions]; + size_t tmp_bufsize; + void *bufptr; + + /* buffer is incremented by byte and loffset is in terms of + the iodessc->mpitype so we need to multiply by the size of + the mpitype. */ + region = iodesc->firstregion; + + /* If setframe was not set before this call, assume a value of + * 0. This is required for backward compatibility. */ + if (fndims > ndims) + if (vdesc->record < 0) + vdesc->record = 0; + + /* Put together start/count arrays for all regions. */ + for (int regioncnt = 0; regioncnt < iodesc->maxregions; regioncnt++) + { + if (!region || iodesc->llen == 0) + { + /* Nothing to write for this region. */ + for (int i = 0; i < fndims; i++) + { + tmp_start[i + regioncnt * fndims] = 0; + tmp_count[i + regioncnt * fndims] = 0; + } + bufptr = NULL; + } + else + { + if (vdesc->record >= 0 && fndims > 1) + { + /* This is a record var. Find start for record dims. */ + tmp_start[regioncnt * fndims] = vdesc->record; + + /* Find start/count for all non-record dims. */ + for (int i = 1; i < fndims; i++) + { + tmp_start[i + regioncnt * fndims] = region->start[i - 1]; + tmp_count[i + regioncnt * fndims] = region->count[i - 1]; + } + + /* Set count for record dimension. */ + if (tmp_count[1 + regioncnt * fndims] > 0) + tmp_count[regioncnt * fndims] = 1; + } + else + { + /* Non-time dependent array */ + for (int i = 0; i < fndims; i++) + { + tmp_start[i + regioncnt * fndims] = region->start[i]; + tmp_count[i + regioncnt * fndims] = region->count[i]; + } + } + } + +#if PIO_ENABLE_LOGGING + /* Log arrays for debug purposes. */ + PLOG((3, "region = %d", region)); + for (int i = 0; i < fndims; i++) + PLOG((3, "tmp_start[%d] = %d tmp_count[%d] = %d", i + regioncnt * fndims, tmp_start[i + regioncnt * fndims], + i + regioncnt * fndims, tmp_count[i + regioncnt * fndims])); +#endif /* PIO_ENABLE_LOGGING */ + + /* Move to next region. */ + if (region) + region = region->next; + } /* next regioncnt */ + + /* IO tasks other than 0 send their starts/counts and data to + * IO task 0. */ + if (ios->io_rank > 0) + { + if ((mpierr = MPI_Send(&iodesc->llen, 1, MPI_OFFSET, 0, ios->io_rank, ios->io_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + PLOG((3, "sent iodesc->llen = %d", iodesc->llen)); + + if (iodesc->llen > 0) + { + if ((mpierr = MPI_Send(&(iodesc->maxregions), 1, MPI_INT, 0, + ios->num_iotasks + ios->io_rank, ios->io_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Send(tmp_count, iodesc->maxregions * fndims, MPI_OFFSET, 0, + 2 * ios->num_iotasks + ios->io_rank, ios->io_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Send(tmp_start, iodesc->maxregions * fndims, MPI_OFFSET, 0, + 3 * ios->num_iotasks + ios->io_rank, ios->io_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + PLOG((3, "sent iodesc->maxregions = %d tmp_count and tmp_start arrays", iodesc->maxregions)); + + if ((mpierr = MPI_Recv(iobuf, iodesc->llen, iodesc->mpitype, 0, + 4 * ios->num_iotasks + ios->io_rank, ios->io_comm, &status))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + PLOG((3, "received %d elements of data", iodesc->llen)); + } + } + else if (ios->io_rank == 0) + { + /* This is IO task 0. Get starts/counts and data from + * other IO tasks. */ + int maxregions = 0; + size_t loffset, regionsize; + size_t this_start[fndims * iodesc->maxregions]; + size_t this_count[fndims * iodesc->maxregions]; + + for (int rtask = 1; rtask <= ios->num_iotasks; rtask++) + { + if (rtask < ios->num_iotasks) + { + if ((mpierr = MPI_Recv(&tmp_bufsize, 1, MPI_OFFSET, rtask, rtask, ios->io_comm, &status))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + PLOG((3, "received tmp_bufsize = %d", tmp_bufsize)); + + if (tmp_bufsize > 0) + { + if ((mpierr = MPI_Recv(&maxregions, 1, MPI_INT, rtask, ios->num_iotasks + rtask, + ios->io_comm, &status))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Recv(this_count, maxregions * fndims, MPI_OFFSET, rtask, + 2 * ios->num_iotasks + rtask, ios->io_comm, &status))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Recv(this_start, maxregions * fndims, MPI_OFFSET, rtask, + 3 * ios->num_iotasks + rtask, ios->io_comm, &status))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + PLOG((3, "received maxregions = %d this_count, this_start arrays ", maxregions)); + } + } + else + { + maxregions = iodesc->maxregions; + tmp_bufsize = iodesc->llen; + } + PLOG((3, "maxregions = %d tmp_bufsize = %d", maxregions, tmp_bufsize)); + + /* Now get each region of data. */ + loffset = 0; + for (int regioncnt = 0; regioncnt < maxregions; regioncnt++) + { + /* Get pointer where data should go. */ + bufptr = (void *)((char *)iobuf + iodesc->mpitype_size * loffset); + regionsize = 1; + + /* ??? */ + if (rtask < ios->num_iotasks) + { + for (int m = 0; m < fndims; m++) + { + start[m] = this_start[m + regioncnt * fndims]; + count[m] = this_count[m + regioncnt * fndims]; + regionsize *= count[m]; + } + } + else + { + for (int m = 0; m < fndims; m++) + { + start[m] = tmp_start[m + regioncnt * fndims]; + count[m] = tmp_count[m + regioncnt * fndims]; + regionsize *= count[m]; + } + } + loffset += regionsize; + + /* Read the data. */ + switch (iodesc->piotype) + { + case PIO_BYTE: + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__); + case PIO_CHAR: + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__); + case PIO_SHORT: + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__); + case PIO_INT: + ierr = GDALc_shp_get_int_field(file->pio_ncid); + break; + case PIO_FLOAT: + ierr = GDALc_shp_get_float_field(file->pio_ncid, vid, start, count, iodesc ,(float *)bufptr, ios->io_rank); + break; + case PIO_DOUBLE: + ierr = GDALc_shp_get_double_field(file->pio_ncid, vid, start, count, (double *)bufptr, ios->io_rank); + break; + default: + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__); + } + + /* Check error code of netCDF call. */ + if (ierr) + return check_netcdf(file, ierr, __FILE__, __LINE__); + } + + /* The decomposition may not use all of the active io + * tasks. rtask here is the io task rank and + * ios->num_iotasks is the number of iotasks actually + * used in this decomposition. */ + if (rtask < ios->num_iotasks && tmp_bufsize > 0){ + if ((mpierr = MPI_Send(iobuf, tmp_bufsize, iodesc->mpitype, rtask, + 4 * ios->num_iotasks + rtask, ios->io_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + } + } + } + } + +#ifdef TIMING + if ((ierr = pio_stop_timer("PIO:read_darray_nc_serial"))) + return pio_err(ios, NULL, ierr, __FILE__, __LINE__); +#endif /* TIMING */ + + PLOG((2, "pio_read_darray_shp complete ierr %d", ierr)); + return PIO_NOERR; +} + +int +GDALc_shp_get_int_field(int fileid) +{ + return PIO_NOERR; +} +int +GDALc_shp_get_double_field(int fileid, int varid, const size_t *startp, + const size_t *countp, double *ip, int rank) +{ + OGRFeatureH hF; + file_desc_t *file; /* Pointer to file information. */ + int ierr; + + /* Get file info based on fileid. */ + if ((ierr = pio_get_file(fileid, &file))) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + if (file->hDS == NULL) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + + OGRLayerH hL = OGR_DS_GetLayer( file->hDS, 0 ); + + // here, we have to assume start and count are only one dimension, and have + // only one assigned value. + for (size_t i = startp[0]; ihDS == NULL) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + + OGRLayerH hL = OGR_DS_GetLayer( file->hDS, 0 ); + OGR_L_ResetReading(hL); + + float total = 0.; + + // here, we have to assume start and count are only one dimension, and have + // only one assigned value. + // This is NOT efficient. We need to pre-read the FIDs and make them + // available here. MSL + for (size_t i = 0; ihDS == NULL) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + +//>>> // -- 1 & 2 skip for now. We have to assume the shpfile +//>>> // exists since we can't write mesh yet. +//>>> // 1. Set CRS/spatial reference +//>>> // 2. Get/set driver +//>>> // 3. Get to Dataset +//>>> // 4. Get to layer + OGRLayerH hL = OGR_DS_GetLayer( file->hDS, 0 ); + // 5. Query &/or add field + // 6. Loop over features and add data + + // here, we have to assume start and count are only one dimension, and have + // only one assigned value. + for (size_t i = startp[0]; i> ip[%d]=%f\n",i,ip[i]); + } + + return PIO_NOERR; +} + +/** + * The PIO-C interface for the NetCDF function nc_def_var + * + * This routine is called collectively by all tasks in the communicator + * ios.union_comm. For more information on the underlying NetCDF commmand + * please read about this function in the NetCDF documentation at: + * http://www.unidata.ucar.edu/software/netcdf/docs/group__variables.html + * + * @param ncid the ncid of the open file, obtained from + * PIOc_openfile() or PIOc_createfile(). + * @param name the variable name. + * @param xtype the PIO_TYPE of the variable. + * @param ndims the number of dimensions. + * @return PIO_NOERR for success, error code otherwise. + * @ingroup PIO_def_var_c + * @author Jim Edwards, Ed Hartnett + */ +int +GDALc_def_field(int ncid, const char *name, nc_type xtype, int ndims, int *varidp) +{ + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + int invalid_unlim_dim = 0; /* True invalid dims are used. */ + int varid; /* The varid of the created var. */ + int rec_var = 0; /* Non-zero if this var uses unlimited dim. */ + PIO_Offset pio_type_size; /* Size of pio type in bytes. */ + MPI_Datatype mpi_type; /* The correspoding MPI type. */ + int mpi_type_size; /* Size of mpi type. */ + int mpierr = MPI_SUCCESS, mpierr2; /* Return code from MPI function codes. */ + int ierr; /* Return code from function calls. */ + + /* Get the file information. */ + if ((ierr = pio_get_file(ncid, &file))) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + ios = file->iosystem; + + /* User must provide name. */ + if (!name || strlen(name) > NC_MAX_NAME) + return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__); + + PLOG((1, "GDALc_def_var ncid = %d name = %s xtype = %d ndims = %d", ncid, name, + xtype, ndims)); + + /* Run this on all tasks if async is not in use, but only on + * non-IO tasks if async is in use. Learn whether each dimension + * is unlimited. */ + if (!ios->async || !ios->ioproc) + { + int nunlimdims; + +//>>> /* Get size of type. */ +//>>> if ((ierr = PIOc_inq_type(ncid, xtype, NULL, &pio_type_size))) +//>>> return check_netcdf(file, ierr, __FILE__, __LINE__); + + /* Get the MPI type corresponding with the PIO type. */ + if ((ierr = find_mpi_type(xtype, &mpi_type, NULL))) + return pio_err(ios, NULL, ierr, __FILE__, __LINE__); + + /* Get the size of the MPI type. */ + if(mpi_type == MPI_DATATYPE_NULL) + mpi_type_size = 0; + else + if ((mpierr = MPI_Type_size(mpi_type, &mpi_type_size))) + return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); + +//>>> /* How many unlimited dims are present in the file? */ +//>>> if ((ierr = PIOc_inq_unlimdims(ncid, &nunlimdims, NULL))) +//>>> return check_netcdf(file, ierr, __FILE__, __LINE__); + + } + + /* If using async, and not an IO task, then send parameters. */ + if (ios->async) + { + if (!ios->ioproc) + { + int msg = PIO_MSG_DEF_VAR; + int namelen = strlen(name); + + if (ios->compmain == MPI_ROOT) + mpierr = MPI_Send(&msg, 1, MPI_INT, ios->ioroot, 1, ios->union_comm); + + if (!mpierr) + mpierr = MPI_Bcast(&(ncid), 1, MPI_INT, ios->compmain, ios->intercomm); + if (!mpierr) + mpierr = MPI_Bcast(&namelen, 1, MPI_INT, ios->compmain, ios->intercomm); + if (!mpierr) + mpierr = MPI_Bcast((void *)name, namelen + 1, MPI_CHAR, ios->compmain, ios->intercomm); + if (!mpierr) + mpierr = MPI_Bcast(&xtype, 1, MPI_INT, ios->compmain, ios->intercomm); + if (!mpierr) + mpierr = MPI_Bcast(&ndims, 1, MPI_INT, ios->compmain, ios->intercomm); + } + + /* Handle MPI errors. */ + if ((mpierr2 = MPI_Bcast(&mpierr, 1, MPI_INT, ios->comproot, ios->my_comm))) + check_mpi(NULL, file, mpierr2, __FILE__, __LINE__); + if (mpierr) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + + /* Broadcast values currently only known on computation tasks to IO tasks. */ + if ((mpierr = MPI_Bcast(&rec_var, 1, MPI_INT, ios->comproot, ios->my_comm))) + check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(&invalid_unlim_dim, 1, MPI_INT, ios->comproot, ios->my_comm))) + check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(&pio_type_size, 1, MPI_OFFSET, ios->comproot, ios->my_comm))) + check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(&mpi_type, 1, MPI_INT, ios->comproot, ios->my_comm))) + check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if ((mpierr = MPI_Bcast(&mpi_type_size, 1, MPI_INT, ios->comproot, ios->my_comm))) + check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + } + + /* Check that only one unlimited dim is specified, and that it is + * first. */ + if (invalid_unlim_dim) + return PIO_EINVAL; + + /* If this is an IO task, then call the GDAL/OGR function. */ + if (ios->ioproc) + { + if (file->iotype == PIO_IOTYPE_GDAL && file->do_io) { + OGRLayerH hL = OGR_DS_GetLayer( file->hDS, 0 ); + // 5. Create & add field + OGRFieldDefnH hFieldDefn = OGR_Fld_Create( name, OFTReal ); + OGR_Fld_SetWidth( hFieldDefn, 32); + OGR_Fld_SetPrecision( hFieldDefn, 8); + if( OGR_L_CreateField( hL, hFieldDefn, TRUE ) != OGRERR_NONE ) + { + printf( "Creating Name field failed.\n" ); + exit( 1 ); + } + OGR_Fld_Destroy(hFieldDefn); + varid = OGR_L_FindFieldIndex(hL,name,1); + PLOG((3, "defined var ierr %d file->iotype %d", ierr, file->iotype)); + } + } + + /* Broadcast and check the return code. */ + if ((mpierr = MPI_Bcast(&ierr, 1, MPI_INT, ios->ioroot, ios->my_comm))) + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); +//>>> if (ierr) +//>>> return check_netcdf(file, ierr, __FILE__, __LINE__); + + /* Broadcast results. */ + if ((mpierr = MPI_Bcast(&varid, 1, MPI_INT, ios->ioroot, ios->my_comm))) + check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + if (varidp) + *varidp = varid; + + /* Add to the list of var_desc_t structs for this file. */ + if ((ierr = add_to_varlist(varid, rec_var, xtype, (int)pio_type_size, mpi_type, + mpi_type_size, ndims, &file->varlist))) + return pio_err(ios, NULL, ierr, __FILE__, __LINE__); + file->nvars++; + + return PIO_NOERR; +} +/** + * Read decomposed features from a shapefile using GDAL. + * + * @param fileid The PIO file ID. + * @param varid Variable ID (not used here but included for consistency). + * @param ddesc Decomposition descriptor (defines the data layout per rank). + * @param ip Pointer to the buffer to fill (should be of size ddesc->ndof). + * @returns PIO_NOERR on success or error code. + */ +int pio_gdal_read_features_par(int fileid, int varid, io_desc_t *ddesc, float *ip) +{ + file_desc_t *file; + int ierr; + + PLOG((3, "in read features, fileid is %d, io size is %d", fileid, sizeof(ip))); + // Lookup the file descriptor + if ((ierr = pio_get_file(fileid, &file))) + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__); + + // Ensure GDAL dataset is open + if (file->hDS == NULL) + return pio_err(NULL, NULL, PIO_EINVAL, __FILE__, __LINE__); + + // Get the first layer (assumes 1 layer per shapefile) + OGRLayerH hLayer = OGR_DS_GetLayer(file->hDS, 0); + if (hLayer == NULL) + return pio_err(NULL, NULL, PIO_EINVAL, __FILE__, __LINE__); + + // Loop over the decomposition indices for this rank + for (int i = 0; i < ddesc->ndof; i++) + { + // Global feature index assigned to this position + PIO_Offset gindex = ddesc->sindex[i]; + + OGRFeatureH hFeature = OGR_L_GetFeature(hLayer, gindex); + if (!hFeature) + return pio_err(NULL, NULL, PIO_EINVAL, __FILE__, __LINE__); + + // Extract first field value as float + OGRFieldDefnH hField = OGR_F_GetFieldDefnRef(hFeature, 0); + if (hField && OGR_Fld_GetType(hField) == OFTReal) + ip[i] = (float)OGR_F_GetFieldAsDouble(hFeature, 0); + else + ip[i] = 0.0; // Default fallback + + OGR_F_Destroy(hFeature); + } + + return PIO_NOERR; +} + +int +pio_read_darray_shp_par(file_desc_t *file, io_desc_t *iodesc, int vid, void *iobuf) +{ + iosystem_desc_t *ios; /* Pointer to io system information. */ + var_desc_t *vdesc; /* Information about the variable. */ + int ndims; /* Number of dims in decomposition. */ + int fndims; /* Number of dims for this var in file. */ + int ierr; /* Return code from netCDF functions. */ +#ifdef USE_VARD_READ + MPI_Offset gdim0; + gdim0 = 0; +#endif + + /* Check inputs. */ + pioassert(file && file->iosystem && iodesc && vid <= PIO_MAX_VARS, "invalid input", + __FILE__, __LINE__); + + /* Get the IO system info. */ + ios = file->iosystem; + PLOG((3, "pio_read_darray_shp_par ios->ioproc %d", ios->ioproc)); + +#ifdef TIMING + /* Start timer if desired. */ + if ((ierr = pio_start_timer("PIO:read_darray_shp_par"))) + return pio_err(ios, NULL, ierr, __FILE__, __LINE__); +#endif /* TIMING */ + + /* Get the variable info. */ + if ((ierr = get_var_desc(vid, &file->varlist, &vdesc))) + return pio_err(NULL, file, ierr, __FILE__, __LINE__); + + /* Get the number of dimensions in the decomposition. */ + ndims = iodesc->ndims; + + /* Get the number of dims for this var in the file. */ + fndims = vdesc->ndims; + PLOG((4, "fndims %d ndims %d", fndims, ndims)); + + /* ??? */ +#if USE_VARD_READ + if(!ios->async || !ios->ioproc) + ierr = get_gdim0(file, iodesc, vid, fndims, &gdim0); +#endif + + /* IO procs will read the data. */ + if (ios->ioproc) + { + io_region *region; + size_t start[fndims]; + size_t count[fndims]; + void *bufptr; + + /* buffer is incremented by byte and loffset is in terms of + the iodessc->mpitype so we need to multiply by the size of + the mpitype. */ + region = iodesc->firstregion; + + /* There are different numbers of dims in the decomposition + * and the file. */ + if (fndims > ndims) + { + /* If the user did not call setframe, use a default frame + * of 0. This is required for backward compatibility. */ + if (vdesc->record < 0) + vdesc->record = 0; + } + + /* For each regions, read the data. */ + for (int regioncnt = 0; regioncnt < iodesc->maxregions; regioncnt++) + { + if (region == NULL || iodesc->llen == 0) + { + /* No data for this region. */ + for (int i = 0; i < fndims; i++) + { + start[i] = 0; + count[i] = 0; + } + bufptr = NULL; + } + else + { + /* Get a pointer where we should put the data we read. */ + if (regioncnt == 0 || region == NULL) + bufptr = iobuf; + else + bufptr=(void *)((char *)iobuf + iodesc->mpitype_size * region->loffset); + + PLOG((2, "iodesc->llen - region->loffset %d, iodesc->llen %d, region->loffset %d vdesc->record %d", + iodesc->llen - region->loffset, iodesc->llen, region->loffset, vdesc->record)); + + /* Get the start/count arrays. */ + if (vdesc->record >= 0 && fndims > 1) + { + /* This is a record var. The unlimited dimension + * (0) is handled specially. */ + start[0] = vdesc->record; + for (int i = 1; i < fndims; i++) + { + start[i] = region->start[i-1]; + count[i] = region->count[i-1]; + } + + /* Read one record. */ + if (count[1] > 0) + count[0] = 1; + } + else + { + /* Non-time dependent array */ + for (int i = 0; i < fndims; i++) + { + start[i] = region->start[i]; + count[i] = region->count[i]; + } + } + } + +#ifdef PIO_ENABLE_LOGGING + for (int i = 1; i < ndims; i++) + PLOG((3, "gdal: start[%d] %d count[%d] %d", i, start[i], i, count[i])); + PLOG((3, "piotype: %d (%d, %d)", iodesc->piotype, PIO_FLOAT, PIO_DOUBLE)); +#endif /* LOGGING */ + /* Do the read. */ + switch (iodesc->piotype) + { + case PIO_BYTE: + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__); + case PIO_CHAR: + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__); + case PIO_SHORT: + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__); + case PIO_INT: + ierr = GDALc_shp_get_int_field(file->pio_ncid); + break; + case PIO_FLOAT: + ierr = GDALc_shp_get_float_field(file->pio_ncid, vid, start, count, iodesc, (float *)bufptr, ios->io_rank); + break; + case PIO_DOUBLE: + ierr = GDALc_shp_get_double_field(file->pio_ncid, vid, start, count, (double *)bufptr, ios->io_rank); + break; + default: + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__); + } + /* Check return code. */ + if (ierr) + return check_netcdf(file, ierr, __FILE__,__LINE__); + + /* Move to next region. */ + if (region) + region = region->next; + } /* next regioncnt */ + } + +#ifdef TIMING + if ((ierr = pio_stop_timer("PIO:read_darray_shp_par"))) + return pio_err(ios, NULL, ierr, __FILE__, __LINE__); +#endif /* TIMING */ + + return PIO_NOERR; +} + +/** + * @} + */ +#endif diff --git a/src/clib/pio_internal.h b/src/clib/pio_internal.h index 620ce4d18..5d7e3db71 100644 --- a/src/clib/pio_internal.h +++ b/src/clib/pio_internal.h @@ -91,6 +91,11 @@ #define PIO_DATATYPE_NULL MPI_CHAR +#ifdef PIO_ENABLE_GDAL +int +pio_read_darray_shp(file_desc_t *file, io_desc_t *iodesc, int vid, + void *iobuf); +#endif #if PIO_ENABLE_LOGGING void pio_log(int severity, const char *fmt, ...); diff --git a/src/clib/pio_lists.c b/src/clib/pio_lists.c index 9340fe600..2151fd2b0 100644 --- a/src/clib/pio_lists.c +++ b/src/clib/pio_lists.c @@ -29,6 +29,8 @@ pio_add_to_file_list(file_desc_t *file) /* Keep a global pointer to the current file. */ current_file = file; + PLOG((2, "pio_add_to_file_list")); + /* Add file to list. */ HASH_ADD_INT(pio_file_list, pio_ncid, file); } diff --git a/src/clib/pioc_support.c b/src/clib/pioc_support.c index c91391969..b67d628cb 100644 --- a/src/clib/pioc_support.c +++ b/src/clib/pioc_support.c @@ -3137,7 +3137,10 @@ iotype_is_valid(int iotype) if (iotype == PIO_IOTYPE_PNETCDF) ret++; #endif /* _PNETCDF */ - +#ifdef PIO_ENABLE_GDAL + if (iotype == PIO_IOTYPE_GDAL) + ret++; +#endif return ret; } diff --git a/tests/cunit/CMakeLists.txt b/tests/cunit/CMakeLists.txt index 004197a15..d63d8be06 100644 --- a/tests/cunit/CMakeLists.txt +++ b/tests/cunit/CMakeLists.txt @@ -27,6 +27,11 @@ endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") + +set(CMAKE_MACOSX_RPATH 1) +if(PIO_ENABLE_GDAL) + set(CMAKE_BUILD_RPATH ${GDAL_LIBRARY_PATH}) +endif() #============================================================================== # PREPARE FOR TESTING #============================================================================== @@ -123,8 +128,12 @@ if (NOT PIO_USE_MPISERIAL) target_link_libraries (test_async_1d pioc) add_executable (test_simple EXCLUDE_FROM_ALL test_simple.c test_common.c) target_link_libraries (test_simple pioc) - add_executable (test_async_perf EXCLUDE_FROM_ALL test_async_perf.c test_common.c) - target_link_libraries(test_async_perf pioc) +# add_executable (test_async_perf EXCLUDE_FROM_ALL test_async_perf.c test_common.c) +# target_link_libraries(test_async_perf pioc) + if(PIO_ENABLE_GDAL) + add_executable (test_gdal EXCLUDE_FROM_ALL test_gdal.c test_common.c) + target_link_libraries (test_gdal pioc) + endif() endif () add_executable (test_spmd EXCLUDE_FROM_ALL test_spmd.c test_common.c) target_link_libraries (test_spmd pioc) @@ -158,8 +167,10 @@ add_dependencies (tests test_async_multi2) add_dependencies (tests test_async_manyproc) add_dependencies (tests test_async_1d) add_dependencies (tests test_simple) -add_dependencies (tests test_async_perf) - +#add_dependencies (tests test_async_perf) +if(PIO_ENABLE_GDAL) + add_dependencies (tests test_gdal) +endif() # Test Timeout in seconds. if (PIO_VALGRIND_CHECK) set (DEFAULT_TEST_TIMEOUT 800) @@ -342,9 +353,20 @@ else () EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_simple NUMPROCS ${EXACTLY_FOUR_TASKS} TIMEOUT ${DEFAULT_TEST_TIMEOUT}) - # add_mpi_test(test_async_perf - # EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_async_perf - # NUMPROCS ${EXACTLY_FOUR_TASKS} - # TIMEOUT ${DEFAULT_TEST_TIMEOUT}) +# add_mpi_test(test_async_perf +# EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_async_perf +# NUMPROCS ${EXACTLY_FOUR_TASKS} +# TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + if(PIO_ENABLE_GDAL) + add_custom_target(link_target ALL + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${CMAKE_CURRENT_SOURCE_DIR}/data ${CMAKE_CURRENT_BINARY_DIR}/data) + add_mpi_test(test_gdal + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_gdal + NUMPROCS ${EXACTLY_FOUR_TASKS} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + add_dependencies(test_gdal link_target) + +endif() endif () MESSAGE("CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}") diff --git a/tests/cunit/Makefile.am b/tests/cunit/Makefile.am index f7afb15a6..e09714d71 100644 --- a/tests/cunit/Makefile.am +++ b/tests/cunit/Makefile.am @@ -21,7 +21,9 @@ test_async_multicomp test_async_multi2 test_async_manyproc \ test_darray_fill test_decomp_frame test_perf2 test_async_perf \ test_darray_vard test_async_1d test_darray_append test_simple \ test_darray_lossycompress - +if PIO_ENABLE_GDAL + check_PROGRAMS += test_gdal +endif if RUN_TESTS # Tests will run from a bash script. TESTS = run_tests.sh @@ -69,6 +71,7 @@ test_async_perf_SOURCES = test_async_perf.c test_common.c pio_tests.h test_darray_vard_SOURCES = test_darray_vard.c test_common.c pio_tests.h test_async_1d_SOURCES = test_async_1d.c pio_tests.h test_simple_SOURCES = test_simple.c test_common.c pio_tests.h +test_gdal_SOURCES = test_gdal.c test_common.c pio_tests.h # Distribute the test script. EXTRA_DIST = run_tests.sh.in CMakeLists.txt test_darray_frame.c diff --git a/tests/cunit/data/simple.dbf b/tests/cunit/data/simple.dbf new file mode 100644 index 000000000..63cb58b96 Binary files /dev/null and b/tests/cunit/data/simple.dbf differ diff --git a/tests/cunit/data/simple.prj b/tests/cunit/data/simple.prj new file mode 100644 index 000000000..f45cbadf0 --- /dev/null +++ b/tests/cunit/data/simple.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/tests/cunit/data/simple.shp b/tests/cunit/data/simple.shp new file mode 100644 index 000000000..f2c1cd1ea Binary files /dev/null and b/tests/cunit/data/simple.shp differ diff --git a/tests/cunit/data/simple.shx b/tests/cunit/data/simple.shx new file mode 100644 index 000000000..9120db920 Binary files /dev/null and b/tests/cunit/data/simple.shx differ diff --git a/tests/cunit/pio_tests.h b/tests/cunit/pio_tests.h index db4fb599f..8acc03731 100644 --- a/tests/cunit/pio_tests.h +++ b/tests/cunit/pio_tests.h @@ -123,4 +123,6 @@ int run_test_main(int argc, char **argv, int min_ntasks, int max_ntasks, /* Create a 2D decomposition used in some tests. */ int create_decomposition_2d(int ntasks, int my_rank, int iosysid, int *dim_len_2d, int *ioid, int pio_type); +int create_decomposition_2d_uneven(int ntasks, int my_rank, int iosysid, int *dim_len_2d, int *ioid, + int pio_type); #endif /* _PIO_TESTS_H */ diff --git a/tests/cunit/run_tests.sh.in b/tests/cunit/run_tests.sh.in index bbc7b1783..9aa23c672 100644 --- a/tests/cunit/run_tests.sh.in +++ b/tests/cunit/run_tests.sh.in @@ -18,6 +18,9 @@ PIO_TESTS='test_intercomm2 test_async_mpi test_spmd test_rearr test_async_simple 'test_darray_3d test_decomp_uneven test_decomps test_darray_async_simple '\ 'test_darray_async test_darray_async_many test_darray_2sync test_async_multicomp '\ 'test_darray_fill test_darray_vard test_async_1d test_darray_append test_simple' +if test "x@PIO_USE_GDAL@" = "xyes"; then + PIO_TESTS="$PIO_TESTS test_gdal" +fi success1=true success2=true diff --git a/tests/cunit/test_common.c b/tests/cunit/test_common.c index cc9f15c31..7a0a784fe 100644 --- a/tests/cunit/test_common.c +++ b/tests/cunit/test_common.c @@ -1038,6 +1038,34 @@ int create_decomposition_2d(int ntasks, int my_rank, int iosysid, int *dim_len_2 return 0; } +int create_decomposition_2d_uneven(int ntasks, int my_rank, int iosysid, int *dim_len_2d, + int *ioid, int pio_type) +{ + PIO_Offset elements_per_pe[4]={0,3,5,8}; /* Array elements per processing unit. */ + PIO_Offset *compdof; /* The decomposition mapping. */ + int ret; + + + /* Allocate space for the decomposition array. */ + if (!(compdof = malloc(elements_per_pe[my_rank] * sizeof(PIO_Offset)))) + return PIO_ENOMEM; + + /* Describe the decomposition. This is a 1-based array, so add 1! */ + for (int i = 0; i < elements_per_pe[my_rank]; i++) + compdof[i] = my_rank*my_rank + i + 1; + + /* Create the PIO decomposition for this test. */ + if ((ret = PIOc_InitDecomp(iosysid, pio_type, NDIM2, dim_len_2d, elements_per_pe[my_rank], + compdof, ioid, NULL, NULL, NULL))) + ERR(ret); + + + /* Free the mapping. */ + free(compdof); + + return 0; +} + /* * This creates a test netCDF file in the specified format. This file * is simple, with a global attribute, 2 dimensions, a scalar var, and diff --git a/tests/cunit/test_darray.c b/tests/cunit/test_darray.c index ee16c525d..cb3b633df 100644 --- a/tests/cunit/test_darray.c +++ b/tests/cunit/test_darray.c @@ -69,7 +69,7 @@ int dim_len[NDIM] = {NC_UNLIMITED, X_DIM_LEN, Y_DIM_LEN}; * @returns 0 for success, error code otherwise. */ int test_darray(int iosysid, int ioid, int num_flavors, int *flavor, int my_rank, - int pio_type) + int pio_type, PIO_Offset arraylen) { char filename[PIO_MAX_NAME + 1]; /* Name for the output files. */ int dimids[NDIM]; /* The dimension IDs. */ @@ -82,7 +82,7 @@ int test_darray(int iosysid, int ioid, int num_flavors, int *flavor, int my_rank MPI_Datatype mpi_type; int type_size; /* size of a variable of type pio_type */ int other_type; /* another variable of the same size but different type */ - PIO_Offset arraylen = 4; +// PIO_Offset arraylen = 4; void *fillvalue, *ofillvalue; void *test_data; void *test_data_in; @@ -345,6 +345,7 @@ int test_all_darray(int iosysid, int num_flavors, int *flavor, int my_rank, int pio_type[NUM_TYPES_TO_TEST] = {PIO_INT, PIO_FLOAT, PIO_DOUBLE}; int dim_len_2d[NDIM2] = {X_DIM_LEN, Y_DIM_LEN}; int ret; /* Return code. */ + PIO_Offset array_len[4] = {0,3,5,8}; for (int t = 0; t < NUM_TYPES_TO_TEST; t++) { @@ -358,7 +359,22 @@ int test_all_darray(int iosysid, int num_flavors, int *flavor, int my_rank, return ret; /* Run a simple darray test. */ - if ((ret = test_darray(iosysid, ioid, num_flavors, flavor, my_rank, pio_type[t]))) + if ((ret = test_darray(iosysid, ioid, num_flavors, flavor, my_rank, pio_type[t], 4))) + return ret; + + /* Free the PIO decomposition. */ + if ((ret = PIOc_freedecomp(iosysid, ioid))) + ERR(ret); + sprintf(filename, "%s_decomp_rank_%d_flavor_%d_type_%d_uneven.nc", TEST_NAME, my_rank, + *flavor, pio_type[t]); + + /* Decompose the data over the tasks. */ + if ((ret = create_decomposition_2d_uneven(TARGET_NTASKS, my_rank, iosysid, dim_len_2d, + &ioid, pio_type[t]))) + return ret; + + /* Run a simple darray test. */ + if ((ret = test_darray(iosysid, ioid, num_flavors, flavor, my_rank, pio_type[t], array_len[my_rank]))) return ret; /* Free the PIO decomposition. */ diff --git a/tests/cunit/test_gdal.c b/tests/cunit/test_gdal.c new file mode 100644 index 000000000..a34b30a40 --- /dev/null +++ b/tests/cunit/test_gdal.c @@ -0,0 +1,279 @@ +/* + * Tests for PIO distributed arrays. + * + * @author Ed Hartnett + * @date 2/16/17 + */ +#include +#include +#include +#include + +/* The number of tasks this test should run on. */ +#define TARGET_NTASKS 2 + +/* The minimum number of tasks this test should run on. */ +#define MIN_NTASKS 2 + +/* The name of this test. */ +#define TEST_NAME "test_gdal" + +/* Number of processors that will do IO. */ +#define NUM_IO_PROCS 1 + +/* Number of computational components to create. */ +#define COMPONENT_COUNT 1 + +/* The number of dimensions in the example data. In this test, we + * are using three-dimensional data. */ +#define NDIM 1 + +/* But sometimes we need arrays of the non-record dimensions. */ +#define NDIM2 2 + +/* The length of our sample data along each dimension. */ +#define X_DIM_LEN 2 +#define Y_DIM_LEN 4 + +/* The number of timesteps of data to write. */ +#define NUM_TIMESTEPS 2 + +/* The names of variables in the netCDF output files. */ +#define VAR_NAME "Billy-Bob" +#define VAR_NAME2 "Sally-Sue" + +/* Test cases relating.*/ +/* currently only shapefile read. No write yet */ +#define NUM_TEST_CASES 1 + +/* Test with and without specifying a fill value to + * PIOc_write_darray(). */ +#define NUM_TEST_CASES_FILLVALUE 2 + +/* The dimension names. */ +//char dim_name[NDIM][PIO_MAX_NAME + 1] = {"timestep", "x", "y"}; + +/* Length of the dimensions in the sample data. */ +//int dim_len[NDIM] = {NC_UNLIMITED, X_DIM_LEN, Y_DIM_LEN}; + +/* Create a 1D decomposition. + * + * @param ntasks the number of available tasks + * @param my_rank rank of this task. + * @param iosysid the IO system ID. + * @param dim_len an array of length 3 with the dimension sizes. + * @param ioid a pointer that gets the ID of this decomposition. + * @param pio_type the type that will be used for basetype. + * @returns 0 for success, error code otherwise. + **/ +int create_decomposition_1d(int ntasks, int my_rank, int iosysid, int *ioid, int pio_type) +{ + PIO_Offset elements_per_pe; /* Array elements per processing unit. */ + int dim_len_1d[NDIM] = {X_DIM_LEN}; + int ret; + + /* How many data elements per task? In this example we will end up + * with 2. */ + elements_per_pe = X_DIM_LEN / ntasks; + + PIO_Offset compdof[elements_per_pe]; + + /* Don't forget to add 1! */ + compdof[0] = my_rank + 1; + + /* This means fill value will be used here. */ + compdof[1] = 0; + + /* Create the PIO decomposition for this test. */ + if ((ret = PIOc_InitDecomp(iosysid, pio_type, NDIM, dim_len_1d, elements_per_pe, + compdof, ioid, NULL, NULL, NULL))) + ERR(ret); + + return 0; +} + +/** + * Test the darray functionality. Create a netCDF file with 3 + * dimensions and 1 PIO_INT variable, and use darray to write some + * data. + * + * @param iosysid the IO system ID. + * @param ioid the ID of the decomposition. + * @param num_flavors the number of IOTYPES available in this build. + * @param flavor array of available iotypes. + * @param my_rank rank of this task. + * @param pio_type the type of the data. + * @returns 0 for success, error code otherwise. + */ +int test_gdal(int iosysid, int ioid, int num_flavors, int *flavor, int my_rank, + int pio_type) +{ + char filename[PIO_MAX_NAME + 1]; /* Name for the output files. */ + int dimids[NDIM]; /* The dimension IDs. */ + int ncid; /* The ncid of the netCDF file. */ + int ncid2; /* The ncid of the re-opened netCDF file. */ + int varid; /* The ID of the netCDF varable. */ + int varid2; /* The ID of a netCDF varable of different type. */ + int wrong_varid = TEST_VAL_42; /* A wrong ID. */ + int ret; /* Return code. */ + MPI_Datatype mpi_type; + int type_size; /* size of a variable of type pio_type */ + int other_type; /* another variable of the same size but different type */ + PIO_Offset arraylen = 8; + void *fillvalue, *ofillvalue; + void *test_data; + int fillvalue_int = NC_FILL_INT; + int test_data_int[]={366, 677}; + float fillvalue_float = NC_FILL_FLOAT; + float test_data_float[]={366., 677.}; + double fillvalue_double = NC_FILL_DOUBLE; + double test_data_double[]={366., 677.}; + int iotype = PIO_IOTYPE_GDAL; + + GDALDatasetH hDSp; + + /* Add a couple of extra tests for the */ + for (int test_multi = 0; test_multi < NUM_TEST_CASES; test_multi++) + { + switch (pio_type) + { + case PIO_INT: + test_data = test_data_int; + // test_data_in = test_data_int; + break; + case PIO_FLOAT: + test_data = test_data_float; + // test_data_in = test_data_float; + break; + case PIO_DOUBLE: + test_data = test_data_double; + // test_data_in = test_data_double; + break; + default: + ERR(ERR_WRONG); + } + + sprintf(filename, "data/simple.shp"); + + /* Open the file. */ + if ((ret = GDALc_openfile(iosysid, &ncid2, &hDSp, &iotype, filename, PIO_NOWRITE))) + ERR(ret); + + if ((ret = GDALc_inq_fieldid(ncid2, "DistFld", &varid))) + ERR(ret); + + /* Read the data. */ + if ((ret = PIOc_read_darray(ncid2, varid, ioid, arraylen, (void *)test_data))) + ERR(ret); + + /* Close the GIS file. */ + if ((ret = PIOc_closefile(ncid2))) + ERR(ret); + } /* next test multi */ + + return PIO_NOERR; +} + +/** + * Run all the tests. + * + * @param iosysid the IO system ID. + * @param num_flavors number of available iotypes in the build. + * @param flavor pointer to array of the available iotypes. + * @param my_rank rank of this task. + * @param test_comm the communicator the test is running on. + * @returns 0 for success, error code otherwise. + */ +int test_all_gdal(int iosysid, int num_flavors, int *flavor, int my_rank, + MPI_Comm test_comm) +{ +#define NUM_TYPES_TO_TEST 1 + int ioid; + char filename[PIO_MAX_NAME + 1]; + int pio_type[NUM_TYPES_TO_TEST] = {PIO_DOUBLE}; + int dim_len_1d[NDIM] = {X_DIM_LEN};//, Y_DIM_LEN}; + int ret; /* Return code. */ + + for (int t = 0; t < NUM_TYPES_TO_TEST; t++) + { + /* Decompose the data over the tasks. */ + if ((ret = create_decomposition_1d(TARGET_NTASKS, my_rank, iosysid, + &ioid, pio_type[t]))) + return ret; + + printf("my_rank %d iosysid %d ioid %d ret %d\n",my_rank, iosysid, ioid, ret); + + /* Run a simple darray test. */ + if ((ret = test_gdal(iosysid, ioid, num_flavors, flavor, my_rank, pio_type[t]))) + return ret; + + /* Free the PIO decomposition. */ + if ((ret = PIOc_freedecomp(iosysid, ioid))) + ERR(ret); + } + + return PIO_NOERR; +} + +/* Run tests for darray functions. */ +int main(int argc, char **argv) +{ +#define NUM_REARRANGERS_TO_TEST 2 + int rearranger[NUM_REARRANGERS_TO_TEST] = {PIO_REARR_SUBSET, PIO_REARR_BOX}; + int my_rank; + int ntasks; + int num_flavors; /* Number of PIO netCDF flavors in this build. */ + int flavor[NUM_FLAVORS]; /* iotypes for the supported netCDF IO flavors. */ + MPI_Comm test_comm; /* A communicator for this test. */ + int ret; /* Return code. */ + + OGRRegisterAll(); + + /* Initialize test. */ + if ((ret = pio_test_init2(argc, argv, &my_rank, &ntasks, MIN_NTASKS, + MIN_NTASKS, -1, &test_comm))) + ERR(ERR_INIT); + + if ((ret = PIOc_set_iosystem_error_handling(PIO_DEFAULT, PIO_RETURN_ERROR, NULL))) + return ret; + + /* Only do something on max_ntasks tasks. */ + if (my_rank < TARGET_NTASKS) + { + int iosysid; /* The ID for the parallel I/O system. */ + int ioproc_stride = 1; /* Stride in the mpi rank between io tasks. */ + int ioproc_start = 0; /* Zero based rank of first processor to be used for I/O. */ + int ret; /* Return code. */ + + /* Figure out iotypes. */ + if ((ret = get_iotypes(&num_flavors, flavor))) + ERR(ret); + + for (int r = 0; r < NUM_REARRANGERS_TO_TEST; r++) + { + /* Initialize the PIO IO system. This specifies how + * many and which processors are involved in I/O. */ + if ((ret = PIOc_Init_Intracomm(test_comm, NUM_IO_PROCS, ioproc_stride, + ioproc_start, rearranger[r], &iosysid))) + return ret; + + /* Run tests. */ + printf("Testing rearranger = %d\n", rearranger[r]); + if ((ret = test_all_gdal(iosysid, num_flavors, flavor, my_rank, test_comm))) + return ret; + + /* Finalize PIO system. */ + if ((ret = PIOc_free_iosystem(iosysid))) + return ret; + } /* next rearranger */ + } /* endif my_rank < TARGET_NTASKS */ + + /* Finalize the MPI library. */ + if ((ret = pio_test_finalize(&test_comm))) + return ret; + /* if ((ret = pio_test_finalize2(&test_comm, TEST_NAME))) */ + /* return ret; */ + + printf("%d %s SUCCESS!!\n", my_rank, TEST_NAME); + return 0; +}