Fortran: Clearing defined array when leaving subroutine - arrays

I am doing a sensitivity analysis and thus need to run my program many times with different parameters.
I do have a main program which calls the subroutine that calculates the values I need. Thus I need to call the subroutine many times.
The subroutine has some arrays defined as:
real, dimension(1000) :: array_1, array_2
After leaving the array the program does not free the array, thus for two calls of the function I need to write:
real, dimension(2000) :: array_1, array_2
Is there an easy solution for this ?
Thank you

Let me restate your problem, just to make sure I understand it correctly; please correct me if I'm wrong.
You have a subroutine that calculates 1000 values in two separate arrays: array_1 and array_2.
From your main program you would like to call this subroutine many times, each time generating a unique sets of 1000 values.
Let's say you want to call your subroutine N times, do you want to compare the N sets of numbers out side of the subroutine, ie in your main program, or do you want to do the comparison inside the subroutine?
Either way, I declare the arrays like so:
real, dimension(N,1000) :: array_1, array_2
If you would like to do the comparison outside the subroutine, you will make the above declaration in the main program. Then you will call the subroutine N times, and after each i'th time copy the values into the main's array_1(i,*) and array_2(i,*). In this, the subroutine will only have the arrays defined like so:
real, dimension(1000) :: array_1, array_2
Each time the subroutine is called, it will reuse these arrays, overwriting the previous values. This should be OK if you recorded the previous values in the main's arrays.
If you are doing this in f90, you can dynamically allocate array_1 and array_2 and let N be variable length. Alternatively, you can allocate the arrays initially with enough space to store all possible calls to the subroutine. Lets say you are not going to run more than 100 calls to the subroutine:
real array_1(100,1000), array_2(100,1000)
I'm sorry if this is not what you are looking for, but please clarify if I miss understood your question.

If you have many arrays with essentially the same data, naming them array, array_1, array_2, etc. will soon become awkward, as you have realized. Two other possible designs are: 1) use one array and process the data in a loop. After one group of data is processed, read in the next group and overwrite the previous data in the same array. 2) Use a two-dimensional array as suggested by #Yann, with the second dimension being the number of datasets / different sets of parameters values.
If the arrays of each dataset have different lengths, for solution 1 you could declare the array "allocatable" and allocate it to the correct length at the start of each iteration, then deallocate at the end of each iteration. For 2 it would probably be easiest (but not most memory efficient) to make the length the maximum length and have an auxiliary array specifying the used length.
P.S. Are these arrays local to the subroutine? Then if you are passing the length to the subroutine as an argument, e.g., "len", you can declare the arrays as:
integer, intent (in) :: len
real, dimension(len) :: array_1, array_2
and the arrays will be recreated when the subroutine is re-invoked with the particular length "len". Inside a procedure (subroutine or function), and if the length is passed as an argument, you can get a variable length array very simply. You can even obtain the length using the size function applied to an array argument! In the main program, or it the length is not known as a constant or argument to a procedure, then you can defer the size of the array by giving it the "allocatable" attribute in the declaration and dynamically allocate the array with the "allocate" statement.

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.

Passing subarray of 2-dimensional array in FORTRAN-77

I have 2-dimensional array
real triangle(0:2, 0:1)
where "triangle" is an array of vectors (1-dim arrays)
also i have subroutine
subroutine vecSub(lhs, rhs, result)
real lhs(0:1), rhs(0:1), result(0:1)
result(0) = lhs(0) - rhs(0)
result(1) = lhs(1) - rhs(1)
return
end
is there any way to pass one of the vectors from "triangle" variable to this subroutine? Fortran-90 can do this: triangle(0, :) which gives first array of triangle, but i'm allowed to use only FORTRAN-77, so this won't do, any suggestions?
#Javier Martin wrote "not with the current layout of your array", but missed the opportunity to suggest an alternative.
If instead you declared the variable as follows:
real triangle(0:1, 0:2)
reversing the order of the bounds, you could then pass triangle(0,0), triangle(0,1) or triangle(0,2) to the subroutine and get exactly the behavior you want, due to a Fortran feature called "sequence association". When you pass a single array element to a dummy argument that is an array, you are implicitly passing that and following elements, in array element order. This is about the only allowed violation of the normal Fortran shape-matching rules, and was part of FORTRAN 77.
No, not with the current layout of your array, because of two reasons:
Fortran uses an array element order in which the leftmost dimension is contiguous. That is, in an array of size (n,m,l) the distance between elements (the stride) is (1,n,m), measured in units of array elements (that is, not bytes).
F77 does not include assumed-shape arrays a(:) which are generally implemented by passing a small descriptor structure that communicates details like the stride or the number of elements. Instead, you can only use assumed-length arrays a(*) which are normally a pointer to the first element, kind of like C arrays. You have to pass the length as a separate argument, and array elements have to be contiguous
This is the reason why you can "pass a subarray" to an F77 subroutine, as long as that subarray is e.g. a matrix column: elements therein are contiguous.
A possible solution (one that many current Fortran compilers implement) is that when you try to pass a non-contiguous subarray to a function that is not known to accept them, they make a copy of the array, and even write it back in memory if required. This would be equivalent to:
! Actual array
integer m(3,5)
integer dummy(5)
dummy = m(2,:)
call myF77sub(dummy, 5)
m(2,:) = dummy
However, as others are saying, you should try not to call F77 functions directly, but either adapt them to or at least wrap them in more recent Fortran interfaces. Then you can have code like the above in the wrapper, and call that wrapper "normally" from modern Fortran routines. Then you may eventually get around to rewriting the actual implementation in modern Fortran without affecting client code.

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.

How to concatenate two arrays in Fortran 90

I have an original array called pres_lev3d, whose size is defined by pres_lev3d(im*jm, levsi), where im*jm is 72960 and levsi is 64. This corresponds to global atmospheric data, thus the size. The array is allocatable: real (kind=kind_io8), allocatable :: pres_lev3d(:, :). I have a second array, press_1d, whose size is also defined in a similar fashion pres_1d(im*jm, levsi), but in this array levsi is 1.
I need to concatenate both arrays (technically a 2d and 1d array) to an array of the shape (/72960, 65/). In MATLAB this seems like a very simple process, however, I can't seem to find an easy way to go around it in Fortran 90.
I have tried to create a third array
pres_lev=(/pres_lev3d, pres_1d/)
and also tried to use merge, but none of these approaches seem to work out.
I am fairly new to Fortran.
If I've followed your explanation correctly this would probably work
real(kind_io8), dimension(72960,65) :: out_array
...
out_array(:,1:64) = pres_lev3d
out_array(:,65) = pres_1d
If that's not easy enough, or if I've misunderstood your question, explain further. To allocate out_array to conform to your input arrays, try something like
real(kind_io8), dimension(:,:), allocatable :: out_array
...
allocate(out_array(size(pres_lev3d,1),size(pres_lev3d,2)+1))
...
out_array(:,1:64) = pres_lev3d
out_array(:,65) = pres_1d

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.

Resources