Read derived types from namelist

I want to read a user-defined type from a namelist but I’m not sure whether is not possible or I’m doing something wrong. My approach:

  • The name list:
&type_nml
  a%x = 3.45d0
/
  • My code:
program read_type_nml
  implicit none
  type try
    real(8) :: x
  end type try
  type(try) :: a

  namelist /type_nml/ a%x
end program read_type_nml
  • My result:
$ gfortran read_type_nml.f90 
read_type_nml.f90:8:23:

   namelist /type_nml/ a%x
                       1
Error: Syntax error in NAMELIST statement at (1)

So I’m doing something wrong or is not possible to do this?

Thanks in advance!

1 Like

I think the type component expression is not allowed in namelist.

1 Like

Correct. Although in another context, that is used in the example

from an internal read, although there has been some argument about whether defining the namelist on a single line is acceptable or not (probably safer to use three, especially with IBM compiler).

1 Like

@iarbina , @urbanjost :

If you don’t mind some code toward custom handling of NAMELIST components of derived type, you could consider defined input/output data transfers that the standard supports since Fortran 2003.

See a working example below if you wish to try (again, hope you don’t mind it’s with a commercial processor whose support staff I have bugged over the years to get this and other standard features working):

Click to see Fortran code

Defined input/output with NAMELIST

module point_m
   enum, bind(C)
      enumerator :: RED
      enumerator :: BLUE
      enumerator :: GREEN  
   end enum
   type :: point_t
      real :: x = 0.0
      real :: y = 0.0
      integer :: color = RED
   contains
      private
      procedure, pass(dtv) :: read_point_t
      procedure, pass(dtv) :: write_point_t
      generic, public :: read(formatted) => read_point_t
      generic, public :: write(formatted) => write_point_t
   end type

   character(len=*), parameter :: QUOTE = "'"
   character(len=*), parameter :: BEGIN_TOKEN = "<"
   character(len=*), parameter :: END_TOKEN = ">"

contains
 
   subroutine read_point_t(dtv, lun, iotype, vlist, istat, imsg)

      ! Argument list
      class(point_t), intent(inout)   :: dtv
      integer, intent(in)             :: lun
      character(len=*), intent(in)    :: iotype
      integer, intent(in)             :: vlist(:)
      integer, intent(out)            :: istat
      character(len=*), intent(inout) :: imsg

      ! Local variables
      real :: x
      real :: y
      integer :: color
      integer :: beg_idx, end_idx
      character(len=2048) :: nml_str
      namelist / point / x, y, color

      select case ( iotype )

         case ( "LISTDIRECTED" )
            ! Elided
         case ( "DT" )
            ! Elided
            if ( size(vlist) == 0 ) istat = 1
         case ( "NAMELIST" )
            read_blk: block
               read( unit=lun, fmt=*, iostat=istat, iomsg=imsg ) nml_str
               if ( istat /= 0 ) exit read_blk 
               beg_idx = index( nml_str, BEGIN_TOKEN )
               if ( beg_idx == 0 ) then
                  istat = 1
                  imsg = "Error parsing namelist string for point_t type"
                  exit read_blk
               end if 
               end_idx = index( nml_str, END_TOKEN, back=.true. )
               if ( end_idx == 0 ) then
                  istat = 2
                  imsg = "Error parsing namelist string for point_t type"
                  exit read_blk
               end if 
               if ( beg_idx + 1 >= end_idx-1 ) then
                  istat = 3
                  imsg = "Error parsing namelist string for point_t type"
                  exit read_blk
               end if 
               read( unit=nml_str(beg_idx+1:end_idx-1), nml=point, iostat=istat, iomsg=imsg )
               if ( istat /= 0 ) exit read_blk
               dtv%x = x
               dtv%y = y
               dtv%color = color 
            end block read_blk 
            return
         case default

      end select

      return

   end subroutine read_point_t

   subroutine write_point_t( dtv, lun, iotype, vlist, istat, imsg )

      ! Argument list
      class(point_t), intent(in)       :: dtv
      integer, intent(in)              :: lun
      character(len=*), intent(in)     :: iotype
      integer, intent(in)              :: vlist(:)
      integer, intent(out)             :: istat
      character (len=*), intent(inout) :: imsg

      ! Local variables
      real :: x
      real :: y
      integer :: color
      character(len=2048) :: nml_str
      namelist / point / x, y, color

      istat = 0
      select case ( iotype )
         case ( "LISTDIRECTED" )
            ! Elided
         case ( "DT" )
            ! Elided
            if ( size(vlist) == 0 ) istat = 1
         case ( "NAMELIST" )
            x = dtv%x
            y = dtv%y
            color = dtv%color
            write( unit=nml_str, nml=point, iostat=istat, iomsg=imsg )
            if ( istat == 0 ) then
               write( unit=lun, fmt="(*(g0))", iostat=istat, iomsg=imsg) QUOTE, BEGIN_TOKEN,        &
                  trim(nml_str), END_TOKEN, QUOTE
            end if 
            return
      end select

      return

   end subroutine

end module
   use, intrinsic :: iso_fortran_env, only : stdout => output_unit
   use point_m
   
   type(point_t) :: point
   character(len=:), allocatable :: stream_data
   character(len=:), allocatable :: msg
   character(len=2048) :: imsg
   integer :: istat
   namelist / dat / msg, point

   stream_data = "&dat msg='Hello World!', point=" // QUOTE // BEGIN_TOKEN // &
     "&point x=1.0, y=2.0, color=1 /" // END_TOKEN // QUOTE // "/"
   allocate( character(len=132) :: msg )
   read( stream_data, nml=dat, iostat=istat, iomsg=imsg )
   if ( istat == 0 ) then
      print *, "point%x = ", point%x, "; expected is 1.0" 
      print *, "point%y = ", point%y, "; expected is 2.0" 
      print *, "point%color = ", point%color, "; expected is 1"
      msg = trim(msg)
      write( stdout, nml=dat )
   else
      print *, "Namelist read failed: iostat = ", istat
      print *, trim(imsg)
   end if 
end     

C:\temp>ifort /standard-semantics p.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.3.0 Build 20210609_000000
Copyright (C) 1985-2021 Intel Corporation. All rights reserved.

Microsoft (R) Incremental Linker Version 14.29.30038.1
Copyright (C) Microsoft Corporation. All rights reserved.

-out:p.exe
-subsystem:console
p.obj

C:\temp>p.exe
point%x = 1.000000 ; expected is 1.0
point%y = 2.000000 ; expected is 2.0
point%color = 1 ; expected is 1
&DAT
MSG = Hello World!,
POINT= ‘<&POINT X= 1.000000 ,Y= 2.000000 ,COLOR= 1/>’
/

C:\temp>

1 Like

Though already mentioned above, I think you can read a by specifying its name in the namelist (rather than the name of a component), something like…

!! test.f90
program main
  implicit none
  type try
    real(8) :: x = 100.0d0, y
    integer :: n = 555
  end type
  type(try) :: a

  namelist /type_nml/ a

  open( 10, file="test.dat", status="old" )  !! (or use "newunit=" keyword)
  read( 10, type_nml )   !! specify namelist here
  close( 10 )
  
  print type_nml
end

!! test.dat
&type_nml
  a%x = 1.23
  a%y = 4.56
/

$ gfortran-10 test.f90 && ./a.out

&TYPE_NML
 A%X=  1.2300000000000000     ,
 A%Y=  4.5599999999999996     ,
 A%N=555        ,

IIRC, only the components specified in the input file will be modified (e.g., a % n is not modified in the above case), so it can be used just to read only a% x, for example.

Btw, I really hope the output of namelist will be in lowercases… (hopefully with one space at both sides of the equal sign). But the output format is compiler-dependent (and also prints all of the contents of a type, including the full strings), so it may be more convenient to just write custom output routines. (In my case, I usually make “a % show()” like method).

1 Like

For the program of @FortranFan, gfortran 12.0.0 20210718 from equation.com gives output

 Namelist read failed: iostat =           -1
 End of file

I appreciate the presentation of advanced features of Fortran, even if gfortran does not yet support them.

Interesting approach. Thanks!

Thanks a lot! This actually solved my problem/question!

I do this by using temporary variables–plain reals and integers etc, that are then assigned to the type parameters.