A Simple Fortran Control File Example

May 6, 2008

Most scientific applications require a set of parameters such as dimensions, starting values, and error tolerances. In Fortran one has the option of hard-coding those values in the source code itself, changing them and recompiling the program as needed, or using a control file approach where the parameters are read from a plain text file. After a few hundred iterations of the edit-compile-run loop one starts to appreciate the second approach. Control files also facilitate reproducible research as the same source code can be used to generate multiple sets of results or figures, each with a separate control file.

A simple approach is to require the parameters to be given in a text file in a particular order. Reading this file is as easy as writing a series of read statements for each variable. We could read the control file

1 2 3 4 5

with the following code:

real :: pi
integer, dimension(5) :: vector

open(15, file='control_file.txt')
read(15, *) pi
read(15, *) vector

Unfortunately this results in control files which are difficult to work with as it becomes hard to remember which parameter is on which line. A better approach allows for line labels to identify parameters and does not require them to appear in a particular order:

vector  1 2 3 4 5
pi      3.14159

I’ve written a simple example program to illustrate how to parse control files in this format (control_file.f90, control_file.txt):

program control_file
  implicit none

  ! Input related variables
  character(len=100) :: buffer, label
  integer :: pos
  integer, parameter :: fh = 15
  integer :: ios = 0
  integer :: line = 0

  ! Control file variables
  real :: pi
  integer, dimension(5) :: vector

  open(fh, file='control_file.txt')

  ! ios is negative if an end of record condition is encountered or if
  ! an endfile condition was detected.  It is positive if an error was
  ! detected.  ios is zero otherwise.

  do while (ios == 0)
     read(fh, '(A)', iostat=ios) buffer
     if (ios == 0) then
        line = line + 1

        ! Find the first instance of whitespace.  Split label and data.
        pos = scan(buffer, '    ')
        label = buffer(1:pos)
        buffer = buffer(pos+1:)

        select case (label)
        case ('pi')
           read(buffer, *, iostat=ios) pi
           print *, 'Read pi: ', pi
        case ('vector')
           read(buffer, *, iostat=ios) vector
           print *, 'Read vector: ', vector
        case default
           print *, 'Skipping invalid label at line', line
        end select
     end if
  end do

end program control_file

I have hard-coded the name of the control file in the example program. In practice you probably want to allow a filename to be given on the command-line. If you have a Fortran 2003 compliant compiler you can use the new command line argument intrinsic functions as follows:

! Filename of the control file
character(len=100) :: ctrl_file

! The control file name is the first command-line argument
call get_command_argument(1, ctrl_file)

! Call a subroutine to parse the control file
call read_control_file(ctrl_file)