From 3d9b90374601928d16e6e74d75ad22e9dd7d0385 Mon Sep 17 00:00:00 2001 From: Eric Stofferahn <7784797+GFDL-Eric@users.noreply.github.com> Date: Tue, 29 Dec 2020 18:23:49 -0500 Subject: [PATCH 1/8] Added ascii_read subroutine to fms_io_utils in fms2_io plus testing --- fms2_io/fms2_io.F90 | 1 + fms2_io/fms_io_utils.F90 | 10 ++ mpp/include/mpp_util.inc | 68 +++++++++++++ mpp/mpp.F90 | 2 +- test_fms/fms2_io/Makefile.am | 7 +- test_fms/fms2_io/ascii_test1 | 3 + test_fms/fms2_io/test_read_ascii_file.F90 | 115 ++++++++++++++++++++++ test_fms/fms2_io/test_read_ascii_file.sh | 31 ++++++ 8 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 test_fms/fms2_io/ascii_test1 create mode 100644 test_fms/fms2_io/test_read_ascii_file.F90 create mode 100755 test_fms/fms2_io/test_read_ascii_file.sh diff --git a/fms2_io/fms2_io.F90 b/fms2_io/fms2_io.F90 index 7d102e5450..5c402a38a9 100644 --- a/fms2_io/fms2_io.F90 +++ b/fms2_io/fms2_io.F90 @@ -85,6 +85,7 @@ module fms2_io_mod public :: is_dimension_registered public :: fms2_io_init public :: get_mosaic_tile_grid +public :: ascii_read interface open_file module procedure netcdf_file_open_wrap diff --git a/fms2_io/fms_io_utils.F90 b/fms2_io/fms_io_utils.F90 index 9df43284c9..0b45f9393d 100644 --- a/fms2_io/fms_io_utils.F90 +++ b/fms2_io/fms_io_utils.F90 @@ -22,6 +22,7 @@ !! @email gfdl.climate.model.info@noaa.gov module fms_io_utils_mod use, intrinsic :: iso_fortran_env, only: error_unit +use mpp_mod, only : get_ascii_file_num_lines_and_length, read_ascii_file #ifdef _OPENMP use omp_lib #endif @@ -48,6 +49,7 @@ module fms_io_utils_mod public :: open_check public :: string_compare public :: restart_filepath_mangle +public :: ascii_read !> @brief A linked list of strings type :: char_linked_list @@ -456,6 +458,14 @@ subroutine open_check(flag, fname) endif end subroutine open_check +subroutine ascii_read(ascii_filename, ascii_var) + character(len=*), intent(in) :: ascii_filename + character(len=:), dimension(:), allocatable, intent(out) :: ascii_var + integer, dimension(2) :: lines_and_length !lines = 1, length = 2 + lines_and_length = get_ascii_file_num_lines_and_length(ascii_filename) + allocate(character(len=lines_and_length(2))::ascii_var(lines_and_length(1))) + call read_ascii_file(ascii_filename, lines_and_length(2), ascii_var) +end subroutine ascii_read include "array_utils.inc" include "array_utils_char.inc" diff --git a/mpp/include/mpp_util.inc b/mpp/include/mpp_util.inc index 6da06d8057..5f21e31214 100644 --- a/mpp/include/mpp_util.inc +++ b/mpp/include/mpp_util.inc @@ -1408,6 +1408,74 @@ end function rarray_to_char end function get_ascii_file_num_lines + !####################################################################### + function get_ascii_file_num_lines_and_length(FILENAME, PELIST) + character(len=*), intent(in) :: FILENAME + integer, intent(in), optional, dimension(:) :: PELIST + + integer, dimension(2) :: get_ascii_file_num_lines_and_length + integer :: num_lines, max_length + integer, parameter :: LENGTH=1024 + character(len=LENGTH) :: str_tmp + character(len=5) :: text + integer :: status, f_unit, from_pe + logical :: file_exist + + if( read_ascii_file_on) then + call mpp_error(FATAL, & + "mpp_util.inc: get_ascii_file_num_lines is called again before calling read_ascii_file") + endif + read_ascii_file_on = .true. + + from_pe = root_pe + get_ascii_file_num_lines_and_length = -1 + num_lines = -1 + max_length = -1 + if ( pe == root_pe ) then + inquire(FILE=FILENAME, EXIST=file_exist) + + if ( file_exist ) then + f_unit = get_unit() + open(UNIT=f_unit, FILE=FILENAME, ACTION='READ', STATUS='OLD', IOSTAT=status) + + if ( status .ne. 0 ) then + write (UNIT=text, FMT='(I5)') status + call mpp_error(FATAL, 'get_ascii_file_num_lines: Error opening file:' //trim(FILENAME)// & + '. (IOSTAT = '//trim(text)//')') + else + num_lines = 1 + do + read (UNIT=f_unit, FMT='(A)', IOSTAT=status) str_tmp + if ( status .lt. 0 ) exit + if ( status .gt. 0 ) then + write (UNIT=text, FMT='(I5)') num_lines + call mpp_error(FATAL, 'get_ascii_file_num_lines: Error reading line '//trim(text)// & + ' in file '//trim(FILENAME)//'.') + end if + if ( len_trim(str_tmp) == LENGTH ) then + write(UNIT=text, FMT='(I5)') length + call mpp_error(FATAL, 'get_ascii_file_num_lines: Length of output string ('//trim(text)//' is too small.& + & Increase the LENGTH value.') + end if + if (len_trim(str_tmp) > max_length) max_length = len_trim(str_tmp) + num_lines = num_lines + 1 + end do + close(UNIT=f_unit) + end if + else + call mpp_error(FATAL, 'get_ascii_file_num_lines: File '//trim(FILENAME)//' does not exist.') + end if + max_length = max_length+1 + end if + + ! Broadcast number of lines + call mpp_broadcast(num_lines, from_pe, PELIST=PELIST) + call mpp_broadcast(max_length, from_pe, PELIST=PELIST) + get_ascii_file_num_lines_and_length(1) = num_lines + get_ascii_file_num_lines_and_length(2) = max_length + + end function get_ascii_file_num_lines_and_length + !----------------------------------------------------------------------- ! ! AUTHOR: Rusty Benson , diff --git a/mpp/mpp.F90 b/mpp/mpp.F90 index 35d0e9b6d2..25bd9b8c03 100644 --- a/mpp/mpp.F90 +++ b/mpp/mpp.F90 @@ -215,7 +215,7 @@ module mpp_mod public :: mpp_get_current_pelist, mpp_set_current_pelist, mpp_get_current_pelist_name public :: mpp_clock_id, mpp_clock_set_grain, mpp_record_timing_data, get_unit public :: read_ascii_file, read_input_nml, mpp_clock_begin, mpp_clock_end - public :: get_ascii_file_num_lines + public :: get_ascii_file_num_lines, get_ascii_file_num_lines_and_length public :: mpp_record_time_start, mpp_record_time_end !--- public interface from mpp_comm.h ------------------------------ diff --git a/test_fms/fms2_io/Makefile.am b/test_fms/fms2_io/Makefile.am index 54abfa3880..a63d7c2382 100644 --- a/test_fms/fms2_io/Makefile.am +++ b/test_fms/fms2_io/Makefile.am @@ -30,7 +30,7 @@ LDADD = $(top_builddir)/libFMS/libFMS.la # Build this test program. check_PROGRAMS = test_get_is_valid test_fms2_io test_atmosphere_io test_io_simple test_io_with_mask test_global_att \ - test_get_mosaic_tile_grid + test_get_mosaic_tile_grid test_read_ascii_file # This is the source code for the test. test_get_is_valid_SOURCES = test_get_is_valid.F90 @@ -44,8 +44,9 @@ test_io_simple_SOURCES = test_io_simple.F90 argparse.F90 setup.F90 test_global_att_SOURCES = test_global_att.F90 test_io_with_mask_SOURCES=test_io_with_mask.F90 test_get_mosaic_tile_grid_SOURCES=test_get_mosaic_tile_grid.F90 +test_read_ascii_file_SOURCES=test_read_ascii_file.F90 ascii_test1 -EXTRA_DIST = test_fms2_io.sh test_atmosphere_io.sh test_io_simple.sh test_global_att.sh test_io_with_mask.sh +EXTRA_DIST = test_fms2_io.sh test_atmosphere_io.sh test_io_simple.sh test_global_att.sh test_io_with_mask.sh test_read_ascii_file.sh argparse.mod: argparse.$(OBJEXT) setup.mod: setup.$(OBJEXT) @@ -55,7 +56,7 @@ test_io_simple.$(OBJEXT): setup.mod test_fms2_io.$(OBJEXT): argparse.mod # Run the test program. -TESTS = test_fms2_io.sh test_atmosphere_io.sh test_io_simple.sh test_io_with_mask.sh test_global_att.sh +TESTS = test_fms2_io.sh test_atmosphere_io.sh test_io_simple.sh test_io_with_mask.sh test_global_att.sh test_read_ascii_file.sh # skips tests that fail if using netCDF 4.7.4 if SKIP_FMS2_IO_TESTS diff --git a/test_fms/fms2_io/ascii_test1 b/test_fms/fms2_io/ascii_test1 new file mode 100644 index 0000000000..43aac0633e --- /dev/null +++ b/test_fms/fms2_io/ascii_test1 @@ -0,0 +1,3 @@ +3, 4, 5, "fort", "global" +"dovalia" "flatugula" +99 431 "boolakah" diff --git a/test_fms/fms2_io/test_read_ascii_file.F90 b/test_fms/fms2_io/test_read_ascii_file.F90 new file mode 100644 index 0000000000..d764d54982 --- /dev/null +++ b/test_fms/fms2_io/test_read_ascii_file.F90 @@ -0,0 +1,115 @@ +!*********************************************************************** +!* GNU Lesser General Public License +!* +!* This file is part of the GFDL Flexible Modeling System (FMS). +!* +!* FMS is free software: you can redistribute it and/or modify it under +!* the terms of the GNU Lesser General Public License as published by +!* the Free Software Foundation, either version 3 of the License, or (at +!* your option) any later version. +!* +!* FMS is distributed in the hope that it will be useful, but WITHOUT +!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +!* for more details. +!* +!* You should have received a copy of the GNU Lesser General Public +!* License along with FMS. If not, see . +!*********************************************************************** + +!> @file +!! @brief Tests the read_ascii_file subroutine +!! @author Colin Gladue +!! @email gfdl.climate.model.info@noaa.gov + +program test_read_ascii_file + + use mpp_mod, only : mpp_init + use mpp_mod, only : mpp_error, FATAL, NOTE + use fms2_io_mod, only : fms2_io_init, ascii_read + + character(len=:), dimension(:), allocatable :: test_array !< Content array + character(len=256) :: filename !< Name of ascii file to be read + character(len=256) :: filename2 !< Name of alternative ascii file to be read + character(len=256) :: line !< Content of a line of the read ascii file + integer :: num_lines !< Number of lines in the ascii file + integer :: stat !< IOSTATUS from the read method + integer, allocatable :: cur_pelist(:) !< PELIST is read into this variable + integer :: ierr !< used by MPI_FINALIZE + +! namelist /test_read_ascii_file_nml/ test_numb + +! open(20, file="test_numb_ascii.nml", form="formatted", status="old") +! read(20, nml=test_read_ascii_file_nml) +! close(20) + + ! Tests not meant to raise errors + call mpp_init() + call fms2_io_init() + filename = "ascii_test1" + call ascii_read(filename, test_array) +! if (test_numb == 1 .or. test_numb == 7 .or. test_numb == 8) then +! if (test_numb == 1) then +! filename = "input.nml" +! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) +! allocate(test_array(num_lines)) +! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) +! else if (test_numb == 7) then +! filename = "input.nml" +! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) +! allocate(test_array(num_lines)) +! allocate(cur_pelist(0:mpp_npes()-1)) +! call mpp_get_current_pelist(cur_pelist) +! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array, PELIST=cur_pelist) +! else if (test_numb == 8) then +! filename = "empty.nml" +! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) +! allocate(test_array(num_lines)) +! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) +! end if +! ! Content check +! open(2, file=filename, iostat=stat) +! do i=1, num_lines-1 +! read(2, '(A)', iostat=stat) line +! if (stat.eq.-1) then +! call mpp_error(FATAL, "Problem reading the ascii file") +! end if +! if (test_array(i).ne.line) then +! call mpp_error(FATAL, "Content array variable does not& +! & match the ascii file content") +! end if +! end do +! ! Tests meant to raise errors +! else +! if (test_numb == 2) then +! filename = "input.nml" +! allocate(test_array(20)) +! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) +! else if (test_numb == 3) then +! filename = "doesnotexist.txt" +! ! Need to pass in an exist file name below to avoid raising error on +! ! get_ascii_file_num_lines call in order to get to the error in read_ascii_file +! filename2 = "input.nml" +! num_lines = get_ascii_file_num_lines(filename2, INPUT_STR_LENGTH) +! allocate(test_array(num_lines)) +! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) +! else if (test_numb == 4) then +! filename = "input.nml" +! filename2 = "empty.nml" +! num_lines = get_ascii_file_num_lines(filename2, INPUT_STR_LENGTH) +! allocate(test_array(num_lines)) +! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) +! else if (test_numb == 5) then +! filename = "input.nml" +! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) +! allocate(test_array(num_lines)) +! call read_ascii_file(filename, 0, test_array) +! else if (test_numb == 6) then +! filename = "input.nml" +! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) +! allocate(test_array(num_lines-1)) +! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) +! end if +! end if + call MPI_FINALIZE(ierr) +end program test_read_ascii_file diff --git a/test_fms/fms2_io/test_read_ascii_file.sh b/test_fms/fms2_io/test_read_ascii_file.sh new file mode 100755 index 0000000000..51b00781b4 --- /dev/null +++ b/test_fms/fms2_io/test_read_ascii_file.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +#*********************************************************************** +#* GNU Lesser General Public License +#* +#* This file is part of the GFDL Flexible Modeling System (FMS). +#* +#* FMS is free software: you can redistribute it and/or modify it under +#* the terms of the GNU Lesser General Public License as published by +#* the Free Software Foundation, either version 3 of the License, or (at +#* your option) any later version. +#* +#* FMS is distributed in the hope that it will be useful, but WITHOUT +#* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +#* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +#* for more details. +#* +#* You should have received a copy of the GNU Lesser General Public +#* License along with FMS. If not, see . +#*********************************************************************** + +# This is part of the GFDL FMS package. This is a shell script to +# execute tests in the test_fms/fms2_io directory. + +# Set common test settings. +. ../test_common.sh +# make an input.nml for mpp_init to read +printf "EOF\n&dummy\nEOF" | cat > input.nml +cp $top_srcdir/test_fms/fms2_io/ascii_test1 ascii_test1 +# run the tests +run_test test_read_ascii_file 6 $netcdf_version_skip From 877054082889e5983e11171a15f8d4244cf388bb Mon Sep 17 00:00:00 2001 From: Eric Stofferahn <7784797+GFDL-Eric@users.noreply.github.com> Date: Thu, 4 Mar 2021 09:58:14 -0500 Subject: [PATCH 2/8] Updated fms2_io to include parse_mask_table --- fms2_io/fms_io_utils.F90 | 162 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/fms2_io/fms_io_utils.F90 b/fms2_io/fms_io_utils.F90 index 0b45f9393d..7c54afc079 100644 --- a/fms2_io/fms_io_utils.F90 +++ b/fms2_io/fms_io_utils.F90 @@ -22,7 +22,7 @@ !! @email gfdl.climate.model.info@noaa.gov module fms_io_utils_mod use, intrinsic :: iso_fortran_env, only: error_unit -use mpp_mod, only : get_ascii_file_num_lines_and_length, read_ascii_file +!use mpp_mod, only : get_ascii_file_num_lines_and_length, read_ascii_file #ifdef _OPENMP use omp_lib #endif @@ -50,6 +50,7 @@ module fms_io_utils_mod public :: string_compare public :: restart_filepath_mangle public :: ascii_read +public :: parse_mask_table !> @brief A linked list of strings type :: char_linked_list @@ -58,6 +59,11 @@ module fms_io_utils_mod endtype char_linked_list +interface parse_mask_table + module procedure parse_mask_table_2d + module procedure parse_mask_table_3d +end interface parse_mask_table + interface allocate_array module procedure allocate_array_i4_kind_1d module procedure allocate_array_i4_kind_2d @@ -467,6 +473,160 @@ subroutine ascii_read(ascii_filename, ascii_var) call read_ascii_file(ascii_filename, lines_and_length(2), ascii_var) end subroutine ascii_read +subroutine parse_mask_table_2d(mask_table, maskmap, modelname) + + character(len=*), intent(in) :: mask_table + logical, intent(out) :: maskmap(:,:) + character(len=*), intent(in) :: modelname + integer :: nmask, layout(2) + integer, allocatable :: mask_list(:,:) + character(len=:), dimension(:), allocatable :: mask_table_contents !< Content array + integer :: mystat, n, stdoutunit + character(len=128) :: record + + maskmap = .true. + nmask = 0 + stdoutunit = stdout() + call ascii_read(mask_table, mask_table_contents) + if( mpp_pe() == mpp_root_pe() ) then + read(mask_table_contents, FMT=*, IOSTAT=mystat) nmask + write(stdoutunit,*)"parse_mask_table: Number of domain regions masked in ", trim(modelname), " = ", nmask + if( nmask > 0 ) then + !--- read layout from mask_table and confirm it matches the shape of maskmap + read(mask_table_contents, FMT=*, IOSTAT=mystat) layout + if( (layout(1) .NE. size(maskmap,1)) .OR. (layout(2) .NE. size(maskmap,2)) )then + write(stdoutunit,*)"layout=", layout, ", size(maskmap) = ", size(maskmap,1), size(maskmap,2) + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): layout in file "//trim(mask_table)// & + "does not match size of maskmap for "//trim(modelname)) + endif + !--- make sure mpp_npes() == layout(1)*layout(2) - nmask + if( mpp_npes() .NE. layout(1)*layout(2) - nmask ) call mpp_error(FATAL, & + "fms2_io(parse_mask_table_2d): mpp_npes() .NE. layout(1)*layout(2) - nmask for "//trim(modelname)) + endif + endif + + call mpp_broadcast(nmask, mpp_root_pe()) + + if(nmask==0) return + + allocate(mask_list(nmask,2)) + + if( mpp_pe() == mpp_root_pe() ) then + n = 0 + do while( .true. ) + read(mask_table_contents,'(a)',end=999) record + if (record(1:1) == '#') cycle + if (record(1:10) == ' ') cycle + n = n + 1 + if( n > nmask ) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): number of mask_list entry "// & + "is greater than nmask in file "//trim(mask_table) ) + endif + read(record,*,err=888) mask_list(n,1), mask_list(n,2) + enddo +888 call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error in reading mask_list from file "//trim(mask_table)) + +999 continue + !--- make sure the number of entry for mask_list is nmask + if( n .NE. nmask) call mpp_error(FATAL, & + "fms2_io(parse_mask_table_2d): number of mask_list entry does not match nmask in file "//trim(mask_table)) + endif + + call mpp_broadcast(mask_list, 2*nmask, mpp_root_pe()) + do n = 1, nmask + if(debug_mask_list) then + write(stdoutunit,*) "==>NOTE from parse_mask_table_2d: ", trim(modelname), " mask_list = ", mask_list(n,1), mask_list(n,2) + endif + maskmap(mask_list(n,1),mask_list(n,2)) = .false. + enddo + + deallocate(mask_list) + +end subroutine parse_mask_table_2d + + +!####################################################################### +subroutine parse_mask_table_3d(mask_table, maskmap, modelname) + + character(len=*), intent(in) :: mask_table + logical, intent(out) :: maskmap(:,:,:) + character(len=*), intent(in) :: modelname + integer :: nmask, layout(2) + integer, allocatable :: mask_list(:,:) + character(len=:), dimension(:), allocatable :: mask_table_contents !< Content array + integer :: mystat, n, stdoutunit, ntiles + character(len=128) :: record + + maskmap = .true. + nmask = 0 + stdoutunit = stdout() + call ascii_read(mask_table, mask_table_contents) + if( mpp_pe() == mpp_root_pe() ) then + read(mask_table_contents, FMT=*, IOSTAT=mystat) nmask + write(stdoutunit,*)"parse_mask_table: Number of domain regions masked in ", trim(modelname), " = ", nmask + if( nmask > 0 ) then + !--- read layout from mask_table and confirm it matches the shape of maskmap + read(mask_table_contents, FMT=*, IOSTAT=mystat) layout(1), layout(2), ntiles + if( (layout(1) .NE. size(maskmap,1)) .OR. (layout(2) .NE. size(maskmap,2)) )then + write(stdoutunit,*)"layout=", layout, ", size(maskmap) = ", size(maskmap,1), size(maskmap,2) + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): layout in file "//trim(mask_table)// & + "does not match size of maskmap for "//trim(modelname)) + endif + if( ntiles .NE. size(maskmap,3) ) then + write(stdoutunit,*)"ntiles=", ntiles, ", size(maskmap,3) = ", size(maskmap,3) + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): ntiles in file "//trim(mask_table)// & + "does not match size of maskmap for "//trim(modelname)) + endif + !--- make sure mpp_npes() == layout(1)*layout(2) - nmask + if( mpp_npes() .NE. layout(1)*layout(2)*ntiles - nmask ) then + print*, "layout=", layout, nmask, mpp_npes() + call mpp_error(FATAL, & + "fms2_io(parse_mask_table_3d): mpp_npes() .NE. layout(1)*layout(2) - nmask for "//trim(modelname)) + endif + endif + endif + + call mpp_broadcast(nmask, mpp_root_pe()) + + if(nmask==0) return + + allocate(mask_list(nmask,3)) + + if( mpp_pe() == mpp_root_pe() ) then + n = 0 + do while( .true. ) + read(mask_table_contents,'(a)',end=999) record + if (record(1:1) == '#') cycle + if (record(1:10) == ' ') cycle + n = n + 1 + if( n > nmask ) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): number of mask_list entry "// & + "is greater than nmask in file "//trim(mask_table) ) + endif + read(record,*,err=888) mask_list(n,1), mask_list(n,2), mask_list(n,3) + enddo +888 call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error in reading mask_list from file "//trim(mask_table)) + +999 continue + !--- make sure the number of entry for mask_list is nmask + if( n .NE. nmask) call mpp_error(FATAL, & + "fms2_io(parse_mask_table_3d): number of mask_list entry does not match nmask in file "//trim(mask_table)) +! call mpp_close(unit) + endif + + call mpp_broadcast(mask_list, 3*nmask, mpp_root_pe()) + do n = 1, nmask + if(debug_mask_list) then + write(stdoutunit,*) "==>NOTE from parse_mask_table_3d: ", trim(modelname), " mask_list = ", & + mask_list(n,1), mask_list(n,2), mask_list(n,3) + endif + maskmap(mask_list(n,1),mask_list(n,2),mask_list(n,3)) = .false. + enddo + + deallocate(mask_list) + +end subroutine parse_mask_table_3d + include "array_utils.inc" include "array_utils_char.inc" include "get_data_type_string.inc" From 579e7d5621cb5dbfa4c41c71387804a668548d89 Mon Sep 17 00:00:00 2001 From: Eric Stofferahn <7784797+GFDL-Eric@users.noreply.github.com> Date: Thu, 11 Mar 2021 11:46:02 -0500 Subject: [PATCH 3/8] moved get_mosaic_tile_file to fms2_io --- data_override/data_override.F90 | 12 +++-- fms2_io/fms_io_utils.F90 | 95 +++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/data_override/data_override.F90 b/data_override/data_override.F90 index 641bbe223d..26852ec124 100644 --- a/data_override/data_override.F90 +++ b/data_override/data_override.F90 @@ -62,15 +62,14 @@ module data_override_mod NO_REGION, INSIDE_REGION, OUTSIDE_REGION, & get_external_fileobj use fms_mod, only: write_version_number, field_exist, lowercase, check_nml_error -use fms_io_mod, only: fms_io_init, get_mosaic_tile_file -use axis_utils_mod, only: get_axis_bounds -use axis_utils2_mod, only : nearest_index, axis_edges +use fms_io_mod, only: fms_io_init, get_mosaic_tile_file_classic=>get_mosaic_tile_file use mpp_domains_mod, only : domain2d, mpp_get_compute_domain, NULL_DOMAIN2D,operator(.NE.),operator(.EQ.) use mpp_domains_mod, only : mpp_get_global_domain, mpp_get_data_domain use mpp_domains_mod, only : domainUG, mpp_pass_SG_to_UG, mpp_get_UG_SG_domain, NULL_DOMAINUG use time_manager_mod, only: time_type use fms2_io_mod, only : FmsNetcdfFile_t, open_file, close_file, & - read_data, fms2_io_init, variable_exists + read_data, fms2_io_init, variable_exists, & + get_mosaic_tile_file_fms2_io=>get_mosaic_tile_file use get_grid_version_mpp_mod, only: get_grid_version_classic_1, get_grid_version_classic_2 use get_grid_version_fms2io_mod, only: get_grid_version_1, get_grid_version_2 @@ -877,7 +876,10 @@ subroutine data_override_3d(gridname,fieldname_code,data,time,override,data_inde ! Allow on-grid data_overrides on cubed sphere grid inquire(file=trim(filename),EXIST=exists) if (.not. exists) then - call get_mosaic_tile_file(filename,filename2,.false.,domain) + if (use_mpp_bug) then + call get_mosaic_tile_file_classic(filename,filename2,.false.,domain) + else + call get_mosaic_tile_file_fms2_io(filename,filename2,.false.,domain) filename = filename2 endif diff --git a/fms2_io/fms_io_utils.F90 b/fms2_io/fms_io_utils.F90 index 7c54afc079..544ee05cf1 100644 --- a/fms2_io/fms_io_utils.F90 +++ b/fms2_io/fms_io_utils.F90 @@ -51,6 +51,7 @@ module fms_io_utils_mod public :: restart_filepath_mangle public :: ascii_read public :: parse_mask_table +public :: get_mosaic_tile_file !> @brief A linked list of strings type :: char_linked_list @@ -64,6 +65,11 @@ module fms_io_utils_mod module procedure parse_mask_table_3d end interface parse_mask_table +interface get_mosaic_tile_file + module procedure get_mosaic_tile_file_sg + module procedure get_mosaic_tile_file_ug +end interface get_mosaic_tile_file + interface allocate_array module procedure allocate_array_i4_kind_1d module procedure allocate_array_i4_kind_2d @@ -627,6 +633,95 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) end subroutine parse_mask_table_3d +subroutine get_mosaic_tile_file_sg(file_in, file_out, is_no_domain, domain, tile_count) + character(len=*), intent(in) :: file_in + character(len=*), intent(out) :: file_out + logical, intent(in) :: is_no_domain + type(domain2D), intent(in), optional, target :: domain + integer, intent(in), optional :: tile_count + character(len=256) :: basefile, tilename + integer :: lens, ntiles, ntileMe, tile, my_tile_id + integer, dimension(:), allocatable :: tile_id + type(domain2d), pointer, save :: d_ptr =>NULL() + logical :: domain_exist + + if(index(file_in, '.nc', back=.true.)==0) then + basefile = trim(file_in) + else + lens = len_trim(file_in) + if(file_in(lens-2:lens) .NE. '.nc') call mpp_error(FATAL, & + 'fms_io_mod: .nc should be at the end of file '//trim(file_in)) + basefile = file_in(1:lens-3) + end if + + !--- get the tile name + ntiles = 1 + my_tile_id = 1 + domain_exist = .false. + if(PRESENT(domain))then + domain_exist = .true. + ntiles = mpp_get_ntile_count(domain) + d_ptr => domain + endif + + if(domain_exist) then + ntileMe = mpp_get_current_ntile(d_ptr) + allocate(tile_id(ntileMe)) + tile_id = mpp_get_tile_id(d_ptr) + tile = 1 + if(present(tile_count)) tile = tile_count + my_tile_id = tile_id(tile) + endif + + if(ntiles > 1 .or. my_tile_id > 1 )then + tilename = 'tile'//string(my_tile_id) + if(index(basefile,'.'//trim(tilename),back=.true.) == 0)then + basefile = trim(basefile)//'.'//trim(tilename); + end if + end if + if(allocated(tile_id)) deallocate(tile_id) + + file_out = trim(basefile)//'.nc' + + d_ptr =>NULL() + +end subroutine get_mosaic_tile_file_sg + +subroutine get_mosaic_tile_file_ug(file_in, file_out, domain) + character(len=*), intent(in) :: file_in + character(len=*), intent(out) :: file_out + type(domainUG), intent(in), optional :: domain + character(len=256) :: basefile, tilename + integer :: lens, ntiles, my_tile_id + + if(index(file_in, '.nc', back=.true.)==0) then + basefile = trim(file_in) + else + lens = len_trim(file_in) + if(file_in(lens-2:lens) .NE. '.nc') call mpp_error(FATAL, & + 'fms_io_mod: .nc should be at the end of file '//trim(file_in)) + basefile = file_in(1:lens-3) + end if + + !--- get the tile name + ntiles = 1 + my_tile_id = 1 + if(PRESENT(domain))then + ntiles = mpp_get_UG_domain_ntiles(domain) + my_tile_id = mpp_get_UG_domain_tile_id(domain) + endif + + if(ntiles > 1 .or. my_tile_id > 1 )then + tilename = 'tile'//string(my_tile_id) + if(index(basefile,'.'//trim(tilename),back=.true.) == 0)then + basefile = trim(basefile)//'.'//trim(tilename); + end if + end if + + file_out = trim(basefile)//'.nc' + +end subroutine get_mosaic_tile_file_ug + include "array_utils.inc" include "array_utils_char.inc" include "get_data_type_string.inc" From e9b70ed4a2a87d06f95820f90c80356d1cdcb9be Mon Sep 17 00:00:00 2001 From: Eric Stofferahn <7784797+GFDL-Eric@users.noreply.github.com> Date: Fri, 12 Mar 2021 14:47:01 -0500 Subject: [PATCH 4/8] Added doxygen to new subroutines and functions --- fms2_io/fms_io_utils.F90 | 59 ++++++++++++++++++++++++---------------- mpp/include/mpp_util.inc | 8 ++++-- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/fms2_io/fms_io_utils.F90 b/fms2_io/fms_io_utils.F90 index 544ee05cf1..0f9c59f2cb 100644 --- a/fms2_io/fms_io_utils.F90 +++ b/fms2_io/fms_io_utils.F90 @@ -470,23 +470,29 @@ subroutine open_check(flag, fname) endif end subroutine open_check +!> @brief Read the ascii text from filename `ascii_filename`into string array +!! `ascii_var` subroutine ascii_read(ascii_filename, ascii_var) - character(len=*), intent(in) :: ascii_filename - character(len=:), dimension(:), allocatable, intent(out) :: ascii_var - integer, dimension(2) :: lines_and_length !lines = 1, length = 2 + character(len=*), intent(in) :: ascii_filename !< The file name to be read + character(len=:), dimension(:), allocatable, intent(out) :: ascii_var !< The + !! string + !! array + integer, dimension(2) :: lines_and_length !< lines = 1, length = 2 lines_and_length = get_ascii_file_num_lines_and_length(ascii_filename) allocate(character(len=lines_and_length(2))::ascii_var(lines_and_length(1))) call read_ascii_file(ascii_filename, lines_and_length(2), ascii_var) end subroutine ascii_read +!> @brief Populate 2D maskmap from mask_table given a model subroutine parse_mask_table_2d(mask_table, maskmap, modelname) - character(len=*), intent(in) :: mask_table - logical, intent(out) :: maskmap(:,:) - character(len=*), intent(in) :: modelname + character(len=*), intent(in) :: mask_table !< Mask table to be read in + logical, intent(out) :: maskmap(:,:) !< 2D Mask output + character(len=*), intent(in) :: modelname !< Model to which this applies + integer :: nmask, layout(2) integer, allocatable :: mask_list(:,:) - character(len=:), dimension(:), allocatable :: mask_table_contents !< Content array + character(len=:), dimension(:), allocatable :: mask_table_contents integer :: mystat, n, stdoutunit character(len=128) :: record @@ -551,15 +557,16 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) end subroutine parse_mask_table_2d -!####################################################################### +!> @brief Populate 3D maskmap from mask_table given a model subroutine parse_mask_table_3d(mask_table, maskmap, modelname) - character(len=*), intent(in) :: mask_table - logical, intent(out) :: maskmap(:,:,:) - character(len=*), intent(in) :: modelname + character(len=*), intent(in) :: mask_table !< Mask table to be read in + logical, intent(out) :: maskmap(:,:,:) !< 2D Mask output + character(len=*), intent(in) :: modelname !< Model to which this applies + integer :: nmask, layout(2) integer, allocatable :: mask_list(:,:) - character(len=:), dimension(:), allocatable :: mask_table_contents !< Content array + character(len=:), dimension(:), allocatable :: mask_table_contents integer :: mystat, n, stdoutunit, ntiles character(len=128) :: record @@ -630,15 +637,18 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) enddo deallocate(mask_list) - end subroutine parse_mask_table_3d +!> @brief Determine tile_file for structured grid based on filename and current +!! tile on mpp_domain (this is mostly used for ongrid data_overrides) subroutine get_mosaic_tile_file_sg(file_in, file_out, is_no_domain, domain, tile_count) - character(len=*), intent(in) :: file_in - character(len=*), intent(out) :: file_out - logical, intent(in) :: is_no_domain - type(domain2D), intent(in), optional, target :: domain - integer, intent(in), optional :: tile_count + character(len=*), intent(in) :: file_in !< name of 'base' file + character(len=*), intent(out) :: file_out !< name of tile_file + logical, intent(in) :: is_no_domain !< are we providing a + !! domain + type(domain2D), intent(in), optional, target :: domain !< domain provided + integer, intent(in), optional :: tile_count !< tile count + character(len=256) :: basefile, tilename integer :: lens, ntiles, ntileMe, tile, my_tile_id integer, dimension(:), allocatable :: tile_id @@ -687,12 +697,15 @@ subroutine get_mosaic_tile_file_sg(file_in, file_out, is_no_domain, domain, tile end subroutine get_mosaic_tile_file_sg +!> @brief Determine tile_file for unstructured grid based on filename and current +!! tile on mpp_domain (this is mostly used for ongrid data_overrides) subroutine get_mosaic_tile_file_ug(file_in, file_out, domain) - character(len=*), intent(in) :: file_in - character(len=*), intent(out) :: file_out - type(domainUG), intent(in), optional :: domain - character(len=256) :: basefile, tilename - integer :: lens, ntiles, my_tile_id + character(len=*), intent(in) :: file_in !< name of base file + character(len=*), intent(out) :: file_out !< name of tile file + type(domainUG), intent(in), optional :: domain !< domain provided + + character(len=256) :: basefile, tilename + integer :: lens, ntiles, my_tile_id if(index(file_in, '.nc', back=.true.)==0) then basefile = trim(file_in) diff --git a/mpp/include/mpp_util.inc b/mpp/include/mpp_util.inc index 5f21e31214..51a391bf1a 100644 --- a/mpp/include/mpp_util.inc +++ b/mpp/include/mpp_util.inc @@ -1409,11 +1409,13 @@ end function rarray_to_char end function get_ascii_file_num_lines !####################################################################### + !> @brief Function to determine the maximum line length and number of lines from an ascii file function get_ascii_file_num_lines_and_length(FILENAME, PELIST) - character(len=*), intent(in) :: FILENAME - integer, intent(in), optional, dimension(:) :: PELIST + character(len=*), intent(in) :: FILENAME !< name of the file to be read + integer, intent(in), optional, dimension(:) :: PELIST !< optional pelist - integer, dimension(2) :: get_ascii_file_num_lines_and_length + integer, dimension(2) :: get_ascii_file_num_lines_and_length !< number of lines (1) and + !! max line length (2) integer :: num_lines, max_length integer, parameter :: LENGTH=1024 character(len=LENGTH) :: str_tmp From 406370cd7e5e3796056983f0e5441c1cf122178d Mon Sep 17 00:00:00 2001 From: Eric Stofferahn <7784797+GFDL-Eric@users.noreply.github.com> Date: Fri, 12 Mar 2021 16:00:19 -0500 Subject: [PATCH 5/8] Fixed several errors --- data_override/data_override.F90 | 3 ++ fms2_io/fms2_io.F90 | 1 + fms2_io/fms_io_utils.F90 | 54 ++++++++++++++++++++------------- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/data_override/data_override.F90 b/data_override/data_override.F90 index 26852ec124..3637f0469e 100644 --- a/data_override/data_override.F90 +++ b/data_override/data_override.F90 @@ -62,6 +62,8 @@ module data_override_mod NO_REGION, INSIDE_REGION, OUTSIDE_REGION, & get_external_fileobj use fms_mod, only: write_version_number, field_exist, lowercase, check_nml_error +use axis_utils_mod, only: get_axis_bounds +use axis_utils2_mod, only : nearest_index, axis_edges use fms_io_mod, only: fms_io_init, get_mosaic_tile_file_classic=>get_mosaic_tile_file use mpp_domains_mod, only : domain2d, mpp_get_compute_domain, NULL_DOMAIN2D,operator(.NE.),operator(.EQ.) use mpp_domains_mod, only : mpp_get_global_domain, mpp_get_data_domain @@ -880,6 +882,7 @@ subroutine data_override_3d(gridname,fieldname_code,data,time,override,data_inde call get_mosaic_tile_file_classic(filename,filename2,.false.,domain) else call get_mosaic_tile_file_fms2_io(filename,filename2,.false.,domain) + endif filename = filename2 endif diff --git a/fms2_io/fms2_io.F90 b/fms2_io/fms2_io.F90 index 5c402a38a9..663f9a282e 100644 --- a/fms2_io/fms2_io.F90 +++ b/fms2_io/fms2_io.F90 @@ -86,6 +86,7 @@ module fms2_io_mod public :: fms2_io_init public :: get_mosaic_tile_grid public :: ascii_read +public :: get_mosaic_tile_file interface open_file module procedure netcdf_file_open_wrap diff --git a/fms2_io/fms_io_utils.F90 b/fms2_io/fms_io_utils.F90 index 0f9c59f2cb..d96187ee2f 100644 --- a/fms2_io/fms_io_utils.F90 +++ b/fms2_io/fms_io_utils.F90 @@ -27,6 +27,9 @@ module fms_io_utils_mod use omp_lib #endif use mpp_mod +use mpp_domains_mod, only: domain2D, domainUG, mpp_get_ntile_count, & + mpp_get_current_ntile, mpp_get_tile_id, & + mpp_get_UG_domain_ntiles, mpp_get_UG_domain_tile_id use platform_mod implicit none private @@ -493,7 +496,7 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) integer :: nmask, layout(2) integer, allocatable :: mask_list(:,:) character(len=:), dimension(:), allocatable :: mask_table_contents - integer :: mystat, n, stdoutunit + integer :: mystat, n, stdoutunit, offset character(len=128) :: record maskmap = .true. @@ -501,11 +504,11 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) stdoutunit = stdout() call ascii_read(mask_table, mask_table_contents) if( mpp_pe() == mpp_root_pe() ) then - read(mask_table_contents, FMT=*, IOSTAT=mystat) nmask + read(mask_table_contents(1), FMT=*, IOSTAT=mystat) nmask write(stdoutunit,*)"parse_mask_table: Number of domain regions masked in ", trim(modelname), " = ", nmask if( nmask > 0 ) then !--- read layout from mask_table and confirm it matches the shape of maskmap - read(mask_table_contents, FMT=*, IOSTAT=mystat) layout + read(mask_table_contents(2), FMT=*, IOSTAT=mystat) layout if( (layout(1) .NE. size(maskmap,1)) .OR. (layout(2) .NE. size(maskmap,2)) )then write(stdoutunit,*)"layout=", layout, ", size(maskmap) = ", size(maskmap,1), size(maskmap,2) call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): layout in file "//trim(mask_table)// & @@ -525,10 +528,16 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) if( mpp_pe() == mpp_root_pe() ) then n = 0 + offset = 2 do while( .true. ) - read(mask_table_contents,'(a)',end=999) record - if (record(1:1) == '#') cycle - if (record(1:10) == ' ') cycle + read(mask_table_contents(n+offset),'(a)',end=999) record + if (record(1:1) == '#') then + offset = offset + 1 + cycle + elseif (record(1:10) == ' ') then + offset = offset + 1 + cycle + endif n = n + 1 if( n > nmask ) then call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): number of mask_list entry "// & @@ -546,9 +555,6 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) call mpp_broadcast(mask_list, 2*nmask, mpp_root_pe()) do n = 1, nmask - if(debug_mask_list) then - write(stdoutunit,*) "==>NOTE from parse_mask_table_2d: ", trim(modelname), " mask_list = ", mask_list(n,1), mask_list(n,2) - endif maskmap(mask_list(n,1),mask_list(n,2)) = .false. enddo @@ -567,7 +573,7 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) integer :: nmask, layout(2) integer, allocatable :: mask_list(:,:) character(len=:), dimension(:), allocatable :: mask_table_contents - integer :: mystat, n, stdoutunit, ntiles + integer :: mystat, n, stdoutunit, ntiles, offset character(len=128) :: record maskmap = .true. @@ -575,11 +581,11 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) stdoutunit = stdout() call ascii_read(mask_table, mask_table_contents) if( mpp_pe() == mpp_root_pe() ) then - read(mask_table_contents, FMT=*, IOSTAT=mystat) nmask + read(mask_table_contents(1), FMT=*, IOSTAT=mystat) nmask write(stdoutunit,*)"parse_mask_table: Number of domain regions masked in ", trim(modelname), " = ", nmask if( nmask > 0 ) then !--- read layout from mask_table and confirm it matches the shape of maskmap - read(mask_table_contents, FMT=*, IOSTAT=mystat) layout(1), layout(2), ntiles + read(mask_table_contents(2), FMT=*, IOSTAT=mystat) layout(1), layout(2), ntiles if( (layout(1) .NE. size(maskmap,1)) .OR. (layout(2) .NE. size(maskmap,2)) )then write(stdoutunit,*)"layout=", layout, ", size(maskmap) = ", size(maskmap,1), size(maskmap,2) call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): layout in file "//trim(mask_table)// & @@ -607,10 +613,16 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) if( mpp_pe() == mpp_root_pe() ) then n = 0 + offset = 2 do while( .true. ) - read(mask_table_contents,'(a)',end=999) record - if (record(1:1) == '#') cycle - if (record(1:10) == ' ') cycle + read(mask_table_contents(n+offset),'(a)',end=999) record + if (record(1:1) == '#') then + offset = offset + 1 + cycle + elseif (record(1:10) == ' ') then + offset = offset + 1 + cycle + endif n = n + 1 if( n > nmask ) then call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): number of mask_list entry "// & @@ -629,10 +641,6 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) call mpp_broadcast(mask_list, 3*nmask, mpp_root_pe()) do n = 1, nmask - if(debug_mask_list) then - write(stdoutunit,*) "==>NOTE from parse_mask_table_3d: ", trim(modelname), " mask_list = ", & - mask_list(n,1), mask_list(n,2), mask_list(n,3) - endif maskmap(mask_list(n,1),mask_list(n,2),mask_list(n,3)) = .false. enddo @@ -650,6 +658,7 @@ subroutine get_mosaic_tile_file_sg(file_in, file_out, is_no_domain, domain, tile integer, intent(in), optional :: tile_count !< tile count character(len=256) :: basefile, tilename + character(len=1) :: my_tile_str integer :: lens, ntiles, ntileMe, tile, my_tile_id integer, dimension(:), allocatable :: tile_id type(domain2d), pointer, save :: d_ptr =>NULL() @@ -684,7 +693,8 @@ subroutine get_mosaic_tile_file_sg(file_in, file_out, is_no_domain, domain, tile endif if(ntiles > 1 .or. my_tile_id > 1 )then - tilename = 'tile'//string(my_tile_id) + write(my_tile_str, '(I1)') my_tile_id + tilename = 'tile'//my_tile_str if(index(basefile,'.'//trim(tilename),back=.true.) == 0)then basefile = trim(basefile)//'.'//trim(tilename); end if @@ -705,6 +715,7 @@ subroutine get_mosaic_tile_file_ug(file_in, file_out, domain) type(domainUG), intent(in), optional :: domain !< domain provided character(len=256) :: basefile, tilename + character(len=1) :: my_tile_str integer :: lens, ntiles, my_tile_id if(index(file_in, '.nc', back=.true.)==0) then @@ -725,7 +736,8 @@ subroutine get_mosaic_tile_file_ug(file_in, file_out, domain) endif if(ntiles > 1 .or. my_tile_id > 1 )then - tilename = 'tile'//string(my_tile_id) + write(my_tile_str, '(I1)') my_tile_id + tilename = 'tile'//my_tile_str if(index(basefile,'.'//trim(tilename),back=.true.) == 0)then basefile = trim(basefile)//'.'//trim(tilename); end if From c7f47fef24f994c7fb026f5cb10893178cc3d884 Mon Sep 17 00:00:00 2001 From: Eric Stofferahn <7784797+GFDL-Eric@users.noreply.github.com> Date: Tue, 16 Mar 2021 16:27:35 -0400 Subject: [PATCH 6/8] Edited parse_mask_table to work, edited test_read_ascii to actually test things, made parse_mask_table public in fms2_io, edited test_io_with_mask to work with new parse_mask_table call --- fms2_io/fms2_io.F90 | 1 + fms2_io/fms_io_utils.F90 | 20 ++++++++------------ test_fms/fms2_io/Makefile.am | 4 ++-- test_fms/fms2_io/ascii_test1 | 3 --- test_fms/fms2_io/test_io_with_mask.F90 | 4 ++-- test_fms/fms2_io/test_io_with_mask.sh | 2 +- test_fms/fms2_io/test_read_ascii_file.F90 | 8 +++++++- test_fms/fms2_io/test_read_ascii_file.sh | 2 +- 8 files changed, 22 insertions(+), 22 deletions(-) delete mode 100644 test_fms/fms2_io/ascii_test1 diff --git a/fms2_io/fms2_io.F90 b/fms2_io/fms2_io.F90 index 663f9a282e..e657429faa 100644 --- a/fms2_io/fms2_io.F90 +++ b/fms2_io/fms2_io.F90 @@ -87,6 +87,7 @@ module fms2_io_mod public :: get_mosaic_tile_grid public :: ascii_read public :: get_mosaic_tile_file +public :: parse_mask_table interface open_file module procedure netcdf_file_open_wrap diff --git a/fms2_io/fms_io_utils.F90 b/fms2_io/fms_io_utils.F90 index d96187ee2f..cfbdcc05f9 100644 --- a/fms2_io/fms_io_utils.F90 +++ b/fms2_io/fms_io_utils.F90 @@ -528,9 +528,9 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) if( mpp_pe() == mpp_root_pe() ) then n = 0 - offset = 2 - do while( .true. ) - read(mask_table_contents(n+offset),'(a)',end=999) record + offset = 3 + do while (offset + n < size(mask_table_contents)+1) + read(mask_table_contents(n+offset),'(a)') record if (record(1:1) == '#') then offset = offset + 1 cycle @@ -543,11 +543,9 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): number of mask_list entry "// & "is greater than nmask in file "//trim(mask_table) ) endif - read(record,*,err=888) mask_list(n,1), mask_list(n,2) + read(record,*) mask_list(n,1), mask_list(n,2) enddo -888 call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error in reading mask_list from file "//trim(mask_table)) -999 continue !--- make sure the number of entry for mask_list is nmask if( n .NE. nmask) call mpp_error(FATAL, & "fms2_io(parse_mask_table_2d): number of mask_list entry does not match nmask in file "//trim(mask_table)) @@ -613,9 +611,9 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) if( mpp_pe() == mpp_root_pe() ) then n = 0 - offset = 2 - do while( .true. ) - read(mask_table_contents(n+offset),'(a)',end=999) record + offset = 3 + do while (offset + n < size(mask_table_contents)+1) + read(mask_table_contents(n+offset),'(a)') record if (record(1:1) == '#') then offset = offset + 1 cycle @@ -628,11 +626,9 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): number of mask_list entry "// & "is greater than nmask in file "//trim(mask_table) ) endif - read(record,*,err=888) mask_list(n,1), mask_list(n,2), mask_list(n,3) + read(record,*) mask_list(n,1), mask_list(n,2), mask_list(n,3) enddo -888 call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error in reading mask_list from file "//trim(mask_table)) -999 continue !--- make sure the number of entry for mask_list is nmask if( n .NE. nmask) call mpp_error(FATAL, & "fms2_io(parse_mask_table_3d): number of mask_list entry does not match nmask in file "//trim(mask_table)) diff --git a/test_fms/fms2_io/Makefile.am b/test_fms/fms2_io/Makefile.am index a63d7c2382..458b647832 100644 --- a/test_fms/fms2_io/Makefile.am +++ b/test_fms/fms2_io/Makefile.am @@ -44,7 +44,7 @@ test_io_simple_SOURCES = test_io_simple.F90 argparse.F90 setup.F90 test_global_att_SOURCES = test_global_att.F90 test_io_with_mask_SOURCES=test_io_with_mask.F90 test_get_mosaic_tile_grid_SOURCES=test_get_mosaic_tile_grid.F90 -test_read_ascii_file_SOURCES=test_read_ascii_file.F90 ascii_test1 +test_read_ascii_file_SOURCES=test_read_ascii_file.F90 EXTRA_DIST = test_fms2_io.sh test_atmosphere_io.sh test_io_simple.sh test_global_att.sh test_io_with_mask.sh test_read_ascii_file.sh @@ -69,4 +69,4 @@ endif TESTS_ENVIRONMENT = srcdir="$(top_srcdir)"; \ netcdf_version_skip=${skipflag} -CLEANFILES = *.mod *.nc *.nc.* input.nml logfile.000000.out the_mask +CLEANFILES = *.mod *.nc *.nc.* input.nml logfile.000000.out the_mask ascii_test1 diff --git a/test_fms/fms2_io/ascii_test1 b/test_fms/fms2_io/ascii_test1 deleted file mode 100644 index 43aac0633e..0000000000 --- a/test_fms/fms2_io/ascii_test1 +++ /dev/null @@ -1,3 +0,0 @@ -3, 4, 5, "fort", "global" -"dovalia" "flatugula" -99 431 "boolakah" diff --git a/test_fms/fms2_io/test_io_with_mask.F90 b/test_fms/fms2_io/test_io_with_mask.F90 index 9c38c77954..d5789fafc1 100644 --- a/test_fms/fms2_io/test_io_with_mask.F90 +++ b/test_fms/fms2_io/test_io_with_mask.F90 @@ -29,9 +29,9 @@ program test_io_with_mask mpp_get_compute_domain,domain2d use mpp_mod, only: mpp_pe, mpp_root_pe, mpp_error, FATAL use fms2_io_mod, only: open_file, register_axis, register_variable_attribute, close_file, & - FmsNetcdfDomainFile_t, write_data, register_field, read_data + FmsNetcdfDomainFile_t, write_data, register_field, read_data, & + parse_mask_table use fms_mod, only: fms_init, fms_end -use fms_io_mod, only: parse_mask_table use netcdf, only: nf90_open, nf90_get_var, nf90_nowrite, NF90_NOERR, nf90_get_var, & nf90_close use mpi, only: mpi_barrier, mpi_comm_world diff --git a/test_fms/fms2_io/test_io_with_mask.sh b/test_fms/fms2_io/test_io_with_mask.sh index 3929827106..1db317e260 100755 --- a/test_fms/fms2_io/test_io_with_mask.sh +++ b/test_fms/fms2_io/test_io_with_mask.sh @@ -40,7 +40,7 @@ touch input.nml # . ----- . ----- . # | (3,1) | (3,2) | # . ----- . ----- . -printf "\n1\n2,3\n1,1" | cat > the_mask +printf "1\n2,3\n1,1" | cat > the_mask # For example, if you have a grid that is 60 by 60 and a layout of 2,3 # You are going to need 6 ranks: diff --git a/test_fms/fms2_io/test_read_ascii_file.F90 b/test_fms/fms2_io/test_read_ascii_file.F90 index d764d54982..2acd76898f 100644 --- a/test_fms/fms2_io/test_read_ascii_file.F90 +++ b/test_fms/fms2_io/test_read_ascii_file.F90 @@ -33,7 +33,7 @@ program test_read_ascii_file character(len=256) :: filename2 !< Name of alternative ascii file to be read character(len=256) :: line !< Content of a line of the read ascii file integer :: num_lines !< Number of lines in the ascii file - integer :: stat !< IOSTATUS from the read method + integer, dimension(2) :: stat !< IOSTATUS from the read method integer, allocatable :: cur_pelist(:) !< PELIST is read into this variable integer :: ierr !< used by MPI_FINALIZE @@ -48,6 +48,12 @@ program test_read_ascii_file call fms2_io_init() filename = "ascii_test1" call ascii_read(filename, test_array) + read(test_array(1), *) stat + print *, stat(1)*6, stat(2)+3 + read(test_array(2), *) num_lines + print *, num_lines-11 + read(test_array(3), *) line + print *, trim(line)//"wut" ! if (test_numb == 1 .or. test_numb == 7 .or. test_numb == 8) then ! if (test_numb == 1) then ! filename = "input.nml" diff --git a/test_fms/fms2_io/test_read_ascii_file.sh b/test_fms/fms2_io/test_read_ascii_file.sh index 51b00781b4..52f450583c 100755 --- a/test_fms/fms2_io/test_read_ascii_file.sh +++ b/test_fms/fms2_io/test_read_ascii_file.sh @@ -26,6 +26,6 @@ . ../test_common.sh # make an input.nml for mpp_init to read printf "EOF\n&dummy\nEOF" | cat > input.nml -cp $top_srcdir/test_fms/fms2_io/ascii_test1 ascii_test1 +printf "5, 14 \n23\n\"forlendula\"" | cat > ascii_test1 # run the tests run_test test_read_ascii_file 6 $netcdf_version_skip From 50c2dd91596e863237fb5ec5846f47b4761cb653 Mon Sep 17 00:00:00 2001 From: Eric Stofferahn <7784797+GFDL-Eric@users.noreply.github.com> Date: Wed, 17 Mar 2021 12:22:09 -0400 Subject: [PATCH 7/8] Added error checks in to parse_mask_table and reworked the test_read_ascii routine to properly fail if answers aren't read in correctly. --- fms2_io/fms_io_utils.F90 | 60 +++++++++++++++--- test_fms/fms2_io/test_read_ascii_file.F90 | 76 +---------------------- 2 files changed, 53 insertions(+), 83 deletions(-) diff --git a/fms2_io/fms_io_utils.F90 b/fms2_io/fms_io_utils.F90 index cfbdcc05f9..25c5a1bcf0 100644 --- a/fms2_io/fms_io_utils.F90 +++ b/fms2_io/fms_io_utils.F90 @@ -496,7 +496,7 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) integer :: nmask, layout(2) integer, allocatable :: mask_list(:,:) character(len=:), dimension(:), allocatable :: mask_table_contents - integer :: mystat, n, stdoutunit, offset + integer :: iocheck, n, stdoutunit, offset character(len=128) :: record maskmap = .true. @@ -504,11 +504,21 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) stdoutunit = stdout() call ascii_read(mask_table, mask_table_contents) if( mpp_pe() == mpp_root_pe() ) then - read(mask_table_contents(1), FMT=*, IOSTAT=mystat) nmask + read(mask_table_contents(1), FMT=*, IOSTAT=iocheck) nmask + if (iocheck > 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error in reading nmask from file variable") + elseif (iocheck < 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error: nmask not completely read from file variable") + endif write(stdoutunit,*)"parse_mask_table: Number of domain regions masked in ", trim(modelname), " = ", nmask if( nmask > 0 ) then !--- read layout from mask_table and confirm it matches the shape of maskmap - read(mask_table_contents(2), FMT=*, IOSTAT=mystat) layout + read(mask_table_contents(2), FMT=*, IOSTAT=iocheck) layout + if (iocheck > 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error in reading layout from file variable") + elseif (iocheck < 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error: layout not completely read from file variable") + endif if( (layout(1) .NE. size(maskmap,1)) .OR. (layout(2) .NE. size(maskmap,2)) )then write(stdoutunit,*)"layout=", layout, ", size(maskmap) = ", size(maskmap,1), size(maskmap,2) call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): layout in file "//trim(mask_table)// & @@ -530,7 +540,12 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) n = 0 offset = 3 do while (offset + n < size(mask_table_contents)+1) - read(mask_table_contents(n+offset),'(a)') record + read(mask_table_contents(n+offset),'(a)',iostat=iocheck) record + if (iocheck > 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error in reading record from file variable") + elseif (iocheck < 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error: record not completely read from file variable") + endif if (record(1:1) == '#') then offset = offset + 1 cycle @@ -543,7 +558,12 @@ subroutine parse_mask_table_2d(mask_table, maskmap, modelname) call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): number of mask_list entry "// & "is greater than nmask in file "//trim(mask_table) ) endif - read(record,*) mask_list(n,1), mask_list(n,2) + read(record,*,iostat=iocheck) mask_list(n,1), mask_list(n,2) + if (iocheck > 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error in reading mask_list from record variable") + elseif (iocheck < 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_2d): Error: mask_list not completely read from record variable") + endif enddo !--- make sure the number of entry for mask_list is nmask @@ -571,7 +591,7 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) integer :: nmask, layout(2) integer, allocatable :: mask_list(:,:) character(len=:), dimension(:), allocatable :: mask_table_contents - integer :: mystat, n, stdoutunit, ntiles, offset + integer :: iocheck, n, stdoutunit, ntiles, offset character(len=128) :: record maskmap = .true. @@ -579,11 +599,21 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) stdoutunit = stdout() call ascii_read(mask_table, mask_table_contents) if( mpp_pe() == mpp_root_pe() ) then - read(mask_table_contents(1), FMT=*, IOSTAT=mystat) nmask + read(mask_table_contents(1), FMT=*, IOSTAT=iocheck) nmask + if (iocheck > 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error in reading nmask from file variable") + elseif (iocheck < 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error: nmask not completely read from file variable") + endif write(stdoutunit,*)"parse_mask_table: Number of domain regions masked in ", trim(modelname), " = ", nmask if( nmask > 0 ) then !--- read layout from mask_table and confirm it matches the shape of maskmap - read(mask_table_contents(2), FMT=*, IOSTAT=mystat) layout(1), layout(2), ntiles + read(mask_table_contents(2), FMT=*, IOSTAT=iocheck) layout(1), layout(2), ntiles + if (iocheck > 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error in reading layout from file variable") + elseif (iocheck < 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error: layout not completely read from file variable") + endif if( (layout(1) .NE. size(maskmap,1)) .OR. (layout(2) .NE. size(maskmap,2)) )then write(stdoutunit,*)"layout=", layout, ", size(maskmap) = ", size(maskmap,1), size(maskmap,2) call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): layout in file "//trim(mask_table)// & @@ -613,7 +643,12 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) n = 0 offset = 3 do while (offset + n < size(mask_table_contents)+1) - read(mask_table_contents(n+offset),'(a)') record + read(mask_table_contents(n+offset),'(a)',iostat=iocheck) record + if (iocheck > 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error in reading record from file variable") + elseif (iocheck < 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error: record not completely read from file variable") + endif if (record(1:1) == '#') then offset = offset + 1 cycle @@ -626,7 +661,12 @@ subroutine parse_mask_table_3d(mask_table, maskmap, modelname) call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): number of mask_list entry "// & "is greater than nmask in file "//trim(mask_table) ) endif - read(record,*) mask_list(n,1), mask_list(n,2), mask_list(n,3) + read(record,*,iostat=iocheck) mask_list(n,1), mask_list(n,2), mask_list(n,3) + if (iocheck > 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error in reading mask_list from record variable") + elseif (iocheck < 0) then + call mpp_error(FATAL, "fms2_io(parse_mask_table_3d): Error: mask_list not completely read from record variable") + endif enddo !--- make sure the number of entry for mask_list is nmask diff --git a/test_fms/fms2_io/test_read_ascii_file.F90 b/test_fms/fms2_io/test_read_ascii_file.F90 index 2acd76898f..0b38fc8420 100644 --- a/test_fms/fms2_io/test_read_ascii_file.F90 +++ b/test_fms/fms2_io/test_read_ascii_file.F90 @@ -37,85 +37,15 @@ program test_read_ascii_file integer, allocatable :: cur_pelist(:) !< PELIST is read into this variable integer :: ierr !< used by MPI_FINALIZE -! namelist /test_read_ascii_file_nml/ test_numb - -! open(20, file="test_numb_ascii.nml", form="formatted", status="old") -! read(20, nml=test_read_ascii_file_nml) -! close(20) - - ! Tests not meant to raise errors call mpp_init() call fms2_io_init() filename = "ascii_test1" call ascii_read(filename, test_array) read(test_array(1), *) stat - print *, stat(1)*6, stat(2)+3 + if (stat(1)*6 - (stat(2)+3) /= 13) call mpp_error(FATAL, "test_read_ascii: failed to read integers") read(test_array(2), *) num_lines - print *, num_lines-11 + if (num_lines-11 /= 12) call mpp_error(FATAL, "test_read_ascii: failed to read integer") read(test_array(3), *) line - print *, trim(line)//"wut" -! if (test_numb == 1 .or. test_numb == 7 .or. test_numb == 8) then -! if (test_numb == 1) then -! filename = "input.nml" -! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) -! allocate(test_array(num_lines)) -! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) -! else if (test_numb == 7) then -! filename = "input.nml" -! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) -! allocate(test_array(num_lines)) -! allocate(cur_pelist(0:mpp_npes()-1)) -! call mpp_get_current_pelist(cur_pelist) -! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array, PELIST=cur_pelist) -! else if (test_numb == 8) then -! filename = "empty.nml" -! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) -! allocate(test_array(num_lines)) -! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) -! end if -! ! Content check -! open(2, file=filename, iostat=stat) -! do i=1, num_lines-1 -! read(2, '(A)', iostat=stat) line -! if (stat.eq.-1) then -! call mpp_error(FATAL, "Problem reading the ascii file") -! end if -! if (test_array(i).ne.line) then -! call mpp_error(FATAL, "Content array variable does not& -! & match the ascii file content") -! end if -! end do -! ! Tests meant to raise errors -! else -! if (test_numb == 2) then -! filename = "input.nml" -! allocate(test_array(20)) -! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) -! else if (test_numb == 3) then -! filename = "doesnotexist.txt" -! ! Need to pass in an exist file name below to avoid raising error on -! ! get_ascii_file_num_lines call in order to get to the error in read_ascii_file -! filename2 = "input.nml" -! num_lines = get_ascii_file_num_lines(filename2, INPUT_STR_LENGTH) -! allocate(test_array(num_lines)) -! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) -! else if (test_numb == 4) then -! filename = "input.nml" -! filename2 = "empty.nml" -! num_lines = get_ascii_file_num_lines(filename2, INPUT_STR_LENGTH) -! allocate(test_array(num_lines)) -! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) -! else if (test_numb == 5) then -! filename = "input.nml" -! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) -! allocate(test_array(num_lines)) -! call read_ascii_file(filename, 0, test_array) -! else if (test_numb == 6) then -! filename = "input.nml" -! num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) -! allocate(test_array(num_lines-1)) -! call read_ascii_file(filename, INPUT_STR_LENGTH, test_array) -! end if -! end if + if (trim(line)//"wut" /= "forlendulawut") call mpp_error(FATAL, "test_read_ascii: failed to read string") call MPI_FINALIZE(ierr) end program test_read_ascii_file From 72f06af3195d8fe56c1034bf242b95e6a8b31141 Mon Sep 17 00:00:00 2001 From: Eric Stofferahn <7784797+GFDL-Eric@users.noreply.github.com> Date: Mon, 22 Mar 2021 11:02:32 -0400 Subject: [PATCH 8/8] updated input_nml_file to use get_ascii_file_num_lines_and_length --- mpp/include/mpp_util.inc | 11 ++++++----- mpp/mpp.F90 | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mpp/include/mpp_util.inc b/mpp/include/mpp_util.inc index 51a391bf1a..893f4b97ee 100644 --- a/mpp/include/mpp_util.inc +++ b/mpp/include/mpp_util.inc @@ -1301,7 +1301,8 @@ end function rarray_to_char character(len=*), intent(in), optional :: pelist_name_in ! private variables integer :: log_unit - integer :: num_lines, i + integer :: i + integer, dimension(2) :: lines_and_length logical :: file_exist character(len=len(peset(current_peset_num)%name)) :: pelist_name character(len=128) :: filename @@ -1328,9 +1329,9 @@ end function rarray_to_char if (.not. file_exist ) then filename='input.nml' endif - num_lines = get_ascii_file_num_lines(filename, INPUT_STR_LENGTH) - allocate(input_nml_file(num_lines)) - call read_ascii_file(filename, INPUT_STR_LENGTH, input_nml_file) + lines_and_length = get_ascii_file_num_lines_and_length(filename) + allocate(character(len=lines_and_length(2))::input_nml_file(lines_and_length(1))) + call read_ascii_file(filename, lines_and_length(2), input_nml_file) ! write info logfile if (pe == root_pe) then @@ -1338,7 +1339,7 @@ end function rarray_to_char write(log_unit,'(a)') '========================================================================' write(log_unit,'(a)') 'READ_INPUT_NML: '//trim(version) write(log_unit,'(a)') 'READ_INPUT_NML: '//trim(filename)//' ' - do i = 1, num_lines + do i = 1, lines_and_length(1) write(log_unit,*) trim(input_nml_file(i)) enddo end if diff --git a/mpp/mpp.F90 b/mpp/mpp.F90 index 25bd9b8c03..aa8d1bcf10 100644 --- a/mpp/mpp.F90 +++ b/mpp/mpp.F90 @@ -1280,7 +1280,7 @@ module mpp_mod ! parameter defining length of character variables integer, parameter :: INPUT_STR_LENGTH = 256 ! public variable needed for reading input.nml from an internal file - character(len=INPUT_STR_LENGTH), dimension(:), allocatable, target, public :: input_nml_file + character(len=:), dimension(:), allocatable, target, public :: input_nml_file logical :: read_ascii_file_on = .FALSE. !***********************************************************************