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
3.14159
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
close(15)
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)