I am working with a code originally written in Fortran 77 that makes use of namelists (supported by compiler extension at the time of its writing - this feature only became standard in Fortran 90) for reading input files. The namelist input files have groups of namelist variables in between (multiple) plain text headers and footers (see example.nml
). Some groups of namelist variables are only read if certain conditions are met for previously read variables.
When reading all the namelist groups in a file in sequence, executables compiled with gfortran, ifort and nagfor all behave the same and give the expected output. However, when a given namelist group in the input file is to be skipped (the optional reading), gfortran and ifort executables handle this as desired, while the executable compiled with nagfor raises a runtime error:
Runtime Error: reader.f90, line 27: Expected NAMELIST group /GRP3/ but found /GRP2/ Program terminated by I/O error on unit 15 (File="example.nml",Formatted,Sequential)
As a minimal working example reproducing the problem, consider the namelist file example.nml
and driver program reader.f90
given below, in which NUM2
from namelist group GRP2
should only be read if NUM1
from namelist group GRP1
equals 1
:
example.nml:
this is a header
&GRP1 NUM1=1 /
&GRP2 NUM2=2 /
&GRP3 NUM3=3 /
this is a footer
reader.f90:
program reader
implicit none
character(len=40) :: hdr, ftr
integer :: num1, num2, num3, icode
! namelist definition
namelist/grp1/num1
namelist/grp2/num2
namelist/grp3/num3
! open input file
open(unit=15, file='example.nml', form='formatted', status='old', iostat=icode)
! read input data from namelists
read(15, '(a)') hdr
print *, hdr
read(15, grp1)
print *, num1
if (num1 == 1) then
read(15, grp2)
print *, num2
end if
read(15,grp3)
print *, num3
read(15, '(a)') ftr
print *, ftr
! close input file
close(unit=15)
end program reader
All executables give this expected output when NUM1=1
:
this is a header
1
2
3
this is a footer
However, when e.g. NUM1=0
, the executables compiled with gfortran and ifort give the desired output:
this is a header
0
3
this is a footer
while the executable compiled with nagfor (which is known for being strictly standard conforming), reads the header and first namelist group:
this is a header
0
but then terminates with the previously mentioned runtime error.
As indicated by the error message, example.nml
is accessed sequentially, and if that is the case, /GRP2/ is the next record to be read, not /GRP3/ as the program logic asks for, so the error message makes sense to me.
So my question is this:
read(15,*)
in an else
branch for the if
statement in reader.f90
. This seems to work with all the mentioned compilers. Would this make the code standard conforming (Fortran 90 or later)?These are the compiler versions and options that were used to compile the executables:
gfortran -Wall -Wextra -fcheck=all -g -Og -fbacktrace reader.f90
ifort -Od -debug:all -check:all -traceback reader.f90
nagfor -O0 -g -C reader.f90
When namelist formatting is requested on an external file, the namelist record is taken to commence at the record at the current position of the file.
The structure of a namelist input record is well defined by the language specification (see Fortran 2018 13.11.3.1, for example). In particular, this does not allow a mismatching namelist group name. nagfor complaining about this does so legitimately.
Several compilers do indeed appear to continue skipping over records until the namelist group is identified in a record, but I'm not aware of compiler flags available to control that behaviour. Historically, the case was generally that multiple namelists would be specified using distinct files.
Coming to your "simple workaround": this is, alas, not sufficient in the general case. Namelist input may consume several records of an external file. read(15,*)
will advance the file position by only a single record. You will want to advance to after the terminating record for the namelist.
When you know the namelist is just that single record then the workaround is good.