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
Oh, and ifort is 16.0.2
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).
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.
In Fortran 95, namelist I/O with internal files was not allowed, perhaps because of the issues exposed in this thread.
220.127.116.11 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.
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
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.
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
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.