arraysfortranreadfilegfortranallocatable-array

Allocatable array '' at (1) must have a deferred shape or assumed rank, and Syntax error in READ statement at (1) errors


I am trying to read an ASCII file and am getting errors when compiling such as:

Error: Syntax error in READ statement at (1)

And

Error: Allocatable array 'pos' at (1) must have a deferred shape or assumed rank

My code:

subroutine read_file(pos,mass,rho,vel,n)
integer :: i, n
real, allocatable, intent(out) :: pos(3,n), mass(n), rho(n), vel(3,n)

open(unit=11,file="star.ascii",status="old",action="read") 

n = 0

do
  read(unit=11,*)
  n = n+1
enddo

allocate(pos(3,n), mass(n), rho(n), vel(3,n))

do i = 1,n
  read(unit=11,*) pos(:,i), mass(i), rho(i), vel(:,i)
enddo
close(unit=11)

end subroutine read_file

The first 8 columns in my ascii file are the x, y, z components of position, mass, density, and the x, y, z components of velocity which I am reading into arrays, with (1,n), (2,n), (3,n) being the x, y, and z components correspondingly and n is supposed to be the number of particles.

What am I doing wrong and how can I make this code compile?

Update: first error solved, but still getting the same syntax error with the READ statement.

subroutine read_file(pos,mass,rho,vel,n) 
integer :: i, n, ios
real, allocatable, intent(out) :: pos(:,:),mass(:),rho(:),vel(:,:)

open(unit=11,file="star.ascii",status="old",action="read")

n = 0
do
  read(unit=11,*,iostat=ios) pos,mass,rho,vel 
  if (ios /= 0) exit
  n = n+1
enddo

allocate(pos(3,n), mass(n), rho(n), vel(3,n))  
rewind(11)

do i = 1,n
  read(unit=11,*)pos(:,i),mass(i),rho(i),vel(:,i) 
enddo
close(unit=11)

end subroutine read_file

Solution

  • The read syntax error comes from the fact that you use a named dummy argument (unit=11) but then you do not continue to use named dummy arguments.

    Say you have a subroutine with a this interface:

    subroutine mysub(a, b, c)
        implicit none
        integer, intent(in), optional :: a, b, c
    end subroutine mysub
    

    There are two ways to call such a subroutine:

    1. Without named dummy arguments:

      call mysub(1, 2, 3)   ! a->1, b->2, c->3
      call mysub(4711, 2)   ! a->4711, b->2, c unset
      
    2. Using so-called keyword arguments:

      call mysub(a=4, c=2)  ! b unset
      call mysub(b=14)      ! a and c unset
      

    In case 1. the compiler interprets the inputs according to the order in which the arguments are defined in the interface. In case 2. it sets them by the keyword argument. To some extend, you can mix a bit:

    call mysub(1, c=5)   ! b unset
    

    But, and this is important, after the first keyword argument, you can't go back:

    call mysub(b=4, 2)   ! DOES NOT WORK
    

    The compiler doesn't know whether a or c is supposed to be 2 in this case. You might say, "What if I use mysub(a=1, 2, 3), that's clear, isn't it?" It might be, but in order to avert the pitfalls, the Fortran Guidelines state that

    ... once a keyword argument appears in the list, all of the remaining arguments must also be keyword arguments1

    The interface for read has the unit as the first, and the fmt as the second parameter. So you can either one of these:

    read(unit=11, fmt=*, iostat=ios) ...
    read(11, *, iostat=ios) ...
    

    But you can't use unit= without also declaring fmt=

    If you declare an allocatable array, you need to tell it the number of dimensions you want to reserve for allocation. This is not done with n, but with the colon ::

    real, allocatable :: pos(:, :), mass(:), rho(:), vel(:, :)
    

    1 from Chapman: Fortran 95/2003 For Scientists and Engineers