diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index d40bb8c9..19a63a15 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -995,7 +995,7 @@ RECURSIVE = NO # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = @abs_top_srcdir@/src/g2c_interface.F90 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/spack/package.py b/spack/package.py index b16224a7..3789a8f3 100644 --- a/spack/package.py +++ b/spack/package.py @@ -55,16 +55,16 @@ class G2(CMakePackage): depends_on("jasper@:2.0.32", when="@:3.4.7") depends_on("jasper") - depends_on("g2c", when="@develop") - depends_on("g2c@develop +aec", when="+aec") + depends_on("g2c@2.0.0:", when="@develop") + depends_on("g2c@2.0.0 +aec", when="+aec") depends_on("libpng") depends_on("zlib-api", when="@develop") depends_on("bacio", when="@3.4.6:") depends_on("ip", when="@develop") requires("^ip precision=d", when="^ip@4.1:") depends_on("sp", when="^ip@:4") - requires("^sp precision=d", when="^ip@:4 ^sp@2.4:") - depends_on("g2c@1.8: +utils", when="+g2c_compare") + depends_on("sp precision=d", when="^ip@:4 ^sp@2.4:") + depends_on("g2c@2.0: +utils", when="+g2c_compare") with when("+w3emc"): depends_on("w3emc") depends_on("w3emc precision=4", when="precision=4") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e8cf315..12a0e0e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,11 +4,12 @@ # Mark Potts, Kyle Gerheiser, Ed Hartnett # These are the fortran source files. -set(fortran_src compack.F90 drstemplates.F90 g2bytes.F90 g2grids.F90 -g2get.F90 g2getgb2.F90 g2index.F90 g2gf.F90 g2unpack.F90 g2create.F90 -${CMAKE_CURRENT_BINARY_DIR}/gribmod.F90 gridtemplates.F90 intmath.F90 -g2jpc.F90 pack_gp.f params_ecmwf.F90 params.F90 pdstemplates.F90 -g2png.F90 realloc.F90 reduce.f g2sim.F90 skgb.F90 g2spec.F90 g2logging.F90) +set(fortran_src compack.F90 drstemplates.F90 g2bytes.F90 +g2grids.F90 g2get.F90 g2getgb2.F90 g2index.F90 g2gf.F90 g2unpack.F90 +g2create.F90 ${CMAKE_CURRENT_BINARY_DIR}/gribmod.F90 gridtemplates.F90 +intmath.F90 g2jpc.F90 pack_gp.f params_ecmwf.F90 params.F90 +pdstemplates.F90 g2png.F90 realloc.F90 reduce.f g2sim.F90 skgb.F90 +g2spec.F90 g2logging.F90 g2cf.F90 g2c_interface.F90) # This function calls NCEPLIBS-w3emc. if (BUILD_WITH_W3EMC) @@ -24,7 +25,8 @@ set(c_src mova2i.c) # Create this fortran file, which has the contents of the VERSION file # substituted in. -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/gribmod.F90.in" "${CMAKE_CURRENT_BINARY_DIR}/gribmod.F90" @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/gribmod.F90.in" + "${CMAKE_CURRENT_BINARY_DIR}/gribmod.F90" @ONLY) foreach(kind ${kinds}) diff --git a/src/g2c_interface.F90 b/src/g2c_interface.F90 new file mode 100644 index 00000000..b97d3737 --- /dev/null +++ b/src/g2c_interface.F90 @@ -0,0 +1,42 @@ +! Contains a module with interfaces to the C functions in the g2c +! library. +! +! Note: this file is excluded from the doxygen build (see Doxyfile.in) +! because these functions are already documented in the g2c library. +! +! Edward Hartnett, 6/12/2024 +module g2c_interface + interface + function g2c_open(path, mode, g2idp) bind(c) + use iso_c_binding + character(kind = c_char), intent(in) :: path(*) + integer(c_int), value :: mode + integer(c_int), intent(out) :: g2idp + integer(c_int) :: g2c_open + end function g2c_open + + function g2c_open_index(data_file, index_file, mode, g2cid) bind(c) + use iso_c_binding + character(kind=c_char), intent(in) :: data_file(*), index_file(*) + integer(c_int), value :: mode + integer(c_int), intent(out) :: g2cid + integer(c_int) :: g2c_open_index + end function g2c_open_index + + ! int g2c_inq(int g2cid, int *num_msg); + ! int g2c_inq_msg(int g2cid, int msg_num, unsigned char *discipline, int *num_fields, + ! int *num_local, short *center, short *subcenter, unsigned char *master_version, + ! unsigned char *local_version); + function g2c_close(g2id) bind(c) + use iso_c_binding + integer(c_int), value :: g2id + integer(c_int) :: g2c_close + end function g2c_close + function g2c_set_log_level(log_level) bind(c) + use iso_c_binding + integer(c_int), intent(in) :: log_level + integer(c_int) :: g2c_set_log_level + end function g2c_set_log_level + + end interface +end module g2c_interface diff --git a/src/g2cf.F90 b/src/g2cf.F90 new file mode 100644 index 00000000..faf96440 --- /dev/null +++ b/src/g2cf.F90 @@ -0,0 +1,179 @@ +!> @file +!> @brief Module for the NCEPLIBS-g2 file-based GRIB2 API. +!> @author Edward Hartnett @date 2020-16-12 + +!> @brief Module for the NCEPLIBS-g2 file-based GRIB2 API. +!> +!> @author Edward Hartnett @date 2020-16-12 +module g2cf + use g2c_interface + +contains + !> Add a C_NULL_CHAR to a string to create a C compatible + !> string. Assumes target variable will be of length + !> LEN(string)+1. Trailing blanks will be stripped from string and + !> length of trimmed string will be returned in nlen. + !> + !> @param string The string to trimmed and null-terminated + !> @param nlen The length of the returned string, including + !> null-terminator. + !> + !> @return the trimmed, null-terminated string + !> + !> This function was originally written by, Richard Weed, Ph.D., as part of + !> netcdf-fortran. + !> + !> @author Edward Hartnett @date 2024-06-12 + function addcnullchar(string, nlen) result(cstring) + use iso_c_binding + implicit none + + character(len=*), intent(in) :: string + integer, intent(inout) :: nlen + character(len = (len(string) + 1)) :: cstring + + integer :: inull + + ! First check to see if we already have a C NULL char attached + ! to string and strip trailing blanks. We will use it if its present + ! otherwise we add one. The length of the trimmed string plus the + ! C_NULL_CHAR is returned in nlen. + nlen = len_trim(string) + inull = scan(string, C_NULL_CHAR) + cstring = repeat(" ", len(cstring)) ! init to blanks + if (inull > 0) then ! string has a NULL char + nlen = inull + cstring = string(1:nlen) + else ! append null char to trimmed string + cstring = string(1:nlen)//C_NULL_CHAR + nlen = nlen + 1 + endif + end function addcnullchar + + !> Open a GRIB2 file. + !> + !> @param path The path to the file + !> @param mode Flag with open mode information + !> @param g2id The ID of the open file + !> + !> @return 0 for success, error code otherwise. + !> + !> @author Edward Hartnett @date 2024-06-12 + function g2cf_open(path, mode, g2id) result (status) + use iso_c_binding + use g2c_interface + implicit none + + character(len = *), intent(in) :: path + integer, intent(in) :: mode + integer, intent(inout) :: g2id + integer :: status + + integer(c_int) :: cmode, cg2id, cstatus + character(len = (len(path) + 1)) :: cpath + integer :: ie + + cmode = mode + cg2id = 0 + + ! Check for C null character on path and add one if not present. + cpath = addCNullChar(path, ie) + + ! Call g2c_open to open GRIB2 file. + cstatus = g2c_open(cpath(1:ie), cmode, cg2id) + + ! Return results to caller. + if (cstatus .eq. 0) then + g2id = cg2id + endif + status = cstatus + + end function g2cf_open + + !> Open a GRIB2 file using an exsiting index file (generated by the + !> grb2index utility). + !> + !> @param data_file Path to data file. + !> @param index_file Path to index file. + !> @param mode Open mode, may be NC_CLOBBER (0) or NC_NOCLOBBER. + !> @param g2cid File ID. + !> + !> @return + !> - 0 No error + !> + !> @author Ed Hartnett @date 2022-11-21 + function g2cf_open_index(data_file, index_file, mode, g2cid) result (status) + use iso_c_binding + implicit none + + character(len=*), intent(in) :: data_file, index_file + integer, intent(in) :: mode + integer, intent(inout) :: g2cid + integer :: status + + integer(c_int) :: cmode, cg2cid, cstatus + character(len = (len(data_file) + 1)) :: cdata_file + character(len = (len(index_file) + 1)) :: cindex_file + integer :: ie1, ie2 + + cmode = mode + cg2cid = 0 + + ! Check for c null character on path and add one if not present. + cdata_file = addcnullchar(data_file, ie1) + cindex_file = addcnullchar(index_file, ie2) + + ! Call g2c_open_index to open the file. + cstatus = g2c_open_index(cdata_file(1:ie1), cindex_file(1:ie2), cmode, cg2cid) + + if (cstatus == 0) then + g2cid = cg2cid + endif + status = cstatus + end function g2cf_open_index + + !> Close a GRIB2 file. + !> + !> @param g2id The ID of the open file + !> + !> @return 0 for success, error code otherwise. + !> + !> @author Edward Hartnett @date 2024-06-12 + function g2cf_close(g2id) result(status) + use iso_c_binding + use g2c_interface + implicit none + + integer, intent(in) :: g2id + integer :: status + + integer(c_int) :: cg2id, cstatus + + cg2id = g2id + cstatus = g2c_close(cg2id) + status = cstatus + end function g2cf_close + + !> Turn internal logging on. + !> + !> @param log_level 0 for no logging, 5 for maximum logging. + !> + !> @return 0 for success, error code otherwise. + !> + !> @author Edward Hartnett @date 2024-12-16 + function g2cf_set_log_level(log_level) result(status) + use iso_c_binding + use g2c_interface + implicit none + + integer, intent(in) :: log_level + integer :: status + + integer(c_int) :: clog_level, cstatus + + clog_level = log_level + cstatus = g2c_set_log_level(clog_level) + status = cstatus + end function g2cf_set_log_level + +end module g2cf diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2433e0d2..91b16c8e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -171,7 +171,7 @@ copy_test_data(ref_jpeg_bitmap.grib2) # Build a _4 and _d version of each test and link them to the _4 and # _d builds of the library, for 4-byte real, and 8-byte real. foreach(kind ${kinds}) -# create_test(test_g2cf ${kind}) + create_test(test_g2cf ${kind}) create_test(test_misc ${kind}) create_test(test_g2 ${kind}) create_test(test_g1 ${kind}) diff --git a/tests/test_g2cf.F90 b/tests/test_g2cf.F90 index 474d3da9..0e284f1a 100644 --- a/tests/test_g2cf.F90 +++ b/tests/test_g2cf.F90 @@ -21,16 +21,16 @@ program test_g2cf ierr = g2cf_open(fileName, 0, g2cid) if (ierr .ne. 0) stop 2 - ! Check number of messages. - ierr = g2cf_inq(g2cid, num_msg) - if (ierr .ne. 0) stop 10 - if (num_msg .ne. 19) stop 11 + ! ! Check number of messages. + ! ierr = g2cf_inq(g2cid, num_msg) + ! if (ierr .ne. 0) stop 10 + ! if (num_msg .ne. 19) stop 11 - ! Check the last message. - ierr = g2cf_inq_msg(g2cid, 19, discipline, num_fields, num_local, center, subcenter, & - master_version, local_version) - if (discipline .ne. 10 .or. num_fields .ne. 1 .or. num_local .ne. 0 .or. center .ne. 7 .or. & - subcenter .ne. 0 .or. master_version .ne. 2 .or. local_version .ne. 1) stop 12 + ! ! Check the last message. + ! ierr = g2cf_inq_msg(g2cid, 19, discipline, num_fields, num_local, center, subcenter, & + ! master_version, local_version) + ! if (discipline .ne. 10 .or. num_fields .ne. 1 .or. num_local .ne. 0 .or. center .ne. 7 .or. & + ! subcenter .ne. 0 .or. master_version .ne. 2 .or. local_version .ne. 1) stop 12 ! Close the file. ierr = g2cf_close(g2cid)