3

What is the best way to save a binary snapshot of the variable save which is made out of sample data type below?

program save_it

   type core
      integer, dimension(8) :: indx
   end type core 

   type sample
      integer :: a
      real*8, dimension(:), allocatable :: b
      type(core), dimension(:), allocatable :: c
   end type sample

   ! here it comes
   type(sample) :: save

   ! here we allocate all componenets of variable "save"
   !.
   !.

   ! Now, how to write/read variable "save" to/from external file?

end program save_it 

There is pretty straight binary input/output streaming in C++ but I don't know how to do it in Fortran 90+.

francescalus
  • 30,576
  • 16
  • 61
  • 96
argasm
  • 275
  • 3
  • 15

1 Answers1

3

If by Fortran90+ you mean you are happy with Fortran 2003, then there is the option of user-defined derived type IO. This allows you to wrap the extra bookkeeping required for the allocation in the write statement. I'll put example code at the bottom.

If you don't want to use this feature, which is possibly because you don't have a compiler which supports it (I've tested with ifort 14), then you can mimic the bookkeeping easily enough.

The crucial part is just sending out and reading back in the sizes and allocating the variables before the read.

The code:

module types

   type core
      integer, dimension(8) :: indx
   end type core 

   type sample
      integer :: a
      real*8, dimension(:), allocatable :: b
      type(core), dimension(:), allocatable :: c
    contains
      procedure write_sample
      procedure read_sample
      generic :: write(unformatted) => write_sample
      generic :: read(unformatted) => read_sample
   end type sample

   contains

     ! Unformatted writing for the sample derived type
     subroutine write_sample(dtv, unit, iostat, iomsg)
       class(sample), intent(in) :: dtv
       integer, intent(in) :: unit
       integer, intent(out) :: iostat
       character(*), intent(inout) :: iomsg

       integer i

       ! Write a record giving sizes for the allocation
       write(unit, iostat=iostat, iomsg=iomsg) SIZE(dtv%b), SIZE(dtv%c)
       write(unit, iostat=iostat, iomsg=iomsg) dtv%a, dtv%b, &
                                               (dtv%c(i)%indx, i=1,SIZE(dtv%c))

     end subroutine write_sample

     ! Unformatted reading for the sample derived type
     subroutine read_sample(dtv, unit, iostat, iomsg)
       class(sample), intent(inout) :: dtv
       integer, intent(in) :: unit
       integer, intent(out) :: iostat
       character(*), intent(inout) :: iomsg

       integer i
       integer sizeb, sizec

       ! We first have a record telling us the sizes of components
       read(unit, iostat=iostat, iomsg=iomsg) sizeb, sizec
       ! So we do the allocation
       allocate(dtv%b(sizeb), dtv%c(sizec))
       ! And then finally the reading.
       read(unit, iostat=iostat, iomsg=iomsg) dtv%a, dtv%b, &
                                             (dtv%c(i)%indx, i=1,SIZE(dtv%c))

     end subroutine read_sample

end module types

program save_it
   use types

   implicit none

   integer i, unit_in, unit_out

   ! here it comes
   type(sample) :: save
   type(sample) :: save_test

   ! Define some values - using ifort don't forget to set the compile flag
   save%a = 14
   save%b = [(i*1., i=1, 10)]
   save%c = [core([(i, i=1,8)]), core([(i, i=11, 18)])]

   ! Write out the derived type
   open(newunit=unit_out, file='serial', form='unformatted', &
        status='replace', action='write')
   write(unit_out) save
   close(unit_out)

   ! Read in the derived type to a new one
   open(newunit=unit_in, file='serial', form='unformatted', &
        status='old', action='read')
   read(unit_in) save_test
   close(unit_in)

   ! Test, if we want to be certain

end program save_it

There's certainly a lot of work to be done on making it robust.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • very insightful piece of code and great ideas! Thank you – argasm Mar 27 '14 at 19:09
  • Note that child input/output statements are always treated as non-advancing - both the write_sample and read_sample procedures only write and read their data in the single record created by the parent write and read statements in the main program. – IanH Mar 27 '14 at 20:26
  • @IanH That's a good point, thanks. Perhaps in my procedures' comments I should say "chunk" rather than "record"? Or there's an even less loaded/more correct term? – francescalus Mar 27 '14 at 20:50
  • 1
    @francescalus what compiler are you using? This doesn't work with gfortran 6.3.0 (latest available version I'm aware of for MinGW). I get: `generic :: write(unformatted) => write_sample`, `Error: Expected '=>' at (1)`. If I remove, `(unformatted)`, I get: `write(unit_out) save`, `Error: Data transfer element at (1) cannot have ALLOCATABLE components unless it is processed by a defined input/output procedure`. – Jeff Irwin Jul 29 '18 at 16:12
  • 1
    @JeffIrwin, I used ifort (14 if my comment in the answer is correct, not tested with later). As I understand things you'd need gfortran 7+ for support for defined I/O. – francescalus Jul 29 '18 at 23:11
  • @francescalus thanks! Sorry I missed that you mentioned ifort 14 in your answer. I did find a nice workaround for older gfortran versions for formatted writing though. I was able to overload the `//` operator and then just cat my type with other characters that I'm printing. – Jeff Irwin Aug 01 '18 at 00:05