Assumed size arrays in fortran [duplicate] - arrays

I am writing code to add on a closed-source Finite-Element Framework that forces me (due to relying on some old F77 style approaches) in one place to rely on assumed-size arrays.
Is it possible to write an assumed-size array to the standard output, whatever its size may be?
This is not working:
module fun
implicit none
contains
subroutine writer(a)
integer, dimension(*), intent(in) :: a
write(*,*) a
end subroutine writer
end module fun
program test
use fun
implicit none
integer, dimension(2) :: a
a(1) = 1
a(2) = 2
call writer(a)
end program test
With the Intel Fortran compiler throwing
error #6364: The upper bound shall not be omitted in the last dimension of a reference to an assumed size array.

The compiler does not know how large an assumed-size array is. It has only the address of the first element. You are responsible to tell how large it is.
write(*,*) a(1:n)
Equivalently you can use an explicit-size array
integer, intent(in) :: a(n)
and then you can do
write(*,*) a

An assumed-size array may not occur as a whole array reference when that reference requires the shape of the array. As an output item in a write statement that is one such disallowed case.
So, in that sense the answer is: no, it is not possible to have the write statement as you have it.
From an assumed-size array, array sections and array elements may appear:
write (*,*) a(1:2)
write (*,*) a(1), a(2)
write (*,*) (a(i), i=1,2)
leading simply to how to get the value 2 into the subroutine; at other times it may be 7 required. Let's call it n.
Naturally, changing the subroutine is tempting:
subroutine writer (a,n)
integer n
integer a(n) ! or still a(*)
end subroutine
or even
subroutine writer (a)
integer a(:)
end subroutine
One often hasn't a choice, alas, in particular when associating a procedure with a dummy procedure with a specific interface . However, n can get into the subroutine through any of several other ways: as a module or host entity, or through a common block (avoid this one if possible). These methods do not require modifying the interface of the procedure. For example:
subroutine writer(a)
use aux_params, only : n
integer, dimension(*), intent(in) :: a
write(*,*) a(1:n)
end subroutine writer
or we could have n as an entity in the module fun and have it accesible in writer through host association. In either case, setting this n's value in the main program before writer is executed will be necessary.

Related

Is there any reason to pass array by first item?

In a rather old style Fortran project, I often see this pattern, when an array is passed by its first item:
program test
implicit none
integer :: a(10)
a(:) = 1
call sub(a(1), 10) ! here
contains
subroutine sub(a, length)
integer, intent(in) :: length
integer, intent(in) :: a(length)
print *, a
end subroutine
end program
where it could be:
call sub(a, 10) ! here
which is valid even in Fortran 77.
Note the size of the array has to be passed and used explicitly, this will not work for assumed shape array:
subroutine sub(a)
integer, intent(in) :: a(:)
print *, a
end subroutine
For me this is confusing, as the call suggests a scalar is passed to the subroutine. I suppose it works because the array is passed by reference.
Is there any reason to pass arrays like this, especially now?
One uses this feature when using the older interface to the non-blocking MPI routines.
Say you want to pass subarray A(10,10:19) which is a part of a bigger array A(1:100,1:100).
If you pass
use mpi
call ISend(A(10,10:19), 10, MPI_REAL, ...
you pass a temporary copy of array A and the address of the temporary copy will not be valid at the time of the MPI_Wait. Therefore, instead, you create an MPI derived type, that describes the offsets in the array to be sent and you use it as
use mpi
call ISend(A(10,10), 1, derived_type, ...
Of course, with the most modern MPI libraries and compilers you use use mpi_f08. However, most HPC codes in the wild do not use it yet.
Another solution is to use an MPI derived type that includes the absolute address of the subarray and just pass A. Sometimes it is practical, sometimes it is not. It depends on how much the subarrays passed vary throughout the code.
Be aware that there are other issues present in non-blocking MPI in the old interface and it helps if you explicitly mark the routines as ASYNCHRONOUS.
Consider the following example:
implicit none
integer a(2,2)
a = RESHAPE([1,2,3,4],[2,2])
call sub(a(2,1))
print '(2I3)', TRANSPOSE(a)
contains
subroutine sub(b)
integer, intent(out) :: b(2)
b = -1
end subroutine sub
end
Here, the element sequence represented by the actual argument a is a(2,1), a(1,2), a(2,2), and the first two are associated with the dummy argument b.
For arrays of rank greater than one, this element sequence use may make things (much) easier to specify certain consecutive elements of the actual argument. For a rank-1 array we can write a(3:) instead of a(3), say, as the actual argument. For higher rank arrays we haven't that option.
I won't express an opinion on whether this is a good use this feature.

Fortran: Array of arbitrary dimension?

If I want to create an allocatable multidimensional array, I can say:
program test
real, dimension(:,:), allocatable :: x
integer :: i,j
allocate(x(5, 5))
do i = 1,size(x,1)
do j = 1,size(x,2)
x(i,j) = i*j
end do
end do
write(*,*) x
end program test
However, what if I don't know how many dimension x will be. Is there a way to accommodate that?
Newer compilers allow the use of assumed-rank objects for interoperability.
I think that is what you are looking for. But this is for call to functions or subroutines. The function or subroutine declares the dummy argument as assumed-rank and the actual rank is passed with the actual argument at runtime.
Example from IBM website:
REAL :: a0
REAL :: a1(10)
REAL :: a2(10, 20)
REAL, POINTER :: a3(:,:,:)
CALL sub1(a0)
CALL sub1(a1)
CALL sub1(a2)
CALL sub1(a3)
CONTAINS
SUBROUTINE sub1(a)
REAL :: a(..)
PRINT *, RANK(a)
END
END
follow this or that for more details
It looks to me like you're trying to carry out stencil computations across an array of rank-1, -2 or -3 -- this isn't quite the same as needing arrays of arbitrary rank. And assumed-rank arrays are really only applicable when passing an array argument to a routine, there's no mechanism even in the forthcoming standard for declaring an array to have a rank determined at run-time.
If you're impatient to get on with your code and your compiler doesn't yet implement TS 29113:2012 perhaps the following approach will appeal to you.
real, dimension(:,:,:), allocatable :: voltage_field
if (nd == 1) allocate(voltage_field(nx,1,1))
if (nd == 2) allocate(voltage_field(nx,ny,1))
if (nd == 3) allocate(voltage_field(nx,ny,nz))
Your current approach faces the problem of not knowing, in advance of knowing the number of dimensions in the field, how many nearest-neighbours to consider in the stencil, so you might find yourself writing 3 versions of each stencil update. If you simply abuse a rank-3 array of size nx*1*1 to represent a 1D problem (mutatis mutandis a 2D problem) you always have 3 sets of nearest-neighbours in each stencil calculation. It's just that in the flattened dimensions the nearest neighbour is, well, either a ghost cell containing a boundary value, or the cell itself if your space wraps round.
Writing your code to work always in 3 dimensions but to make no assumptions about the extent of at least two of them will, I think, be easier than writing rank-sensitive code. But I haven't given the matter a lot of thought and I haven't really thought too much about its impact on your f-d scheme.

Write array to file by columns

Is there a way to write columns to a file one by one? For example, I would like to write:
write(1,*) 1
write(1,*) 2
And then write (possibly in another subroutine)
write(1,*) 3
write(1,*) 4
in such a way that will produce an output file in the format:
1 3
2 4
without combining the arrays (e.g.)
write(1,*) 1,3
write(1,*) 2,4
I'm thinking that there may be a way to move the "pointer" (of file location) back to the beginning and add spaces or something, but I really have no idea if this is possible. Any help is greatly appreciated!
So far, this is my attempt to make a subroutine to make this work:
subroutine writeArrayToNthColumn(arr,u,n)
implicit none
real(dpn),dimension(:),intent(in),target :: arr
real(dpn),dimension(:),pointer :: parr
integer,intent(in) :: u,n
integer :: i,s
s = size(arr)
allocate(parr(s))
parr = arr
rewind(u)
if (n.eq.1) then
do i=1,s
write(u,'(1F20.10)') parr(i)
enddo
else
do i=1,s
write(u,'(1F40.10)') parr(i)
enddo
endif
end subroutine
But upon calling the subroutine a second time, the first column is deleted..
The comments above from George, and High Performance Mark are exactly right. I'm posting this as an answer, because it has source code, but it should be considered an extended comment, whose only purpose is to demonstrate how right they are.
You can do this, in a sense, although under no circumstances should you do so. Moving around within a file - a so called seek - is an incredibly expensive operation. On a local hard drive, this can take 10-20 milliseconds or so per operation (eg, in this case, per entry); if you are using a large shared system it can be much larger than that.
Moving things around in memory, however, is much less expensive. A well-tuned transpose function will do most of the moving around in cache, with operation that take a few nanoseconds; the data will get batched into and from main memory in fewer operations which each take ~100ns or so. Even a seek on a modern SSD will take something like dozens of microseconds.
In other words, there is no situation in which doing the transpose in your file I/O will be less than hundreds of times slower than doing the transpose in memory (and then back if needed). You still have to do the file/IO in the end, but if you are writing out in one batch, this is much faster.
So let's try some examples in Fortran, since that's the language of this question. Fortran, sensibly, makes this somewhat harder by making it difficult to access file locations directly, and even to explicitly output in carriage returns; I've used direct-access IO here and hard-coded in a non-portable way of doing the carriage returns.
Here's doing the transpose in your I/O:
program badiotxt
implicit none
integer, parameter :: asize = 200
integer, dimension(asize, asize) :: a
integer :: i, j
integer :: record
forall (i=1:asize, j=1:asize)
a(i,j) = (i-1)*asize+j
end forall
open(unit=7,file="bad.txt", status="new", access="direct", &
form="formatted", action="write", recl=10)
do j=1,asize
do i=1,asize-1
record=(j-1)*asize+i
write(7, rec=record, fmt="(2X,I7,1X)") a(i,j)
enddo
enddo
i = asize
do j=1,asize
record=(j-1)*asize+i
write(7, rec=record, fmt="(1X,I7,A1,A1)") a(i,j), char(13), char(10)
enddo
close(7)
end program badiotxt
and here's one done by transposing the array:
program goodiotxt
implicit none
integer, parameter :: asize = 200
integer, dimension(asize, asize) :: a
integer :: i, j
integer :: record
forall (i=1:asize, j=1:asize)
a(i,j) = (i-1)*asize+j
end forall
open(unit=7,file="good.txt", status="new", &
form="formatted", action="write")
a = transpose(a)
do i=1,asize
write(7,fmt="(1X,200(2X,I7))") (a(i,j), j=1,asize)
enddo
a = transpose(a)
close(7)
end program goodiotxt
Note that the good version is cleaner code, and that these are not large arrays by any stretch. The resulting times are:
Transpose in memory: 0.05s
Transpose on disk: 1.70s
Slowdown: 34x
I've a bit more time now than when I first commented on this question. This is, however, an extended comment rather than a full-blown answer.
Even IF (that's a big if) I had to write a data file column-by-column I wouldn't use direct access IO as #Jonathan Dursi's answer proposes. That (direct access IO) is for situations where it is difficult to predict, at the time the code is written, the ordering of writes to a file. In OP's case the ordering of writes seems to be entirely predictable.
I would:
Write column 1, element-by-element, to a file.
2.1 Open a second file for writing, and the first file for reading.
2.2 Read the first line from the first file, write it to the second file and append the first element of the next column.
2.3 Repeat 2.2 until I got to the end of the column/file.
2.4 Close the second file (which now contains columns 1 and 2) and the first file.
3.1 Open the second file for reading, and perform a destructive open on the first file for writing.
... by now you should have got the picture.

purpose to using allocatable without a deferred size or vice-versa

I'm in the process of learning fortran (90), with past experience in c and python. I'm reading about arrays (it's interesting to see that so much of the python array behavior is based on that of fortran); and I was wondering, is there ever a reason to assign an allocatable array without a deferred size? And is it possible to defer the size without using allocatable -- if so, how?
e.g.
REAL, DIMENSION(:) :: arr1
REAL, ALLOCATABLE, DIMENSION(20) :: arr2
Array terminology can be a bit daunting in Fortran. The first thing to realize is the difference between an actual argument, a variable for which a procedure has to allocate some memory, and a dummy argument, placeholders for actual arguments passed along by the calling procedure. If a variable is in a procedure's argument list, it is a dummy argument.
So, as for actual arguments, there are two kinds of arrays:
explicit-shape, e.g. <type> :: A(3,6)
deferred-shape, e.g. <type>, <allocatable|pointer> :: A(:,:,:)
A deferred shape must have an allocatable or pointer attribute.
When it comes to dummy arguments, there are also two kinds of arrays:
assumed-size, e.g. <type> :: A(4,5,*), B(1:2,4:*), C(m,n)
assumed-shape, e.g. <type> :: A(:,4:)
The actual and dummy arguments are not related in any way, so don't mix them. Also note that there exist something called automatic arrays, these look exactly like assumed-size arrays with dummy variables as sizes (C(m,n)), but don't appear in the argument list, so they are not dummy arguments.
For the assumed-size array, the last dimension's upper bound should be left unspecified (the *), other than that lower+upper bounds can be specified, including variables passed along to the procedure. Think of it as an array for which you re-specify the entire layout, irrespective of the actual argument. This allows you to do things like:
program toby
integer, parameter :: n = 10
real :: a(n**3)
call my_sub(a,n)
end program
subroutine my_sub(a,n)
integer, intent(in) :: n
real, intent(inout) :: a(n,n,*)
...
end subroutine
The other dummy argument, the assumed-shape, only allows you to specify lower bounds, as it gets its size info from the actual argument. This means you also can't redefine the dimensionality, and you need an explicit interface (e.g. via a module). This makes it more stringent, and also unnecessary to pass along size information with the array.
program toby
integer, parameter :: n = 10
real :: a(n,n,n)
call my_sub(a)
contains
subroutine my_sub(a)
real, intent(inout) :: a(:,:,:)
integer :: n
n = size(a,1)
...
end subroutine
end program
You can read about it in much more detail here I find it to be an ideal reference.
Last but not least, since you mention python, don't be to eager to apply python-like array slicing in Fortran, as it can cause the use of temporary arrays, which can slow down the program. You can use -fcheck=array-temps with gfortran to warn for that. Furthermore, even though elemental operations on whole arrays are recommended for efficiency (e.g. A=A+1 in using arrays efficiently), don't misinterpret that as "writing very concise code is good for efficiency". The latter is of course not (necessarily) true.

fortran, passing allocatable arrays to a subroutine with right bounds

I need in a program to pass some allocatable arrays to subroutines, and i need to know if the way I do it are in the standard or not.
If you know where I can search for the standard of fortran, Tell me please.
Here is a little code that will better explain than words
program test
use modt99
implicit none
real(pr), dimension(:), allocatable :: vx
allocate(vx(-1:6))
vx=(/666,214,558,332,-521,-999,120,55/)
call test3(vx,vx,vx)
deallocate(vx)
end program test
with the module modt99
module modt99
contains
subroutine test3(v1,v2,v3)
real(pr), dimension(:), intent(in) :: v1
real(pr), dimension(0:), intent(in) :: v2
real(pr), dimension(:), allocatable, intent(in) :: v3
print*,'================================'
print*,v1(1:3)
print*,'================================'
print*,v2(1:3)
print*,'================================'
print*,v3(1:3)
print*,'================================'
end subroutine test3
end module modt99
on screen, I get
================================
666.000000000000 214.000000000000 558.000000000000
================================
214.000000000000 558.000000000000 332.000000000000
================================
558.000000000000 332.000000000000 -521.000000000000
================================
So are the three ways of dummy arguments in subroutine test3 legal (in what version of fortran, 90, 95, 2003?) and are their behavior normal?
The first version passes the array slice to the subroutine. Note that boundary information are not passed along in this way, arrays are assumed to start at 1 and go to size(array).
The second way is just like the first one, but you manually set the lower boundary to 0, that's why printing v3(1:3) gives you the values with an offset of 1.
The third way passes all array information to the subroutine (including boundaries), hence the "correct" indexing. Passing allocatable arrays was introduced with Fortran 2003.
Apart from the fact that you have an aliasing issue (passing the same variable to three different dummy arguments), all three versions are legal.
You can find all documents of the standards here.
Especially, take a look at the Fortran 2003 Standard, Ch. 5.1.2.5 DIMENSION attribute to see the differences between assumed shape and deferred shape arrays in dummy arguments.

Resources