Search code examples
fortrangfortranintel-fortrannag-fortran

How to handle an optional group in a Fortran Namelist


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:

  1. Can the shown behaviour be attributed to standard (non-)conformance enforced by nagfor and not gfortran and ifort?
  2. If so, does this mean that the non-sequential reading observed with gfortran and ifort is due to extensions supported by these compilers (and not nagfor)? Can this be turned on/off using compiler flags?
  3. The simplest workaround I can think of (minimal change to a large existing program), would be to add a dummy 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:

  • GNU Fortran (Ubuntu 9.1.0-2ubuntu2~18.04) 9.1.0: gfortran -Wall -Wextra -fcheck=all -g -Og -fbacktrace reader.f90
  • Intel(R) Visual Fortran, Version 16.0 Build 20160415: ifort -Od -debug:all -check:all -traceback reader.f90
  • NAG Fortran Compiler Release 6.1(Tozai) Build 6116: nagfor -O0 -g -C reader.f90

Solution

  • 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.