From f4dfb9648096872aeb37d77bd603b48bd3066cc2 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Sun, 13 Mar 2022 10:24:40 -0400 Subject: [PATCH 1/4] +Better multi-line parameter specification handling Added the ability to detect and handle multiline parameter specifications that are longer than INPUT_STR_LENGTH after the lines have been concatenated. Many instances where character strings are set to length INPUT_STR_LENGTH are replaced by param_file_type%max_line_len. This commit partially addresses MOM6 issue #75. All answers and output are bitwise identical in cases that worked previously, but there is a new element in the publicly visible param_file_type, and some cases where there had previously been errors due to very long input specification may now work. --- src/framework/MOM_file_parser.F90 | 107 +++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 18 deletions(-) diff --git a/src/framework/MOM_file_parser.F90 b/src/framework/MOM_file_parser.F90 index 3ad551496f..ed86e77bea 100644 --- a/src/framework/MOM_file_parser.F90 +++ b/src/framework/MOM_file_parser.F90 @@ -69,6 +69,9 @@ module MOM_file_parser logical :: log_to_stdout = log_to_stdout_default !< If true, all log !! messages are also sent to stdout. logical :: log_open = .false. !< True if the log file has been opened. + integer :: max_line_len = 4 !< The maximum number of characters in the lines + !! in any of the files in this param_file_type after + !! any continued lines have been combined. integer :: stdout !< The unit number from stdout(). integer :: stdlog !< The unit number from stdlog(). character(len=240) :: doc_file !< A file where all run-time parameters, their @@ -129,6 +132,7 @@ subroutine open_param_file(filename, CS, checkable, component, doc_file_dir) logical :: file_exists, unit_in_use, Netcdf_file, may_check, reopened_file integer :: ios, iounit, strlen, i character(len=240) :: doc_path + character(len=16) :: sub_string type(parameter_block), pointer :: block => NULL() may_check = .true. ; if (present(checkable)) may_check = checkable @@ -196,6 +200,11 @@ subroutine open_param_file(filename, CS, checkable, component, doc_file_dir) " has been opened successfully.", 5) call populate_param_data(iounit, filename, CS%param_data(i)) + ! Increment the maximum line length, but always report values in blocks of 4 characters. + CS%max_line_len = max(CS%max_line_len, 4 + 4*(max_input_line_length(CS, i) - 1) / 4) + write (sub_string, '(i4.4)') CS%max_line_len + call MOM_mesg("open_param_file: Maximum input line length determined to be "//& + trim(adjustl(sub_string))//" characters.", 5) call read_param(CS,"SEND_LOG_TO_STDOUT",CS%log_to_stdout) call read_param(CS,"REPORT_UNUSED_PARAMS",CS%report_unused) @@ -574,7 +583,7 @@ subroutine read_param_int(CS, varname, value, fail_if_missing) logical, optional, intent(in) :: fail_if_missing !< If present and true, a fatal error occurs !! if this variable is not found in the parameter file ! Local variables - character(len=INPUT_STR_LENGTH) :: value_string(1) + character(len=CS%max_line_len) :: value_string(1) logical :: found, defined call get_variable_line(CS, varname, found, defined, value_string) @@ -606,7 +615,7 @@ subroutine read_param_int_array(CS, varname, value, fail_if_missing) logical, optional, intent(in) :: fail_if_missing !< If present and true, a fatal error occurs !! if this variable is not found in the parameter file ! Local variables - character(len=INPUT_STR_LENGTH) :: value_string(1) + character(len=CS%max_line_len) :: value_string(1) logical :: found, defined call get_variable_line(CS, varname, found, defined, value_string) @@ -642,7 +651,7 @@ subroutine read_param_real(CS, varname, value, fail_if_missing, scale) !! by before it is returned. ! Local variables - character(len=INPUT_STR_LENGTH) :: value_string(1) + character(len=CS%max_line_len) :: value_string(1) logical :: found, defined call get_variable_line(CS, varname, found, defined, value_string) @@ -678,8 +687,8 @@ subroutine read_param_real_array(CS, varname, value, fail_if_missing, scale) !! by before it is returned. ! Local variables - character(len=INPUT_STR_LENGTH) :: value_string(1) - logical :: found, defined + character(len=CS%max_line_len) :: value_string(1) + logical :: found, defined call get_variable_line(CS, varname, found, defined, value_string) if (found .and. defined .and. (LEN_TRIM(value_string(1)) > 0)) then @@ -713,7 +722,7 @@ subroutine read_param_char(CS, varname, value, fail_if_missing) logical, optional, intent(in) :: fail_if_missing !< If present and true, a fatal error occurs !! if this variable is not found in the parameter file ! Local variables - character(len=INPUT_STR_LENGTH) :: value_string(1) + character(len=CS%max_line_len) :: value_string(1) logical :: found, defined call get_variable_line(CS, varname, found, defined, value_string) @@ -737,7 +746,7 @@ subroutine read_param_char_array(CS, varname, value, fail_if_missing) !! if this variable is not found in the parameter file ! Local variables - character(len=INPUT_STR_LENGTH) :: value_string(1), loc_string + character(len=CS%max_line_len) :: value_string(1), loc_string logical :: found, defined integer :: i, i_out @@ -775,7 +784,7 @@ subroutine read_param_logical(CS, varname, value, fail_if_missing) !! if this variable is not found in the parameter file ! Local variables - character(len=INPUT_STR_LENGTH) :: value_string(1) + character(len=CS%max_line_len) :: value_string(1) logical :: found, defined call get_variable_line(CS, varname, found, defined, value_string, paramIsLogical=.true.) @@ -802,7 +811,7 @@ subroutine read_param_time(CS, varname, value, timeunit, fail_if_missing, date_f !! later be logged in the same format. ! Local variables - character(len=INPUT_STR_LENGTH) :: value_string(1) + character(len=CS%max_line_len) :: value_string(1) character(len=240) :: err_msg logical :: found, defined real :: real_time, time_unit @@ -861,7 +870,7 @@ end subroutine read_param_time !> This function removes single and double quotes from a character string function strip_quotes(val_str) character(len=*) :: val_str !< The character string to work on - character(len=INPUT_STR_LENGTH) :: strip_quotes + character(len=len(val_str)) :: strip_quotes ! Local variables integer :: i strip_quotes = val_str @@ -879,7 +888,69 @@ function strip_quotes(val_str) enddo end function strip_quotes -!> This subtoutine extracts the contents of lines in the param_file_type that refer to +!> This function returns the maximum number of characters in any input lines after they +!! have been combined by any line continuation. +function max_input_line_length(CS, pf_num) result(max_len) + type(param_file_type), intent(in) :: CS !< The control structure for the file_parser module, + !! it is also a structure to parse for run-time parameters + integer, optional, intent(in) :: pf_num !< If present, only work on a single file in the + !! param_file_type, or return 0 if this exceeds the + !! number of files in the param_file_type. + integer :: max_len !< The maximum number of characters in any input lines after they + !! have been combined by any line continuation. + + ! Local variables + character(len=FILENAME_LENGTH) :: filename + ! character(len=:), allocatable :: line + character :: last_char + integer :: ipf, ipf_s, ipf_e + integer :: last, last1, line_len, count, contBufSize + logical :: continuedLine + + max_len = 0 + ipf_s = 1 ; ipf_e = CS%nfiles + if (present(pf_num)) then + if (pf_num > CS%nfiles) return + ipf_s = pf_num ; ipf_e = pf_num + endif + + paramfile_loop: do ipf = ipf_s, ipf_e + filename = CS%filename(ipf) + contBufSize = 0 + continuedLine = .false. + + ! Scan through each line of the file + do count = 1, CS%param_data(ipf)%num_lines + ! line = CS%param_data(ipf)%line(count) + last = len_trim(CS%param_data(ipf)%line(count)) + last_char = " " + if (last > 0) last_char = CS%param_data(ipf)%line(count)(last:last) + ! Check if line ends in continuation character (either & or \) + ! Note achar(92) is a backslash + if (last_char == achar(92) .or. last_char == "&") then + contBufSize = contBufSize + last - 1 + continuedLine = .true. + if (count==CS%param_data(ipf)%num_lines .and. is_root_pe()) & + call MOM_error(FATAL, "MOM_file_parser : the last line of the file ends in a"// & + " continuation character but there are no more lines to read. "// & + " Line: '"//trim(CS%param_data(ipf)%line(count)(:last))//"'"// & + " in file "//trim(filename)//".") + cycle ! cycle inorder to append the next line of the file + elseif (continuedLine) then + ! If we reached this point then this is the end of line continuation + line_len = contBufSize + last + contBufSize = 0 + continuedLine = .false. + else ! This is a simple line with no continuation. + line_len = last + endif + max_len = max(max_len, line_len) + enddo ! CS%param_data(ipf)%num_lines + enddo paramfile_loop + +end function max_input_line_length + +!> This subroutine extracts the contents of lines in the param_file_type that refer to !! a named parameter. The value_string that is returned must be interepreted in a way !! that depends on the type of this variable. subroutine get_variable_line(CS, varname, found, defined, value_string, paramIsLogical) @@ -893,9 +964,9 @@ subroutine get_variable_line(CS, varname, found, defined, value_string, paramIsL !! that can be simply defined without parsing a value_string. ! Local variables - character(len=INPUT_STR_LENGTH) :: val_str, lname, origLine - character(len=INPUT_STR_LENGTH) :: line, continuationBuffer, blockName - character(len=FILENAME_LENGTH) :: filename + character(len=CS%max_line_len) :: val_str, lname, origLine + character(len=CS%max_line_len) :: line, continuationBuffer, blockName + character(len=FILENAME_LENGTH) :: filename integer :: is, id, isd, isu, ise, iso, ipf integer :: last, last1, ival, oval, max_vals, count, contBufSize character(len=52) :: set @@ -907,7 +978,7 @@ subroutine get_variable_line(CS, varname, found, defined, value_string, paramIsL logical, parameter :: requireNamedClose = .false. integer, parameter :: verbose = 1 set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - continuationBuffer = repeat(" ",INPUT_STR_LENGTH) + continuationBuffer = repeat(" ", CS%max_line_len) contBufSize = 0 variableKindIsLogical=.false. @@ -949,7 +1020,7 @@ subroutine get_variable_line(CS, varname, found, defined, value_string, paramIsL ! If we reached this point then this is the end of line continuation continuationBuffer(contBufSize+1:contBufSize+len_trim(line))=line(:last) line = continuationBuffer - continuationBuffer=repeat(" ",INPUT_STR_LENGTH) ! Clear for next use + continuationBuffer=repeat(" ",CS%max_line_len) ! Clear for next use contBufSize = 0 continuedLine = .false. last = len_trim(line) @@ -1173,8 +1244,8 @@ end subroutine get_variable_line !> Record that a line has been used to set a parameter subroutine flag_line_as_read(line_used, count) - logical, dimension(:), pointer :: line_used !< A structure indicating which lines have been read - integer, intent(in) :: count !< The parameter on this line number has been read + logical, dimension(:), pointer :: line_used !< A structure indicating which lines have been read + integer, intent(in) :: count !< The parameter on this line number has been read line_used(count) = .true. end subroutine flag_line_as_read From eb677ebd805998d59b4cde361bb263af70bad332 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 14 Mar 2022 06:59:44 -0400 Subject: [PATCH 2/4] Support arbitrarily long parameter spec lines Make the lines of the param_file_type individually allocatable to support arbitrarily long input lines. This required the introduction of a new private type to hold the actual allocatable array, so the lines are now an array of this type. All answers are bitwise identical. --- src/framework/MOM_file_parser.F90 | 61 +++++++++++++++++++------------ 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/framework/MOM_file_parser.F90 b/src/framework/MOM_file_parser.F90 index ed86e77bea..a2fe6ec7a8 100644 --- a/src/framework/MOM_file_parser.F90 +++ b/src/framework/MOM_file_parser.F90 @@ -32,11 +32,17 @@ module MOM_file_parser logical, parameter :: minimal_doc_default = .true. !>@} + +!> A simple type to allow lines in an array to be allocated with variable sizes. +type, private :: file_line_type ; private + character(len=:), allocatable :: line !< An allocatable line with content +end type file_line_type + !> The valid lines extracted from an input parameter file without comments type, private :: file_data_type ; private integer :: num_lines = 0 !< The number of lines in this type - character(len=INPUT_STR_LENGTH), pointer, dimension(:) :: line => NULL() !< The line content - logical, pointer, dimension(:) :: line_used => NULL() !< If true, the line has been read + type(file_line_type), allocatable, dimension(:) :: fln !< Lines with the input content. + logical, pointer, dimension(:) :: line_used => NULL() !< If true, the line has been read end type file_data_type !> A link in the list of variables that have already had override warnings issued @@ -264,7 +270,8 @@ subroutine close_param_file(CS, quiet_close, component) CS%iounit(i) = -1 CS%filename(i) = '' CS%NetCDF_file(i) = .false. - deallocate (CS%param_data(i)%line) + do n=1,CS%param_data(i)%num_lines ; deallocate(CS%param_data(i)%fln(n)%line) ; enddo + deallocate (CS%param_data(i)%fln) deallocate (CS%param_data(i)%line_used) enddo CS%log_open = .false. @@ -322,7 +329,7 @@ subroutine close_param_file(CS, quiet_close, component) num_unused = num_unused + 1 if (CS%report_unused) & call MOM_error(WARNING, "Unused line in "//trim(CS%filename(i))// & - " : "//trim(CS%param_data(i)%line(n))) + " : "//trim(CS%param_data(i)%fln(n)%line)) endif enddo endif @@ -333,7 +340,8 @@ subroutine close_param_file(CS, quiet_close, component) CS%iounit(i) = -1 CS%filename(i) = '' CS%NetCDF_file(i) = .false. - deallocate (CS%param_data(i)%line) + do n=1,CS%param_data(i)%num_lines ; deallocate(CS%param_data(i)%fln(n)%line) ; enddo + deallocate (CS%param_data(i)%fln) deallocate (CS%param_data(i)%line_used) enddo deallocate(CS%blockName) @@ -356,13 +364,11 @@ subroutine populate_param_data(iounit, filename, param_data) ! Local variables character(len=INPUT_STR_LENGTH) :: line - integer :: num_lines + character(len=INPUT_STR_LENGTH), allocatable, dimension(:) :: lines_in + integer :: n, num_lines logical :: inMultiLineComment ! Find the number of keyword lines in a parameter file - ! Allocate the space to hold the lines in param_data%line - ! Populate param_data%line with the keyword lines from parameter file - if (all_PEs_read .or. is_root_pe()) then ! rewind the parameter file rewind(iounit) @@ -386,7 +392,6 @@ subroutine populate_param_data(iounit, filename, param_data) call MOM_error(FATAL, 'MOM_file_parser : A C-style multi-line comment '// & '(/* ... */) was not closed before the end of '//trim(filename)) - ! allocate space to hold contents of the parameter file param_data%num_lines = num_lines endif ! (is_root_pe()) @@ -397,17 +402,15 @@ subroutine populate_param_data(iounit, filename, param_data) ! Set up the space for storing the actual lines. num_lines = param_data%num_lines - allocate (param_data%line(num_lines)) - allocate (param_data%line_used(num_lines)) - param_data%line(:) = ' ' - param_data%line_used(:) = .false. + allocate (lines_in(num_lines)) + lines_in(:) = ' ' ! Read the actual lines. if (all_PEs_read .or. is_root_pe()) then ! rewind the parameter file rewind(iounit) - ! Populate param_data%line + ! Populate param_data%fln%line num_lines = 0 do while(.true.) read(iounit, '(a)', end=18) line @@ -419,7 +422,7 @@ subroutine populate_param_data(iounit, filename, param_data) line = removeComments(line) line = simplifyWhiteSpace(line(:len_trim(line))) num_lines = num_lines + 1 - param_data%line(num_lines) = line + lines_in(num_lines) = line endif if (openMultiLineComment(line)) inMultiLineComment=.true. endif @@ -431,10 +434,22 @@ subroutine populate_param_data(iounit, filename, param_data) // 'reading of '//trim(filename)) endif ! (is_root_pe()) - ! Broadcast the populated array param_data%line + ! Broadcast the populated array lines_in if (.not. all_PEs_read) then - call broadcast(param_data%line, INPUT_STR_LENGTH, root_pe()) + call broadcast(lines_in, INPUT_STR_LENGTH, root_pe()) endif + + ! Allocate space to hold contents of the parameter file, including the lines in param_data%fln + allocate(param_data%fln(num_lines)) + allocate(param_data%line_used(num_lines)) + param_data%line_used(:) = .false. + ! Populate param_data%fln%line with the keyword lines from parameter file + do n=1,num_lines + param_data%fln(n)%line = lines_in(n) + enddo + + deallocate(lines_in) + end subroutine populate_param_data @@ -921,10 +936,10 @@ function max_input_line_length(CS, pf_num) result(max_len) ! Scan through each line of the file do count = 1, CS%param_data(ipf)%num_lines - ! line = CS%param_data(ipf)%line(count) - last = len_trim(CS%param_data(ipf)%line(count)) + ! line = CS%param_data(ipf)%fln(count)%line + last = len_trim(CS%param_data(ipf)%fln(count)%line) last_char = " " - if (last > 0) last_char = CS%param_data(ipf)%line(count)(last:last) + if (last > 0) last_char = CS%param_data(ipf)%fln(count)%line(last:last) ! Check if line ends in continuation character (either & or \) ! Note achar(92) is a backslash if (last_char == achar(92) .or. last_char == "&") then @@ -933,7 +948,7 @@ function max_input_line_length(CS, pf_num) result(max_len) if (count==CS%param_data(ipf)%num_lines .and. is_root_pe()) & call MOM_error(FATAL, "MOM_file_parser : the last line of the file ends in a"// & " continuation character but there are no more lines to read. "// & - " Line: '"//trim(CS%param_data(ipf)%line(count)(:last))//"'"// & + " Line: '"//trim(CS%param_data(ipf)%fln(count)%line(:last))//"'"// & " in file "//trim(filename)//".") cycle ! cycle inorder to append the next line of the file elseif (continuedLine) then @@ -999,7 +1014,7 @@ subroutine get_variable_line(CS, varname, found, defined, value_string, paramIsL ! Scan through each line of the file do count = 1, CS%param_data(ipf)%num_lines - line = CS%param_data(ipf)%line(count) + line = CS%param_data(ipf)%fln(count)%line last = len_trim(line) last1 = max(1,last) From 5ee5daee33cb535c66b5ff5573e6e77bacaeb0b9 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 14 Mar 2022 23:05:00 -0400 Subject: [PATCH 3/4] Reduce the populate_param_data memory footprint Use a character buffer to minimize the memory footprint associated with reading the input parameters on root_PE and broadcasting it to all PEs. All answers are bitwise identical. --- src/framework/MOM_file_parser.F90 | 49 +++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/framework/MOM_file_parser.F90 b/src/framework/MOM_file_parser.F90 index a2fe6ec7a8..738f842842 100644 --- a/src/framework/MOM_file_parser.F90 +++ b/src/framework/MOM_file_parser.F90 @@ -364,10 +364,14 @@ subroutine populate_param_data(iounit, filename, param_data) ! Local variables character(len=INPUT_STR_LENGTH) :: line - character(len=INPUT_STR_LENGTH), allocatable, dimension(:) :: lines_in - integer :: n, num_lines + character(len=1), allocatable, dimension(:) :: char_buf + integer, allocatable, dimension(:) :: line_len ! The trimmed length of each processed input line + integer :: n, num_lines, total_chars, ch, rsc, llen, int_buf(2) logical :: inMultiLineComment + character(len=80) :: frag1, frag2 + + ! Find the number of keyword lines in a parameter file if (all_PEs_read .or. is_root_pe()) then ! rewind the parameter file @@ -375,6 +379,7 @@ subroutine populate_param_data(iounit, filename, param_data) ! count the number of valid entries in the parameter file num_lines = 0 + total_chars = 0 inMultiLineComment = .false. do while(.true.) read(iounit, '(a)', end=8) line @@ -382,7 +387,12 @@ subroutine populate_param_data(iounit, filename, param_data) if (inMultiLineComment) then if (closeMultiLineComment(line)) inMultiLineComment=.false. else - if (lastNonCommentNonBlank(line)>0) num_lines = num_lines + 1 + if (lastNonCommentNonBlank(line)>0) then + line = removeComments(line) + line = simplifyWhiteSpace(line(:len_trim(line))) + num_lines = num_lines + 1 + total_chars = total_chars + len_trim(line) + endif if (openMultiLineComment(line)) inMultiLineComment=.true. endif enddo ! while (.true.) @@ -392,18 +402,22 @@ subroutine populate_param_data(iounit, filename, param_data) call MOM_error(FATAL, 'MOM_file_parser : A C-style multi-line comment '// & '(/* ... */) was not closed before the end of '//trim(filename)) - param_data%num_lines = num_lines + + int_buf(1) = num_lines + int_buf(2) = total_chars endif ! (is_root_pe()) ! Broadcast the number of valid entries in parameter file if (.not. all_PEs_read) then - call broadcast(param_data%num_lines, root_pe()) + call broadcast(int_buf, 2, root_pe()) + num_lines = int_buf(1) + total_chars = int_buf(2) endif ! Set up the space for storing the actual lines. - num_lines = param_data%num_lines - allocate (lines_in(num_lines)) - lines_in(:) = ' ' + param_data%num_lines = num_lines + allocate (line_len(num_lines), source=0) + allocate (char_buf(total_chars), source=" ") ! Read the actual lines. if (all_PEs_read .or. is_root_pe()) then @@ -412,6 +426,7 @@ subroutine populate_param_data(iounit, filename, param_data) ! Populate param_data%fln%line num_lines = 0 + rsc = 0 do while(.true.) read(iounit, '(a)', end=18) line line = replaceTabs(line) @@ -422,7 +437,10 @@ subroutine populate_param_data(iounit, filename, param_data) line = removeComments(line) line = simplifyWhiteSpace(line(:len_trim(line))) num_lines = num_lines + 1 - lines_in(num_lines) = line + llen = len_trim(line) + line_len(num_lines) = llen + do ch=1,llen ; char_buf(rsc+ch)(1:1) = line(ch:ch) ; enddo + rsc = rsc + llen endif if (openMultiLineComment(line)) inMultiLineComment=.true. endif @@ -434,9 +452,10 @@ subroutine populate_param_data(iounit, filename, param_data) // 'reading of '//trim(filename)) endif ! (is_root_pe()) - ! Broadcast the populated array lines_in + ! Broadcast the populated arrays line_len and char_buf if (.not. all_PEs_read) then - call broadcast(lines_in, INPUT_STR_LENGTH, root_pe()) + call broadcast(line_len, num_lines, root_pe()) + call broadcast(char_buf(1:total_chars), 1, root_pe()) endif ! Allocate space to hold contents of the parameter file, including the lines in param_data%fln @@ -444,11 +463,15 @@ subroutine populate_param_data(iounit, filename, param_data) allocate(param_data%line_used(num_lines)) param_data%line_used(:) = .false. ! Populate param_data%fln%line with the keyword lines from parameter file + rsc = 0 do n=1,num_lines - param_data%fln(n)%line = lines_in(n) + line(1:INPUT_STR_LENGTH) = " " + do ch=1,line_len(n) ; line(ch:ch) = char_buf(rsc+ch)(1:1) ; enddo + param_data%fln(n)%line = trim(line) + rsc = rsc + line_len(n) enddo - deallocate(lines_in) + deallocate(char_buf) ; deallocate(line_len) end subroutine populate_param_data From 11328d4403b241b91cf460232f6532a7a3c218f7 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Tue, 15 Mar 2022 10:03:41 -0400 Subject: [PATCH 4/4] Set INPUT_STR_LENGTH to 1024 Now that INPUT_STR_LENGTH is only used as the size of a single temporary character string, the maximum permitted line length can be increased to a larger value without unduly impacting the memory footprint of the model, so it has been increased to 1024. If this is not large enough, considering that MOM6 also supports line continuation, there is a bigger problem than this limit. Some message strings were also changed to be allocatable, while others have sizes that are set using param_file_type%max_line_len. Also eliminated the hard-coded internal parameter all_PEs_read, which had a note that it should have been eliminated in about 2010 (before MOM6 existed). All answers and output are bitwise identical. --- src/framework/MOM_file_parser.F90 | 90 +++++++++++++------------------ 1 file changed, 38 insertions(+), 52 deletions(-) diff --git a/src/framework/MOM_file_parser.F90 b/src/framework/MOM_file_parser.F90 index 738f842842..853d3c4de4 100644 --- a/src/framework/MOM_file_parser.F90 +++ b/src/framework/MOM_file_parser.F90 @@ -16,13 +16,14 @@ module MOM_file_parser implicit none ; private +! These are hard-coded limits that are used in the following code. They should be set +! generously enough not to impose any significant limitations. integer, parameter, public :: MAX_PARAM_FILES = 5 !< Maximum number of parameter files. -integer, parameter :: INPUT_STR_LENGTH = 320 !< Maximum line length in parameter file. -integer, parameter :: FILENAME_LENGTH = 200 !< Maximum number of characters in file names. +integer, parameter :: INPUT_STR_LENGTH = 1024 !< Maximum line length in parameter file. Lines that + !! are combined by ending in '\' or '&' can exceed + !! this limit after merging. +integer, parameter :: FILENAME_LENGTH = 200 !< Maximum number of characters in file names. -! The all_PEs_read option should be eliminated with post-riga shared code. -logical :: all_PEs_read = .false. !< If true, all PEs read the input files - !! TODO: Eliminate this parameter !>@{ Default values for parameters logical, parameter :: report_unused_default = .true. @@ -138,7 +139,6 @@ subroutine open_param_file(filename, CS, checkable, component, doc_file_dir) logical :: file_exists, unit_in_use, Netcdf_file, may_check, reopened_file integer :: ios, iounit, strlen, i character(len=240) :: doc_path - character(len=16) :: sub_string type(parameter_block), pointer :: block => NULL() may_check = .true. ; if (present(checkable)) may_check = checkable @@ -183,11 +183,10 @@ subroutine open_param_file(filename, CS, checkable, component, doc_file_dir) if (Netcdf_file) & call MOM_error(FATAL,"open_param_file: NetCDF files are not yet supported.") - if (all_PEs_read .or. is_root_pe()) then + if (is_root_pe()) then open(newunit=iounit, file=trim(filename), access='SEQUENTIAL', & form='FORMATTED', action='READ', position='REWIND', iostat=ios) - if (ios /= 0) call MOM_error(FATAL, "open_param_file: Error opening '"// & - trim(filename)//"'.") + if (ios /= 0) call MOM_error(FATAL, "open_param_file: Error opening '"//trim(filename)//"'.") else iounit = 1 endif @@ -202,15 +201,11 @@ subroutine open_param_file(filename, CS, checkable, component, doc_file_dir) if (associated(CS%blockName)) deallocate(CS%blockName) allocate(block) ; block%name = '' ; CS%blockName => block - call MOM_mesg("open_param_file: "// trim(filename)// & - " has been opened successfully.", 5) + call MOM_mesg("open_param_file: "// trim(filename)//" has been opened successfully.", 5) call populate_param_data(iounit, filename, CS%param_data(i)) ! Increment the maximum line length, but always report values in blocks of 4 characters. CS%max_line_len = max(CS%max_line_len, 4 + 4*(max_input_line_length(CS, i) - 1) / 4) - write (sub_string, '(i4.4)') CS%max_line_len - call MOM_mesg("open_param_file: Maximum input line length determined to be "//& - trim(adjustl(sub_string))//" characters.", 5) call read_param(CS,"SEND_LOG_TO_STDOUT",CS%log_to_stdout) call read_param(CS,"REPORT_UNUSED_PARAMS",CS%report_unused) @@ -264,7 +259,7 @@ subroutine close_param_file(CS, quiet_close, component) if (present(quiet_close)) then ; if (quiet_close) then do i = 1, CS%nfiles - if (all_PEs_read .or. is_root_pe()) close(CS%iounit(i)) + if (is_root_pe()) close(CS%iounit(i)) call MOM_mesg("close_param_file: "// trim(CS%filename(i))// & " has been closed successfully.", 5) CS%iounit(i) = -1 @@ -334,9 +329,8 @@ subroutine close_param_file(CS, quiet_close, component) enddo endif - if (all_PEs_read .or. is_root_pe()) close(CS%iounit(i)) - call MOM_mesg("close_param_file: "// trim(CS%filename(i))// & - " has been closed successfully.", 5) + if (is_root_pe()) close(CS%iounit(i)) + call MOM_mesg("close_param_file: "// trim(CS%filename(i))//" has been closed successfully.", 5) CS%iounit(i) = -1 CS%filename(i) = '' CS%NetCDF_file(i) = .false. @@ -369,11 +363,8 @@ subroutine populate_param_data(iounit, filename, param_data) integer :: n, num_lines, total_chars, ch, rsc, llen, int_buf(2) logical :: inMultiLineComment - character(len=80) :: frag1, frag2 - - ! Find the number of keyword lines in a parameter file - if (all_PEs_read .or. is_root_pe()) then + if (is_root_pe()) then ! rewind the parameter file rewind(iounit) @@ -408,11 +399,9 @@ subroutine populate_param_data(iounit, filename, param_data) endif ! (is_root_pe()) ! Broadcast the number of valid entries in parameter file - if (.not. all_PEs_read) then - call broadcast(int_buf, 2, root_pe()) - num_lines = int_buf(1) - total_chars = int_buf(2) - endif + call broadcast(int_buf, 2, root_pe()) + num_lines = int_buf(1) + total_chars = int_buf(2) ! Set up the space for storing the actual lines. param_data%num_lines = num_lines @@ -420,7 +409,7 @@ subroutine populate_param_data(iounit, filename, param_data) allocate (char_buf(total_chars), source=" ") ! Read the actual lines. - if (all_PEs_read .or. is_root_pe()) then + if (is_root_pe()) then ! rewind the parameter file rewind(iounit) @@ -435,6 +424,10 @@ subroutine populate_param_data(iounit, filename, param_data) else if (lastNonCommentNonBlank(line)>0) then line = removeComments(line) + if ((len_trim(line) > 1000) .and. is_root_PE()) then + call MOM_error(WARNING, "MOM_file_parser: Consider using continuation to split up "//& + "the excessivley long parameter input line "//trim(line)) + endif line = simplifyWhiteSpace(line(:len_trim(line))) num_lines = num_lines + 1 llen = len_trim(line) @@ -453,10 +446,8 @@ subroutine populate_param_data(iounit, filename, param_data) endif ! (is_root_pe()) ! Broadcast the populated arrays line_len and char_buf - if (.not. all_PEs_read) then - call broadcast(line_len, num_lines, root_pe()) - call broadcast(char_buf(1:total_chars), 1, root_pe()) - endif + call broadcast(line_len, num_lines, root_pe()) + call broadcast(char_buf(1:total_chars), 1, root_pe()) ! Allocate space to hold contents of the parameter file, including the lines in param_data%fln allocate(param_data%fln(num_lines)) @@ -767,8 +758,7 @@ subroutine read_param_char(CS, varname, value, fail_if_missing) if (found) then value = trim(strip_quotes(value_string(1))) elseif (present(fail_if_missing)) then ; if (fail_if_missing) then - call MOM_error(FATAL,'Unable to find variable '//trim(varname)// & - ' in any input files.') + call MOM_error(FATAL, 'Unable to find variable '//trim(varname)//' in any input files.') endif ; endif end subroutine read_param_char @@ -805,8 +795,7 @@ subroutine read_param_char_array(CS, varname, value, fail_if_missing) endif do i=i_out,SIZE(value) ; value(i) = " " ; enddo elseif (present(fail_if_missing)) then ; if (fail_if_missing) then - call MOM_error(FATAL,'Unable to find variable '//trim(varname)// & - ' in any input files.') + call MOM_error(FATAL, 'Unable to find variable '//trim(varname)//' in any input files.') endif ; endif end subroutine read_param_char_array @@ -829,8 +818,7 @@ subroutine read_param_logical(CS, varname, value, fail_if_missing) if (found) then value = defined elseif (present(fail_if_missing)) then ; if (fail_if_missing) then - call MOM_error(FATAL,'Unable to find variable '//trim(varname)// & - ' in any input files.') + call MOM_error(FATAL, 'Unable to find variable '//trim(varname)//' in any input files.') endif ; endif end subroutine read_param_logical @@ -891,11 +879,9 @@ subroutine read_param_time(CS, varname, value, timeunit, fail_if_missing, date_f else if (present(fail_if_missing)) then ; if (fail_if_missing) then if (.not.found) then - call MOM_error(FATAL,'Unable to find variable '//trim(varname)// & - ' in any input files.') + call MOM_error(FATAL, 'Unable to find variable '//trim(varname)//' in any input files.') else - call MOM_error(FATAL,'Variable '//trim(varname)// & - ' found but not set in input files.') + call MOM_error(FATAL, 'Variable '//trim(varname)//' found but not set in input files.') endif endif ; endif endif @@ -907,7 +893,7 @@ end subroutine read_param_time !> This function removes single and double quotes from a character string function strip_quotes(val_str) - character(len=*) :: val_str !< The character string to work on + character(len=*), intent(in) :: val_str !< The character string to work on character(len=len(val_str)) :: strip_quotes ! Local variables integer :: i @@ -939,7 +925,6 @@ function max_input_line_length(CS, pf_num) result(max_len) ! Local variables character(len=FILENAME_LENGTH) :: filename - ! character(len=:), allocatable :: line character :: last_char integer :: ipf, ipf_s, ipf_e integer :: last, last1, line_len, count, contBufSize @@ -1003,7 +988,8 @@ subroutine get_variable_line(CS, varname, found, defined, value_string, paramIsL ! Local variables character(len=CS%max_line_len) :: val_str, lname, origLine - character(len=CS%max_line_len) :: line, continuationBuffer, blockName + character(len=CS%max_line_len) :: line, continuationBuffer + character(len=240) :: blockName character(len=FILENAME_LENGTH) :: filename integer :: is, id, isd, isu, ise, iso, ipf integer :: last, last1, ival, oval, max_vals, count, contBufSize @@ -1407,7 +1393,7 @@ subroutine log_param_int_array(CS, modulename, varname, value, desc, & logical, optional, intent(in) :: like_default !< If present and true, log this parameter as !! though it has the default value, even if there is no default. - character(len=1320) :: mesg + character(len=CS%max_line_len+120) :: mesg character(len=240) :: myunits write(mesg, '(" ",a," ",a,": ",A)') trim(modulename), trim(varname), trim(left_ints(value)) @@ -1549,16 +1535,16 @@ subroutine log_param_char(CS, modulename, varname, value, desc, units, & logical, optional, intent(in) :: like_default !< If present and true, log this parameter as !! though it has the default value, even if there is no default. - character(len=1024) :: mesg, myunits + character(len=:), allocatable :: mesg + character(len=240) :: myunits - write(mesg, '(" ",a," ",a,": ",a)') & - trim(modulename), trim(varname), trim(value) + mesg = " " // trim(modulename) // " " // trim(varname) // ": " // trim(value) if (is_root_pe()) then if (CS%log_open) write(CS%stdlog,'(a)') trim(mesg) if (CS%log_to_stdout) write(CS%stdout,'(a)') trim(mesg) endif - myunits=" "; if (present(units)) write(myunits(1:1024),'(A)') trim(units) + myunits=" "; if (present(units)) write(myunits(1:240),'(A)') trim(units) if (present(desc)) & call doc_param(CS%doc, varname, desc, myunits, value, default, & layoutParam=layoutParam, debuggingParam=debuggingParam, like_default=like_default) @@ -1936,7 +1922,7 @@ subroutine get_param_char_array(CS, modulename, varname, value, desc, units, & ! Local variables logical :: do_read, do_log integer :: i, len_tot, len_val - character(len=1024) :: cat_val + character(len=:), allocatable :: cat_val do_read = .true. ; if (present(do_not_read)) do_read = .not.do_not_read do_log = .true. ; if (present(do_not_log)) do_log = .not.do_not_log @@ -1947,7 +1933,7 @@ subroutine get_param_char_array(CS, modulename, varname, value, desc, units, & endif if (do_log) then - cat_val = trim(value(1)); len_tot = len_trim(value(1)) + cat_val = trim(value(1)) ; len_tot = len_trim(value(1)) do i=2,size(value) len_val = len_trim(value(i)) if ((len_val > 0) .and. (len_tot + len_val + 2 < 240)) then