Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Output statement overflows record on writing a namelist to a string

Todor_K_
Novice
3,160 Views

This piece of code,

program dong
  type x
     integer :: a,b,c
  end type x

  type(x) :: xx

  character(len=65536) :: str
  namelist /zac/ xx

  write(str,nml=zac)
  print *, trim(str)
end program dong

breaks down with the following runtime error:

forrtl: severe (66): output statement overflows record, unit -5, file Internal NML Write

Why? 

Oh, and ifort is 16.0.2

0 Kudos
8 Replies
Steven_L_Intel1
Employee
3,160 Views

Hint - try writing the namelist out to the console and see what you get. Then think about how internal files work with mutiple records.

0 Kudos
Todor_K_
Novice
3,160 Views

Hm. 

I can think of a number of dirty solutions, bur none of them involves only an internal file, derived type and a namelist consisting only of the derived type. Any standard solution to this using ifort? I know that gfortran ver>=5 happily munches through the code above (though, not sure on whether the Fortran standard covers the desired behaviour).

0 Kudos
Steven_L_Intel1
Employee
3,160 Views

The standard leaves it up to the implementation when to start a new record in namelist and list-directed output. You could use an array of character strings as the internal file  - each record would be a separate element. I don't know what you want to do with the data once you get it.

0 Kudos
mecej4
Honored Contributor III
3,160 Views

In Fortran 95, namelist I/O with internal files was not allowed, perhaps because of the issues exposed in this thread.

9.2.2.2 Internal file restrictions

An internal file has the following restrictions:

(1) Reading and writing records shall be accomplished only by sequential access formatted input/output statements that do not specify namelist formatting.

0 Kudos
IanH
Honored Contributor II
3,160 Views

I think Steve's hint is that the namelist output requires more than one record. 

You should be able to accommodate the freedom given to the Fortran processor for the layout of namelist output by using a character array as the internal file.  Each element in the array corresponds to a record.

(Well, that's all a bit out of date.  One of these days I will remember to refresh the forum before I submit a post, but anyway...)

But I think there's still a compiler issue here - I get the record overflow issue with 17.0 update 1 even if an excessively sized array is used. 

program dong
  implicit none
  type x
     integer :: a,b,c
  end type x
  
  type(x) :: xx
  
  character(len=1024) :: str(20)
  namelist /zac/ xx
  
  integer :: i
  
  xx = x(1, 2, 3)
  
  write (*, nml=zac)
  
  write(str,nml=zac)
  print "(*(A,:,/))", (trim(str(i)), i = 1, size(str))
end program dong

 

0 Kudos
Todor_K_
Novice
3,160 Views

Hi all, 

Thanks for reading.

IanH, Steve: The char array was the first thing I tried after the Steve's Delphic answer, but I got an overflow with ifort 16.0.2 , too.

Strings+namelists are the easiest way to communicate complicated data around. You can send them simply via the MPI, can read and write to them, they approach SEXPs, or JSON in the usability. So, this is what I am going to use it for, Steve.

 

0 Kudos
Todor_K_
Novice
3,160 Views

Ok, I've hacked together an ugly cludge to get around the ifort's internal-file-has-only-one-record limitation. Let the posterity judge me for my sin.  

The important subroutine is multi2single which converts a multiple record file into a single record. Now, I don't claim it works in general, but it does cover my use-cases ;) .

The rest is there to set up a suitable test environment. Certain variables, such as the character length of the file, are unknown before you make file inquiries. I could have used used allocatable strings [len=:], to deal with character strings that depend on the aforementioned vars, but I tend to avoid relying on anything too fancy, because it will certainly break at least one of the compilers I depend on (just look at the original post in this thread). Instead, I use blocks to set up the dynamical strings on the stack. I generally like stack more than allocate since it's the next best thing to a garbage collector :) . Blocks tend to work with most compilers I need. Well, in case  they don't, I can use internal procedures (is that the name?). 

Laslty, a plea to ifort devs: please, please, please: ensure that internal procs, blocks , trim and len_trim are never broken in any stable release :)

 

module rdnmlst
  implicit none
  type :: yyy
     integer :: x,y,z
  end type yyy
  
  type :: xxx
     type(yyy) :: q
     integer :: a,b,c
  end type xxx
  
contains
  integer function rough_filelen(fl)
    ! Gives an estimate of the size of a formatted file in chars.
    character(len=*), intent(in) :: fl
    integer :: recsz
    open(10,form='formatted',file=fl)
    inquire(10,recl=recsz)

    block
      character(len=recsz) :: blah
      character(len=recsz) :: fmt
      integer :: row
      fmt=''
      write(fmt,*) '(',recsz,'A',')'
      row=0
      do while(1==1)
         read(10,fmt=fmt,err=111,end=111) blah
         row=row+1
      enddo

111   close(10)
      rough_filelen=(row+1)*recsz
    end block

    
  end function rough_filelen
  
         
       
    
  subroutine multi2single(u,str)
    ! Converts multiple records to single.
    integer, intent(in) :: u    !file unit
    character(len=*),intent(out) :: str !output internal file
    integer :: recsz
    
    inquire(u,recl=recsz)
    block
      character(len=recsz) :: fmt  
      integer :: p
      fmt=''
      write(fmt,*) '(',recsz,'A',')'
      p=1
      do while(1==1)
         read(10,fmt=fmt,err=111,end=111) str(p:)
         p=len_trim(str)+1
      end do
111   close(10)
    end block
    
  end subroutine multi2single
  
  subroutine mk_file(fnm)
    ! Produce a relatively complicated namelist file.
    character(len=*),intent(in) :: fnm
    type(xxx) :: ob
    namelist /zac/ ob
    ob%a=1
    ob%b=2
    ob%c=3
    ob%q%x=11
    ob%q%y=2243223
    ob%q%z=-1223344356
    
    open(10,file=fnm,form='formatted',status='replace')
    write(10,nml=zac)
    close(10)
  end subroutine mk_file
  
    

  subroutine test()

    integer :: flsz
    type(xxx) :: ob
    namelist /zac/ ob
    
    call mk_file('test.nml')
    flsz=rough_filelen('test.nml')
    print *, 'FLSZ', flsz


    block
      character(len=flsz) :: holder
      open(10,file='test.nml',form='formatted', status='old')
      call multi2single(10,holder)
      read(holder,nml=zac)
      close(10)
      write(*,*) ob
    end block
    
  end subroutine test
  
    
  
end module rdnmlst

program EinstIEn
  use rdnmlst
  call test()
end program

 

0 Kudos
Steven_L_Intel1
Employee
3,160 Views

Thanks for the example showing that it's not working the way it's supposed to. Escalated as issue DPD200415969. I did notice that it is trying to fill up the "record", but not quite getting it right.

0 Kudos
Reply