1

Hi I am trying to write a two dimensional array to a .dat file. A sample code is below:

integer :: i,j
integer, parameter :: nx=100, ny=100
real,dimension(-nx:nx,-ny:ny) :: f,g

open(unit=1,file='file1.dat')
open(unit=2,file='file2.dat')

So each 2D array is given by

f(i,j)
g(i,j)

Assuming these are filled, how can I write them to a .dat file? I tried using implied do loops

write(1,*) (f(i,j), i=-nx,nx), j=-ny,ny)
write(2,*) (g(i,j), i=-nx,nx), j=-ny,ny)

however that just stores the 2D array into a .dat file as 1 row which is not what I want. I want the store 2D array as a 2D array (like a matrix would be stored). Since nx=ny, the 2D array should just be a square matrix.

Jeff Faraci
  • 403
  • 13
  • 28
  • Have you seen [Best way to write a large array to file in fortran? Text vs Other](http://stackoverflow.com/a/24395990/1115360) – Andrew Morton Oct 25 '16 at 19:36
  • @AndrewMorton Thanks a lot for the suggestion. I've actually been reading through that one all day, and another helpful link I found on stack exchange. However, I am still not able to apply it to my situation. (I know it should be simple but my lack of fortran knowledge is really showing). – Jeff Faraci Oct 25 '16 at 19:41
  • 2
    But it is there, the lines `do i=1,asize; write(u,*) (array(i,j), j=1,asize); enddo` are the solution. – Vladimir F Героям слава Oct 25 '16 at 20:34
  • @VladimirF Thanks a lot. I was not able to look at the code and pick that part out as the solution as you did. I have to think about it now and make sure I get it, I will let you know if I have any questions here. If not I can delete the question, thanks for your help. – Jeff Faraci Oct 25 '16 at 20:38
  • @VladimirF What is u? It is in the write statement in the lines you posted. It seems it's defined as an integer, I do not have an integer like this, I have x,y (position coordinates) and time. – Jeff Faraci Oct 25 '16 at 20:43
  • `u` is the unit number. You can write it longer as `unit=u`. You use `1` and `2` in your question, but that is not a good practice, one should use larger numbers. – Vladimir F Героям слава Oct 25 '16 at 20:54
  • `dimension(-nx,nx:-ny,ny)` defines a **3D** array. Did you mean `dimension(-nx:nx,-ny:ny)` ? – John Alexiou Oct 25 '16 at 21:06
  • @VladimirF Thanks a lot. Now it works in my code. I will read up more on why to choose larger numbers in the write statement. I never knew what to choose there, I just randomly started at 1 and worked up. thanks for the help. – Jeff Faraci Oct 25 '16 at 21:09
  • @ja72 Yes I meant that, thanks a lot. I fixed the post. – Jeff Faraci Oct 25 '16 at 21:09

1 Answers1

1

First, an important observation. When you pass arrays as arguments to subroutines, the upper and lower bounds of the actual argument are not maintained in the call unless the lower bound is 1. However, when passing pointers, the lower and upper bounds are maintained. See: Fortran subroutine returning wrong values

Second, unlike Java, C/C++, Python, etc, there is no concept of a file in FORTRAN/Fortran. See: http://docs.cray.com/books/S-3695-35/html-S-3695-35/pdollsmg.html

Moreover, hard-coding file identification units is extremely error prone. Instead of using larger integers, I recommend avoiding them altogether; the code below uses the newunit specifier to read and write arrays into unformatted *.dat files.

program main

  use, intrinsic :: ISO_Fortran_env, only: &
       stdout => OUTPUT_UNIT, &
       compiler_version, &
       compiler_options

  ! Explicit typing only
  implicit none

  ! Variables
  integer, parameter :: NX=2, NY=3
  real, pointer      :: write_ptr(:,:) => null()
  real, allocatable  :: read_alloc(:,:)
  ! Your original post did not have valid array extends
  real, target, dimension(-NX:NX, -NY:NY) :: f, g

  ! Write data to binary files
  write_ptr => f
  call write_grid_to_file(write_ptr, './file1.dat')
  nullify(write_ptr)

  write_ptr => g
  call write_grid_to_file(write_ptr, './file2.dat')
  nullify(write_ptr)

  ! Allocate memory to read from files
  allocate( read_alloc, mold=f)

  call read_grid_from_file(read_alloc, './file1.dat')

  print *, 'original f array'
  print *, f
  print *, 'read from *.dat file'
  print *, read_alloc

  write( stdout, '(/4a/)') &
       ' This file was compiled using ', compiler_version(), &
       ' using the options ', compiler_options()

contains

  subroutine write_grid_to_file(grid, file_name)
    ! Dummy arguments
    real, pointer,    intent (in) :: grid(:,:)
    character(len=*), intent(in)  :: file_name
    ! Local variable
    integer :: file_id_unit

    ! Write grid to file
    open( newunit=file_id_unit, file=file_name, &
        status='replace', form='unformatted', &
        action='write', access='stream')
    write( file_id_unit ) grid
    close( file_id_unit )

  end subroutine write_grid_to_file

  subroutine read_grid_from_file(grid, file_name)
    ! Dummy arguments
    real,             intent (out) :: grid(:,:)
    character(len=*), intent(in)   :: file_name
    ! Local variable
    integer :: file_id_unit

    ! Write grid to file
    open( newunit=file_id_unit, file=file_name, &
        status='old', form='unformatted', &
        action='read', access='stream')
    read( file_id_unit ) grid
    close( file_id_unit )

  end subroutine read_grid_from_file

end program main

The command line prompt:

gfortran -Wall -o main.exe main.f90; ./main.exe

yields

 original f array
   0.00000000       0.00000000       0.00000000       0.00000000      -4.64743227E-27   4.59121429E-41   2.93318617E+14   4.56823299E-41  -4.64740762E-27   4.59121429E-41   1.02162904E+15   4.56823299E-41   0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000    
 read from *.dat file
   0.00000000       0.00000000       0.00000000       0.00000000      -4.64743227E-27   4.59121429E-41   2.93318617E+14   4.56823299E-41  -4.64740762E-27   4.59121429E-41   1.02162904E+15   4.56823299E-41   0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000       0.00000000    

 This file was compiled using GCC version 6.2.0 20161010 using the options -mtune=generic -march=x86-64 -Wall
Community
  • 1
  • 1
jlokimlin
  • 593
  • 4
  • 9
  • Ups, I wouldn't really use pointer just for this purpose, I think that is a misuse that will confuse future readers. You don't even need to preserve the lower bounds if you do not work with the indexes directly. Often you just loop from 1 to size(a) and don't care how it is declare in some different scope. And once your array is not target in the parent scope you can't use this. Not only that, you also cannot use the protection of `intent(in)` because you are allowed to modify the value of a target. – Vladimir F Героям слава Oct 25 '16 at 21:51
  • 1
    One of OP's questions was how to write an array to a text file row-by-row. This 'solution' seems like a convoluted way to write an array to a text file in one line. I'm struggling to see the point of this answer. – High Performance Mark Oct 25 '16 at 22:05
  • I do not understand this answer whatsoever. Thanks a lot for your help though. – Jeff Faraci Oct 25 '16 at 22:13
  • 3
    *I do not understand this answer whatsoever* and yet, you accepted it ! – High Performance Mark Oct 25 '16 at 22:28
  • @VladimirF Thanks for catching the `intent` attribute gotcha. I can take down the answer or apply extensive revisions if you deem it unappropriate. – jlokimlin Oct 25 '16 at 22:49