diff --git a/.github/workflows/developer.yml b/.github/workflows/developer.yml index 9cf92586..2df9d26c 100644 --- a/.github/workflows/developer.yml +++ b/.github/workflows/developer.yml @@ -146,7 +146,7 @@ jobs: - name: upload-test-coverage if: matrix.config == 'asan/code coverage' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: g2-test-coverage path: | diff --git a/src/g2_gbytesc.F90 b/src/g2_gbytesc.F90 index 4c76cf29..de841bd0 100644 --- a/src/g2_gbytesc.F90 +++ b/src/g2_gbytesc.F90 @@ -11,7 +11,7 @@ !> has more elements, use g2_sbytesc(). !> !> @param[in] in Array input. -!> @param[out] iout Unpacked array output. +!> @param[inout] iout Unpacked array output. !> @param[in] iskip Initial number of bits to skip. !> @param[in] nbits Number of bits of each integer in IN to take. !> @@ -32,7 +32,7 @@ end subroutine g2_gbytec !> This should be used when input array IN has only one element. If IN !> has more elements, use G2_SBYTESC(). !> -!> @param[out] out packed array output +!> @param[inout] out packed array output !> @param[in] in unpacked array input !> @param[in] iskip initial number of bits to skip !> @param[in] nbits Number of bits of each integer in OUT to fill. diff --git a/src/g2index.F90 b/src/g2index.F90 index 07cbeeb7..5ae1bcd7 100644 --- a/src/g2index.F90 +++ b/src/g2index.F90 @@ -212,6 +212,7 @@ SUBROUTINE GETG2I(LUGI, CBUF, NLEN, NNUM, IRET) INTEGER, INTENT(OUT) :: NLEN, NNUM, IRET CHARACTER CHEAD*162 integer :: ios, istat, lbuf, lhead, nskp + integer :: index_version NULLIFY(CBUF) NLEN = 0 @@ -219,7 +220,7 @@ SUBROUTINE GETG2I(LUGI, CBUF, NLEN, NNUM, IRET) IRET = 4 CALL BAREAD(LUGI, 0, 162, LHEAD, CHEAD) IF (LHEAD .EQ. 162 .AND. CHEAD(42:47) .EQ. 'GB2IX1') THEN - READ(CHEAD(82:162), '(8X, 3I10, 2X, A40)', IOSTAT = IOS) NSKP, NLEN, NNUM + READ(CHEAD(82:162), '(2X, I1, 5X, 3I10, 2X, A40)', IOSTAT = IOS) index_version, NSKP, NLEN, NNUM IF (IOS .EQ. 0) THEN ALLOCATE(CBUF(NLEN), STAT = ISTAT) ! ALLOCATE SPACE FOR CBUF IF (ISTAT .NE. 0) THEN @@ -239,8 +240,8 @@ END SUBROUTINE GETG2I !> and byte offsets within the message to each section. The index file !> record format is documented in subroutine ixgb2(). !> -!> @note Subprogram can be called from a multiprocessing environment. -!> Do not engage the same logical unit from more than one processor. +!> This is the legacy function, which always creates version 1 index +!> files. To create index files of version 1 or 2, use getg2i2r(). !> !> @param[in] lugb Unit of the unblocked GRIB file. Must !> be opened by [baopen() or baopenr()] @@ -265,45 +266,108 @@ END SUBROUTINE GETG2I !> - 3 Error deallocating memory. !> !> @author Mark Iredell @date 1995-10-31 -SUBROUTINE GETG2IR(LUGB, MSK1, MSK2, MNUM, CBUF, NLEN, NNUM, NMESS, IRET) - USE RE_ALLOC ! NEEDED FOR SUBROUTINE REALLOC +subroutine getg2ir(lugb, msk1, msk2, mnum, cbuf, nlen, nnum, nmess, iret) + use re_alloc ! needed for subroutine realloc implicit none + character(len = 1), pointer, dimension(:) :: cbuf + integer, intent(in) :: lugb, msk1, msk2, mnum + integer, intent(out) :: nlen, nnum, nmess, iret + + interface ! required for cbuf pointer + subroutine getg2i2r(lugb, msk1, msk2, mnum, idxver, cbuf, nlen, & + nnum, nmess, iret) + character(len = 1), pointer, dimension(:) :: cbuf + integer, intent(in) :: lugb, msk1, msk2, mnum, idxver + integer, intent(out) :: nlen, nnum, nmess, iret + end subroutine getg2i2r + end interface - CHARACTER(LEN = 1), POINTER, DIMENSION(:) :: CBUF - INTEGER, INTENT(IN) :: LUGB, MSK1, MSK2, MNUM - INTEGER, INTENT(OUT) :: NLEN, NNUM, NMESS, IRET - CHARACTER(LEN = 1), POINTER, DIMENSION(:) :: CBUFTMP + call getg2i2r(lugb, msk1, msk2, mnum, 1, cbuf, nlen, nnum, nmess, iret) +end subroutine getg2ir + +!> Generate an index record for a message in a GRIB2 file. +!> +!> The index record contains byte offsets to the message, it's length, +!> and byte offsets within the message to each section. The index file +!> record format is documented in subroutine ixgb2(). +!> +!> This subroutine is like getg2ir(), but this subroutine can generate +!> index files in version 1 or 2 format. Use index version 2 for files +!> > 2 GB. +!> +!> @note Subprogram can be called from a multiprocessing environment. +!> Do not engage the same logical unit from more than one processor. +!> +!> @param[in] lugb Unit of the unblocked GRIB file. Must +!> be opened by [baopen() or baopenr()] +!> (https://noaa-emc.github.io/NCEPLIBS-bacio/). +!> @param[in] msk18 Number of bytes to search for first message. +!> @param[in] msk28 Number of bytes to search for other messages. +!> @param[in] mnum Number of GRIB messages to skip (usually 0). +!> @param[in] idxver Index version, 1 for legacy, 2 for files > 2 GB. +!> @param[out] cbuf Pointer to a buffer that will get the index +!> records. If any memory is associated with cbuf when this subroutine +!> is called, cbuf will be nullified in the subroutine. Initially cbuf +!> will get an allocation of 5000 bytes. realloc() will be used to +!> increase the size if necessary. Users must free memory that cbuf +!> points to when cbuf is no longer needed. +!> @param[out] nlen Total length of index record buffer in bytes. +!> @param[out] nnum Number of index records, zero if no GRIB +!> messages are found. +!> @param[out] nmess Last GRIB message in file successfully processed +!> @param[out] iret Return code. +!> - 0 No error. +!> - 1 Not enough memory available to hold full index buffer. +!> - 2 Not enough memory to allocate initial index buffer. +!> - 3 Error deallocating memory. +!> +!> @author Mark Iredell, Edward Hartnett @date Feb 4, 2024 +subroutine getg2i2r(lugb, msk18, msk28, mnum, idxver, cbuf, nlen, & + nnum, nmess, iret) + use re_alloc ! needed for subroutine realloc + implicit none + + character(len = 1), pointer, dimension(:) :: cbuf + integer, intent(in) :: lugb + integer (kind = 8), intent(in) :: msk18, msk28 + integer :: msk1, msk2 + integer, intent(in) :: mnum, idxver + integer, intent(out) :: nlen, nnum, nmess, iret + character(len = 1), pointer, dimension(:) :: cbuftmp integer :: nbytes, newsize, next, numfld, m, mbuf, lskip, lgrib integer :: istat, iseek, init, iret1 - PARAMETER(INIT = 50000, NEXT = 10000) + parameter(init = 50000, next = 10000) - INTERFACE ! REQUIRED FOR CBUF POINTER - SUBROUTINE IXGB2(LUGB, LSKIP, LGRIB, CBUF, NUMFLD, MLEN, IRET) - INTEGER, INTENT(IN) :: LUGB, LSKIP, LGRIB - CHARACTER(LEN = 1), POINTER, DIMENSION(:) :: CBUF - INTEGER, INTENT(OUT) :: NUMFLD, MLEN, IRET - END SUBROUTINE IXGB2 - END INTERFACE + interface ! required for cbuf pointer + subroutine ixgb2(lugb, lskip, lgrib, cbuf, numfld, mlen, iret) + integer :: lugb, lskip, lgrib + character(len = 1), pointer, dimension(:) :: cbuf + integer :: numfld, mlen, iret + end subroutine ixgb2 + end interface + + msk1 = msk18 + msk2 = msk28 ! Initialize. - IRET = 0 - NULLIFY(CBUF) - MBUF = INIT - ALLOCATE(CBUF(MBUF), STAT = ISTAT) ! Allocate initial space for cbuf. - IF (ISTAT .NE. 0) THEN - IRET = 2 - RETURN - ENDIF + iret = 0 + nullify(cbuf) + mbuf = init + allocate(cbuf(mbuf), stat = istat) ! allocate initial space for cbuf. + if (istat .ne. 0) then + iret = 2 + return + endif ! Search for first grib message. - ISEEK = 0 - CALL SKGB(LUGB, ISEEK, MSK1, LSKIP, LGRIB) - DO M = 1, MNUM - IF(LGRIB.GT.0) THEN - ISEEK = LSKIP + LGRIB - CALL SKGB(LUGB, ISEEK, MSK2, LSKIP, LGRIB) - ENDIF - ENDDO + iseek = 0 + call skgb(lugb, iseek, msk1, lskip, lgrib) + do m = 1, mnum + if(lgrib.gt.0) then + iseek = lskip + lgrib + call skgb(lugb, iseek, msk2, lskip, lgrib) + endif + enddo ! Get index records for every grib message found. NLEN = 0 @@ -342,7 +406,7 @@ END SUBROUTINE IXGB2 ISEEK = LSKIP + LGRIB CALL SKGB(LUGB, ISEEK, MSK2, LSKIP, LGRIB) ENDDO -END SUBROUTINE GETG2IR +END SUBROUTINE GETG2I2R !> Find information about a GRIB field from the index and fill a @ref !> grib_mod::gribfield. @@ -646,21 +710,93 @@ END SUBROUTINE GETGB2S !> - byte kk + 1- ll the data representation section (drs) !> - byte ll + 1-ll + 6 first 6 bytes of the bit map section (bms) !> -!> @param[in] lugb Unit of the unblocked GRIB file. Must +!> @param lugb Unit of the unblocked GRIB file. Must !> be opened by [baopen() or baopenr()] !> (https://noaa-emc.github.io/NCEPLIBS-bacio/). -!> @param[in] lskip Number of bytes to skip before GRIB message. -!> @param[in] lgrib Number of bytes in GRIB message. When subroutine is +!> @param lskip Number of bytes to skip before GRIB message. +!> @param lgrib Number of bytes in GRIB message. When subroutine is !> called, this must be set to the size of the cbuf buffer. -!> @param[out] cbuf Pointer to a buffer that will get the index +!> @param cbuf Pointer to a buffer that will get the index +!> records. If any memory is associated with cbuf when this subroutine +!> is called, cbuf will be nullified in the subroutine. Initially cbuf +!> will get an allocation of 5000 bytes. realloc() will be used to +!> increase the size if necessary. Users must free memory that cbuf +!> points to when cbuf is no longer needed. +!> @param numfld Number of index records created. +!> @param mlen Total length of all index records. +!> @param iret Return code +!> - 0 No error +!> - 1 Not enough memory available to hold full index buffer. +!> - 2 I/O error in read. +!> - 3 GRIB message is not edition 2. +!> - 4 Not enough memory to allocate extent to index buffer. +!> - 5 Unidentified GRIB section encountered. +!> +!> @author Mark Iredell @date 1995-10-31 +subroutine ixgb2(lugb, lskip, lgrib, cbuf, numfld, mlen, iret) + implicit none + integer :: lugb, lskip, lgrib + character(len = 1), pointer, dimension(:) :: cbuf + integer :: numfld, mlen, iret + integer (kind = 8) :: lskip8 + + interface + subroutine ix2gb2(lugb, lskip8, idxver, lgrib, cbuf, numfld, mlen, iret) + integer :: lugb + integer (kind = 8) :: lskip8 + integer :: idxver, lgrib + character(len = 1), pointer, dimension(:) :: cbuf + integer :: numfld, mlen, iret + end subroutine ix2gb2 + end interface + + lskip8 = lskip + call ix2gb2(lugb, lskip8, 1, lgrib, cbuf, numfld, mlen, iret) +end SUBROUTINE IXGB2 + +!> Generate an index record for each field in a GRIB2 message. The index +!> records are written to index buffer pointed to by cbuf. All integers +!> in the index are in big-endian format. +!> +!> This subroutine is called by getg2ir(), which packages the index +!> records into an index file. +!> +!> The index buffer returned contains index records with the +!> format: +!> - byte 001 - 004 length of index record +!> - byte 005 - 008 bytes to skip in data file before GRIB message +!> - byte 009 - 012 bytes to skip in message before lus (local use) set = 0, if no local section. +!> - byte 013 - 016 bytes to skip in message before gds +!> - byte 017 - 020 bytes to skip in message before pds +!> - byte 021 - 024 bytes to skip in message before drs +!> - byte 025 - 028 bytes to skip in message before bms +!> - byte 029 - 032 bytes to skip in message before data section +!> - byte 033 - 040 bytes total in the message +!> - byte 041 - 041 GRIB version number (2) +!> - byte 042 - 042 message discipline +!> - byte 043 - 044 field number within GRIB2 message +!> - byte 045 - ii identification section (ids) +!> - byte ii + 1- jj grid definition section (gds) +!> - byte jj + 1- kk product definition section (pds) +!> - byte kk + 1- ll the data representation section (drs) +!> - byte ll + 1-ll + 6 first 6 bytes of the bit map section (bms) +!> +!> @param lugb Unit of the unblocked GRIB file. Must +!> be opened by [baopen() or baopenr()] +!> (https://noaa-emc.github.io/NCEPLIBS-bacio/). +!> @param lskip8 Number of bytes to skip before GRIB message. +!> @param idxver Index version, 1 for legacy, 2 for GRIB2 files > 2 GB. +!> @param lgrib Number of bytes in GRIB message. When subroutine is +!> called, this must be set to the size of the cbuf buffer. +!> @param cbuf Pointer to a buffer that will get the index !> records. If any memory is associated with cbuf when this subroutine !> is called, cbuf will be nullified in the subroutine. Initially cbuf !> will get an allocation of 5000 bytes. realloc() will be used to !> increase the size if necessary. Users must free memory that cbuf !> points to when cbuf is no longer needed. -!> @param[out] numfld Number of index records created. -!> @param[out] mlen Total length of all index records. -!> @param[out] iret Return code +!> @param numfld Number of index records created. +!> @param mlen Total length of all index records. +!> @param iret Return code !> - 0 No error !> - 1 Not enough memory available to hold full index buffer. !> - 2 I/O error in read. @@ -669,11 +805,17 @@ END SUBROUTINE GETGB2S !> - 5 Unidentified GRIB section encountered. !> !> @author Mark Iredell @date 1995-10-31 -SUBROUTINE IXGB2(LUGB, LSKIP, LGRIB, CBUF, NUMFLD, MLEN, IRET) - USE RE_ALLOC ! NEEDED FOR SUBROUTINE REALLOC +subroutine ix2gb2(lugb, lskip8, idxver, lgrib, cbuf, numfld, mlen, iret) + use re_alloc ! needed for subroutine realloc implicit none + integer :: lugb + integer (kind = 8) :: lskip8 + integer :: idxver, lgrib CHARACTER(LEN = 1), POINTER, DIMENSION(:) :: CBUF + integer :: numfld, mlen, iret + integer :: lskip + CHARACTER CVER, CDISC CHARACTER(LEN = 4) :: CTEMP INTEGER LOCLUS, LOCGDS, LENGDS, LOCBMS @@ -681,7 +823,7 @@ SUBROUTINE IXGB2(LUGB, LSKIP, LGRIB, CBUF, NUMFLD, MLEN, IRET) integer :: linmax, ixskp integer :: mxspd, mxskp, mxsgd, mxsdr, mxsbm, mxlus integer :: mxlen, mxds, mxfld, mxbms - integer :: init, ixlus, lugb, lskip, lgrib, numfld, mlen, iret + integer :: init, ixlus integer :: ixsgd, ibread, ibskip, ilndrs, ilnpds, istat, ixds integer :: ixspd, ixfld, ixids, ixlen, ixsbm, ixsdr integer :: lbread, lensec, lensec1 @@ -693,6 +835,7 @@ SUBROUTINE IXGB2(LUGB, LSKIP, LGRIB, CBUF, NUMFLD, MLEN, IRET) CHARACTER CBREAD(LINMAX), CINDEX(LINMAX) CHARACTER CIDS(LINMAX), CGDS(LINMAX) + lskip = lskip8 LOCLUS = 0 IRET = 0 MLEN = 0 @@ -811,7 +954,7 @@ SUBROUTINE IXGB2(LUGB, LSKIP, LGRIB, CBUF, NUMFLD, MLEN, IRET) ENDIF IBSKIP = IBSKIP + LENSEC ENDDO -END SUBROUTINE IXGB2 +end subroutine ix2gb2 !> Free all memory associated with the library. !> diff --git a/tests/test_gbytec.F90 b/tests/test_gbytec.F90 index cfb49fb0..6064e2e2 100644 --- a/tests/test_gbytec.F90 +++ b/tests/test_gbytec.F90 @@ -11,6 +11,9 @@ program test_gbytec character*1 :: out5(5) character*1 :: out8(8) character*1 :: out10(10) + character*1 :: out16(16) + character*1 :: out32(32) + integer :: in4(4), in1(1) integer, parameter :: n = 1 integer :: in(n) real :: r_in(n) @@ -24,16 +27,22 @@ program test_gbytec integer :: nskip = 0 integer :: i integer :: num + integer(kind = 4) :: in44(4), in44_1(4) + integer(kind = 8) :: in8(1), in8_1(1), in84(4), in84_1(4) - print *, 'Testing g2_gbytesc.f subroutines.' + print *, 'Testing g2_gbytesc.F90 subroutines.' - print *, 'Testing g2_sbytec()...' + print *, ' testing g2_sbytec()...' in(1) = 3 out(1) = char(0) call g2_sbytec(out, in, iskip, nbits) if (ichar(out(1)) .ne. in(1)) stop 10 - print *, 'Testing g2_sbytesc()...' + print *, ' testing g2_gbytec()...' + call g2_gbytec(out, in, iskip, nbits) + if (ichar(out(1)) .ne. in(1)) stop 11 + + print *, ' testing g2_sbytesc()...' in(1) = 3 out(1) = char(0) call g2_sbytesc(out, in, iskip, nbits, nskip, n) @@ -41,7 +50,7 @@ program test_gbytec ! This will pack the numbers 1 and 2 into the first two chars of the ! buffer. The rest of the output buffer will remain zeros. - print *, 'Testing g2_sbytesc() packing 2 values...' + print *, ' testing g2_sbytesc() packing 2 values...' in2(1) = 1 in2(2) = 2 do i = 1, 8 @@ -58,7 +67,7 @@ program test_gbytec end do ! Now pack 5 values into the 5 character array out5. - print *, 'Testing g2_sbytesc() packing 5 values...' + print *, ' testing g2_sbytesc() packing 5 values...' in5(1) = 1 in5(2) = 2 in5(3) = 3 @@ -76,7 +85,7 @@ program test_gbytec ! Now pack 5 values into the 10 character array out10. Skip every ! other byte in the output. - print *, 'Testing g2_sbytesc() packing 5 values, skipping every other byte...' + print *, ' testing g2_sbytesc() packing 5 values, skipping every other byte...' nbits = 8 nskip = 0 do i = 1, 10 @@ -88,32 +97,45 @@ program test_gbytec if (mod(i, 2) .gt. 0) then if (ichar(out10(i)) .ne. in5(int(i/2) + 1)) stop 51; else - if (ichar(out10(i)) .ne. 0) stop 50; + if (ichar(out10(i)) .ne. 0) stop 52; endif end do - print *, 'Testing g2_sbytec() with iskip of 1...' + print *, ' testing g2_sbytec() with iskip of 1...' in(1) = 1 out(1) = char(0) call g2_sbytec(out, in, 1, 6) - ! print '(z2.2)', out(1) - if (ichar(out(1)) .ne. 2) stop 20 + if (ichar(out(1)) .ne. 2) stop 53 - print *, 'Testing g2_sbytesc() with a small array...' + print *, ' testing g2_sbytesc() with a size 4 output array...' iskip = 0 nbits = 32 nskip = 0 num = 1 in(1) = 1 call g2_sbytesc(out4, in, iskip, nbits, nskip, num) - if (ichar(out4(1)) .ne. 0 .and. ichar(out4(2)) .ne. 0 .and. ichar(out4(3)) .ne. 0 .and. ichar(out4(4)) .ne. 1) stop 50 - !print '(z2.2)', out4(1) + if (ichar(out4(1)) .ne. 0 .and. ichar(out4(2)) .ne. 0 .and. & + ichar(out4(3)) .ne. 0 .and. ichar(out4(4)) .ne. 1) stop 60 + + print *, ' now unpack into 4 ints with g2_gbytesc()...' + call g2_gbytesc(out4, in4, iskip, 8, 0, 4) + do i = 1, 4 + if (i < 4) then + if (in4(i) .ne. 0) stop 61 + else + if (in4(i) .ne. 1) stop 62 + endif + end do + print *, ' now unpack into 1 int with g2_gbytesc()...' + call g2_gbytesc(out4, in1, iskip, 32, 0, 1) + if (in1(1) .ne. 1) stop 70 + ! For this test to pass the -fallow-argument-mismatch flag must be ! used, because I am passing in a real array instead of an int array ! for the in parameter. This is how g2_sbytesc() is called in ! addfield.F90. - print *, 'Testing g2_sbytesc() with a real array (size 1) instead of an int array...' + print *, ' testing g2_sbytesc() with a real array (size 1) instead of an int array...' iskip = 0 nbits = 32 nskip = 0 @@ -122,14 +144,13 @@ program test_gbytec call g2_sbytesc(out4, r_in, iskip, nbits, nskip, num) ! Note that the 32-bit IEEE representation of 1.0 is 3f800000. The ! decimal for 3f is 63, the decimal for 80 is 128. - if (ichar(out4(1)) .ne. 63 .and. ichar(out4(2)) .ne. 128 .and. ichar(out4(3)) .ne. 0 .and. ichar(out4(4)) .ne. 0) stop 50 + if (ichar(out4(1)) .ne. 63 .and. ichar(out4(2)) .ne. 128 .and. & + ichar(out4(3)) .ne. 0 .and. ichar(out4(4)) .ne. 0) stop 80 ! print '(z2.2)', out4(1) - ! print '(z2.2)', out4(2) - ! print '(z2.2)', out4(3) - ! print '(z2.2)', out4(4) - ! This test is the same as above, but does not require the -fallow-argument-mismatch flag. - print *, 'Testing g2_sbytesc() with a real array (size 1) instead of an int array, using transfer() intrinsic...' + ! This test is the same as above, but does not require the + ! -fallow-argument-mismatch flag. + print *, ' testing g2_sbytesc() with a size 1 real array instead of an int array, using transfer()...' iskip = 0 nbits = 32 nskip = 0 @@ -139,14 +160,14 @@ program test_gbytec call g2_sbytesc(out4, in, iskip, nbits, nskip, num) ! Note that the 32-bit IEEE representation of 1.0 is 3f800000. The ! decimal for 3f is 63, the decimal for 80 is 128. - if (ichar(out4(1)) .ne. 63 .and. ichar(out4(2)) .ne. 128 .and. ichar(out4(3)) .ne. 0 .and. ichar(out4(4)) .ne. 0) stop 50 - ! print '(z2.2)', out4(1) + if (ichar(out4(1)) .ne. 63 .and. ichar(out4(2)) .ne. 128 .and. & + ichar(out4(3)) .ne. 0 .and. ichar(out4(4)) .ne. 0) stop 90 ! For this test to pass the -fallow-argument-mismatch flag must be ! used, because I am passing in a real array instead of an int array ! for the in parameter. This is how g2_sbytesc() is called in ! addfield.F90. - print *, 'Testing g2_sbytesc() with a real array instead of an int array...' + print *, ' testing g2_sbytesc() with a real array instead of an int array...' iskip = 0 nbits = 32 nskip = 0 @@ -156,12 +177,14 @@ program test_gbytec call g2_sbytesc(out8, r_in2, iskip, nbits, nskip, num) ! Note that the 32-bit IEEE representation of 1.0 is 3f800000. The ! decimal for 3f is 63, the decimal for 80 is 128. - if (ichar(out8(1)) .ne. 63 .and. ichar(out8(2)) .ne. 128 .and. ichar(out8(3)) .ne. 0 .and. ichar(out8(4)) .ne. 0) stop 50 - if (ichar(out8(5)) .ne. 63 .and. ichar(out8(6)) .ne. 128 .and. ichar(out8(7)) .ne. 0 .and. ichar(out8(8)) .ne. 0) stop 50 + if (ichar(out8(1)) .ne. 63 .and. ichar(out8(2)) .ne. 128 .and. & + ichar(out8(3)) .ne. 0 .and. ichar(out8(4)) .ne. 0) stop 100 + if (ichar(out8(5)) .ne. 63 .and. ichar(out8(6)) .ne. 128 .and. & + ichar(out8(7)) .ne. 0 .and. ichar(out8(8)) .ne. 0) stop 110 ! print '(z2.2)', out8(1) ! This test is the same as above, but does not require the -fallow-argument-mismatch flag. - print *, 'Testing g2_sbytesc() with a real array instead of an int array, using transfer() intrinsic...' + print *, ' testing g2_sbytesc() with a real array instead of an int array, using transfer() intrinsic...' iskip = 0 nbits = 32 nskip = 0 @@ -172,10 +195,73 @@ program test_gbytec call g2_sbytesc(out8, in2, iskip, nbits, nskip, num) ! Note that the 32-bit IEEE representation of 1.0 is 3f800000. The ! decimal for 3f is 63, the decimal for 80 is 128. - if (ichar(out4(1)) .ne. 63 .and. ichar(out4(2)) .ne. 128 .and. ichar(out4(3)) .ne. 0 .and. ichar(out4(4)) .ne. 0) stop 50 - if (ichar(out8(5)) .ne. 63 .and. ichar(out8(6)) .ne. 128 .and. ichar(out8(7)) .ne. 0 .and. ichar(out8(8)) .ne. 0) stop 50 + if (ichar(out4(1)) .ne. 63 .and. ichar(out4(2)) .ne. 128 .and. & + ichar(out4(3)) .ne. 0 .and. ichar(out4(4)) .ne. 0) stop 120 + if (ichar(out8(5)) .ne. 63 .and. ichar(out8(6)) .ne. 128 .and. & + ichar(out8(7)) .ne. 0 .and. ichar(out8(8)) .ne. 0) stop 130 ! print '(z2.2)', out4(1) + print *, ' testing g2_sbytec() with 64-bit int...' + in8(1) = 1 + do i = 1, 8 + out8(i) = char(0) + end do + call g2_sbytec(out8, in8, iskip, 64) + do i = 1, 8 + !print '(z2.2)', out8(i) + if (i .lt. 8) then + if (ichar(out8(i)) .ne. 0) stop 140 + else + if (ichar(out8(i)) .ne. 1) stop 140 + endif + end do + + print *, ' now unpack into 1 64-bit int with g2_gbytesc()...' + in8_1(1) = 0 + call g2_gbytesc(out8, in8_1, iskip, 64, 0, 1) + if (in8_1(1) .ne. in8(1)) stop 150 + + print *, ' testing g2_sbytec() with 32-bit int array of size 4...' + do i = 1, 4 + in44(i) = 1 + end do + do i = 1, 16 + out16(i) = char(0) + end do + call g2_sbytesc(out16, in44, iskip, 32, 0, 4) + do i = 1, 16 + print '(i3, x, z2.2)', i, out16(i) + end do + + print *, ' now unpack into 4 32-bit ints with g2_gbytesc()...' + do i = 1, 4 + in44_1(i) = 0 + end do + call g2_gbytesc(out16, in44_1, iskip, 32, 0, 4) + do i = 1, 4 + if (in44_1(i) .ne. in44(i)) stop 160 + end do + + print *, ' testing g2_sbytec() with 64-bit int array of size 4...' + do i = 1, 4 + in84(i) = 1 + end do + do i = 1, 32 + out32(i) = char(0) + end do + print *, in84 + call g2_sbytesc(out32, in84, iskip, 64, 0, 4) + do i = 1, 32 + print '(i3, x, z2.2)', i, out32(i) + end do + + print *, ' now unpack into 4 64-bit ints with g2_gbytesc()...' + do i = 1, 4 + in84_1(i) = 0 + end do + call g2_gbytesc(out32, in84_1, iskip, 64, 0, 4) + print *, in84_1 + print *, 'SUCCESS!' end program test_gbytec diff --git a/tests/test_getg2ir.F90 b/tests/test_getg2ir.F90 index 268817ac..409cfe03 100644 --- a/tests/test_getg2ir.F90 +++ b/tests/test_getg2ir.F90 @@ -10,6 +10,7 @@ program test_getg2ir integer :: lugb = 3 character(len=1), pointer, dimension(:) :: cbuf(:) integer :: msk1, msk2, mnum + integer (kind = 8) :: msk18, msk28 integer :: nlen, nnum, nmess, iret interface @@ -20,58 +21,79 @@ subroutine getg2ir(lugb, msk1, msk2, mnum, cbuf, nlen, nnum, nmess, iret) end subroutine getg2ir end interface + interface + subroutine getg2i2r(lugb, msk18, msk28, mnum, idxver, cbuf, nlen, & + nnum, nmess, iret) + character(len = 1), pointer, dimension(:) :: cbuf + integer, intent(in) :: lugb + integer (kind = 8), intent(in) :: msk18, msk28 + integer, intent(in) :: mnum, idxver + integer, intent(out) :: nlen, nnum, nmess, iret + end subroutine getg2i2r + end interface integer :: index_rec_len, b2s_message, b2s_lus, b2s_gds, b2s_pds, b2s_drs, b2s_bms, b2s_data integer :: total_bytes, grib_version, discipline, field_number + integer :: idxver, t + + print *, 'Testing the getg2ir()/getg2i2r() subroutines.' - print *, 'Testing the getg2ir() subroutine - expect and ignore error messages during test...' + do t = 1, 2 + ! Open a real GRIB2 file. + print *, 'Indexing a real GRIB2 file, trial ', t + call baopenr(lugb, "data/WW3_Regional_US_West_Coast_20220718_0000.grib2", iret) + if (iret .ne. 0) stop 100 - ! Open a real GRIB2 file. - print *, 'Indexing a real GRIB2 file...' - call baopenr(lugb, "data/WW3_Regional_US_West_Coast_20220718_0000.grib2", iret) - if (iret .ne. 0) stop 100 + msk1 = 1000 + msk2 = 1000 + msk18 = 1000 + msk28 = 1000 + mnum = 0 + if (t .eq. 1) then + call getg2ir(lugb, msk1, msk2, mnum, cbuf, nlen, nnum, nmess, iret) + else + idxver = 1 + call getg2i2r(lugb, msk18, msk28, mnum, idxver, cbuf, nlen, nnum, nmess, iret) + endif + if (iret .ne. 0) stop 101 + if (nlen .ne. 137600 .or. nnum .ne. 688 .or. nmess .ne. 688) stop 102 + print *, 'nlen, nnum, nmess: ', nlen, nnum, nmess - msk1 = 1000 - msk2 = 1000 - mnum = 0 - call getg2ir(lugb, msk1, msk2, mnum, cbuf, nlen, nnum, nmess, iret) - if (iret .ne. 0) stop 101 - if (nlen .ne. 137600 .or. nnum .ne. 688 .or. nmess .ne. 688) stop 102 - print *, 'nlen, nnum, nmess: ', nlen, nnum, nmess - - ! Break out the index record into component values. - call g2_gbytec(cbuf, index_rec_len, 0, 8 * 4) - if (index_rec_len .ne. 200) stop 105 - call g2_gbytec(cbuf, b2s_message, 8 * 4, 8 * 4) - if (b2s_message .ne. 202) stop 106 - call g2_gbytec(cbuf, b2s_lus, 8 * 8, 8 * 4) - if (b2s_lus .ne. 0) stop 107 - call g2_gbytec(cbuf, b2s_gds, 8 * 12, 8 * 4) - if (b2s_gds .ne. 37) stop 108 - call g2_gbytec(cbuf, b2s_pds, 8 * 16, 8 * 4) - if (b2s_pds .ne. 109) stop 109 - call g2_gbytec(cbuf, b2s_drs, 8 * 20, 8 * 4) - if (b2s_drs .ne. 143) stop 110 - call g2_gbytec(cbuf, b2s_bms, 8 * 24, 8 * 4) - if (b2s_bms .ne. 166) stop 111 - call g2_gbytec(cbuf, b2s_data, 8 * 28, 8 * 4) - if (b2s_data .ne. 4721) stop 112 - call g2_gbytec(cbuf, total_bytes, 8 * 32, 8 * 8) - if (total_bytes .ne. 11183) stop 113 - call g2_gbytec(cbuf, grib_version, 8 * 40, 8 * 1) - if (grib_version .ne. 2) stop 113 - call g2_gbytec(cbuf, discipline, 8 * 41, 8 * 1) - if (discipline .ne. 10) stop 113 - call g2_gbytec(cbuf, field_number, 8 * 42, 8 * 2) - if (field_number .ne. 1) stop 113 - print *, 'index_rec_len = ', index_rec_len, ' b2s_message = ', b2s_message - print *, 'b2s_lus, b2s_gds, b2s_pds, b2s_drs, b2s_bms, b2s_data: ', b2s_lus, b2s_gds, b2s_pds, b2s_drs, b2s_bms, b2s_data - print *, 'total_bytes, grib_version, discipline, field_number: ', total_bytes, grib_version, discipline, field_number + ! Break out the index record into component values. + call g2_gbytec(cbuf, index_rec_len, 0, 8 * 4) + if (index_rec_len .ne. 200) stop 105 + call g2_gbytec(cbuf, b2s_message, 8 * 4, 8 * 4) + if (b2s_message .ne. 202) stop 106 + call g2_gbytec(cbuf, b2s_lus, 8 * 8, 8 * 4) + if (b2s_lus .ne. 0) stop 107 + call g2_gbytec(cbuf, b2s_gds, 8 * 12, 8 * 4) + if (b2s_gds .ne. 37) stop 108 + call g2_gbytec(cbuf, b2s_pds, 8 * 16, 8 * 4) + if (b2s_pds .ne. 109) stop 109 + call g2_gbytec(cbuf, b2s_drs, 8 * 20, 8 * 4) + if (b2s_drs .ne. 143) stop 110 + call g2_gbytec(cbuf, b2s_bms, 8 * 24, 8 * 4) + if (b2s_bms .ne. 166) stop 111 + call g2_gbytec(cbuf, b2s_data, 8 * 28, 8 * 4) + if (b2s_data .ne. 4721) stop 112 + call g2_gbytec(cbuf, total_bytes, 8 * 32, 8 * 8) + if (total_bytes .ne. 11183) stop 113 + call g2_gbytec(cbuf, grib_version, 8 * 40, 8 * 1) + if (grib_version .ne. 2) stop 113 + call g2_gbytec(cbuf, discipline, 8 * 41, 8 * 1) + if (discipline .ne. 10) stop 113 + call g2_gbytec(cbuf, field_number, 8 * 42, 8 * 2) + if (field_number .ne. 1) stop 113 + print *, 'index_rec_len = ', index_rec_len, ' b2s_message = ', b2s_message + print *, 'b2s_lus, b2s_gds, b2s_pds, b2s_drs, b2s_bms, b2s_data: ', b2s_lus, b2s_gds, b2s_pds, b2s_drs, b2s_bms, b2s_data + print *, 'total_bytes, grib_version, discipline, field_number: ', total_bytes, grib_version, discipline, field_number - deallocate(cbuf) - - call baclose(lugb, iret) - if (iret .ne. 0) stop 199 + deallocate(cbuf) + cbuf => null() + call baclose(lugb, iret) + if (iret .ne. 0) stop 199 + print *, 'OK!' + end do print *, 'SUCCESS!...' end program test_getg2ir