keeping array limits in fortran during subroutine call - arrays

I have the following program
module test
contains
subroutine foo()
integer, allocatable :: a(:)
allocate(a(-5:5))
call bar(a)
print *, a
end subroutine
subroutine bar(a)
integer, intent(out) :: a(:)
a = 0
a(-4) = 3 ! here
a(2) = 3
end subroutine
end module
program x
use test
call foo()
end program
In the line marked with "here" I am doing something wrong. The fact is that when I receive the array a (in the caller allocated from -5 to +5), the callee uses conventional numbering (1 to n), meaning that assigning -4 I am doing an out of boundary assignment. How can I instruct the compiler that, within the bar routine, the numbering of the a array must be the same as in the caller ?

The type of dummy argument that you are are using in the subroutine, with the dimension specified with a colon, is called "assumed shape". This name is the clue -- Fortran passes only the shape and not the lower and upper bounds. The lower bound is assumed to be one unless you override it as shown in the answer by kemiisto. If the lower bound is not fixed, you can pass an argument to use as the lower bound.
Later addition: a code example if the lower dimension isn't known at compile time:
subroutine example (low, array)
integer, intent (in) :: low
real, dimension (low:), intent (out) :: array

There are two common options:
As kemisto wrote, you pass a second argument. This was common in F77-style code. You can not use the LBOUND trick! It has to be passed as an integer.
You declare the argument to be a pointer, which includes the entire array descriptor. Then the bounds of the array in the subroutine are the same as in the calling scope. Of course you may lose on optimization this way.

How can I instruct the compiler that, within the bar routine, the numbering of the a array must be the same as in the caller ?
Not sure but according to the standard you can specify the lower bound for an assumed-shape array.
subroutine bar(a)
integer, intent(out) :: a(-5:)

Related

Fortran cast 2D Array to 1D array and pass it

I am working with Fortran 2008 with the Intel 17 Compiler.
I am getting a 3D (x,y,z) assumed shape array in a subroutine and want to pass xy slices as a 1D array to another function that multiplies it with a matrix:
SUBROUTINE xy_gyro_average_3d(this,infield,barfield)
class(xy_GyroAverager_t) :: this
REAL,DIMENSION(:,:,:), INTENT(IN),contiguous,TARGET :: infield
REAL,DIMENSION(:,:,:,:,:), INTENT(OUT),contiguous,TARGET :: barfield
INTEGER :: n,m,k,li0,lj0
type(Error_t) :: err
REAL,DIMENSION(:),POINTER :: inptr,outptr
li0=SIZE(infield,1)
lj0=SIZE(infield,2)
DO n=1,this%ln0
DO m=1,this%lm0
DO k=1,this%lk0
inptr(1:li0*lj0) => infield(:,:,k)
outptr(1:li0*lj0) => barfield(:,:,k,m,n)
CALL this%gyromatrix(k,m,n)%multiply_with(invec=inptr,&
& outvec=outptr,err=err)
if (err%err) call write_err(err)
END DO ! k
END DO ! m
END DO ! n
END SUBROUTINE xy_gyro_average_3d
The multiply_with routine looks (simplified) like this:
SUBROUTINE multiply_with(this,invec,outvec,err)
CLASS(Matrix_t) :: this
real, dimension(1:), intent(IN) :: invec
real, dimension(1:),intent(OUT) :: outvec
CLASS(Error_t),INTENT(OUT) :: err
CALL this%multiply_with__do(invec,outvec,err)
END SUBROUTINE multiply_with
I am getting a warning about the pointer allocation and then an error about the types of actual and dummy argument not matching:
src/GyroAverager.F90(294): warning #8589: Fortran 2008 specifies that if a bound remapping list is specified, data target must be of rank one. [INFIELD]
inptr(1:SIZE(infield(:,:,k))) => infield(:,:,k)
----------------------------------------------^
src/GyroAverager.F90(295): warning #8589: Fortran 2008 specifies that if a bound remapping list is specified, data target must be of rank one. [BARFIELD]
outptr(1:li0*lj0) => barfield(:,:,k,m,n)
----------------------------------^
src/GyroAverager.F90(297): error #6633: The type of the actual argument differs from the type of the dummy argument. [INPTR]
CALL this%gyromatrix(k,m,n)%multiply_with(invec=inptr,&
-------------------------------------------------------------^
src/GyroAverager.F90(298): error #6633: The type of the actual argument differs from the type of the dummy argument. [OUTPTR]
& outvec=outptr,err=err)
Now I think the warning should not be a problem as long as the arrays infield and barfield are marked as contiguous (is there any way to do this to get rid of the warning messages though?).
What exactly is the problem with the error message? The pointers inptr and outptr have the same dimension as the dummy arguments invec and outvec.
But I read that if you pass a pointer but do not receive it as a pointer the array it is pointing to is passed instead. So in the end is it still passed as a 2D array instead of a 1D or is the problem somewhere else?
It was working before when the header of the multiply_with routine looked like this:
SUBROUTINE multiply_with(this,invec,outvec,err)
CLASS(Matrix_t) :: this
class(*), dimension(1:),intent(IN) :: invec
class(*), dimension(1:),intent(OUT) :: outvec
CLASS(Error_t),INTENT(OUT) :: err
and then the routines called by multiply_with would get the type via a select type later. But I reduced the code as we are always working with real arrays.
What is the correct way to do this?

Setting array size from command line arguments

I know that in Fortran I can declare an array of a number N of elements if N is declared as a parameter before (and so defined to some value).
On the other hand I know I can get a program to accept arguments from the command line with the use of the subroutine get_command_arg.
My question is:
Can I somehow declare an array of a number of elements given by a command when calling the program from the command line?
I'm looking for something like the command line:
./main -30
where main.f03 would begin with something like:
integer, parameter :: N = get_command_arg(1)
real :: x(N) ...
I'm trying not to define the arrays as allocatable.
There are certain languages where you can initialize variables and named constants from command line (like chapel), but Fortran is not one of them.
You say "I am trying not to define the arrays as allocatable." but that is the problem. You simply have to. There is no other way.
Your code
integer, parameter :: N = get_command_arg(1)
real :: x(N) ...
is illegal for several reasons.
You cannot put GET_COMMAND_ARGUMENT() into a constant expression because it is not among allowed functions. It does not return compile-time constant values. And parameter initializers must be set at the compile time.
GET_COMMAND_ARGUMENT() is a subroutine, not a function. It can return more stuff, not just the value, but also the length and status. It is not pure and the Fortran standard is trying to use only pure functions. Other things, like RANDOM_NUMBER(), are subroutines. It is a good style to follow in your own programs too.
The only way in Fortran to create arrays that change from run to run is to make the array allocatable or pointer. There are also automatic arrays for local arrays.
You can do it this way without allocatables. You just have to pass the size (after converting it to an integer) into a subroutine. But really, I see no reason not to use allocatables for something like this.
program main
implicit none
integer :: n,arg_len,istat
character(len=100) :: arg
call get_command_argument(1,value=arg,status=istat)
if (istat/=0) error stop 'error: cannot read first arg'
read(arg,'(I100)',iostat=istat) n
if (istat/=0) error stop 'error: first arg not an integer'
call real_main(n)
contains
subroutine real_main(n)
integer,intent(in) :: n
integer,dimension(n) :: ival
ival = 1
write(*,*) ival
end subroutine real_main
end program main
Example use:
> ./main 1
1
> ./main 2
1 1
> ./main 3
1 1 1

Use variable name for argument-derived bounds of local arrays

With fortran, I am running in situations where I have multiple local variables whose size is derived from input parameters in a somewhat verbose manner, e.g.
program pbounds
contains
subroutine sbounds(x)
integer,intent(in) :: x(:,:)
integer y1(size(x,1)/3,size(x,2)/2)
integer y2(size(x,1)/3,size(x,2)/2)
integer y3(size(x,1)/3,size(x,2)/2)
! ... do some stuff
end subroutine sbounds
end program pbounds
This seems overly verbose as I keep repeating the size expression. Additionally, when a change is needed – e.g. when it turns out that I need a y4 and change size(x,1)/3 to size(x,1)/4 – for real-world code that doesn't look quite as neat, it is easy to miss one of the previous variables.
In my real code, other examples include declarations with sizes coming from verbose, repetitive composite type fields, such as
type(sometype), intent(in) :: obj
real :: arr1(obj%subfield%nmax, obj%subfield%nmax, obj%subfield%xmax, 3, 3)
real :: arr2(obj%subfield%nmax, obj%subfield%xmax)
...
Is it possible to define a name for the size expressions, without resorting to preprocessor macros or allocatable arrays?
The things I have tried
With allocatable variables, we can use a local variable
as name for the size expressions, but we split the declaration
of the local arrays over two lines each.
program pboundsx
contains
subroutine sboundsx(x)
integer,intent(in) :: x(:,:)
integer,allocatable :: y1(:,:),y2(:,:),y3(:,:)
integer s(2)
s = [ size(x,1)/3, size(x,2)/2 ]
allocate(y1(s(1),s(2)))
allocate(y2(s(1),s(2)))
allocate(y3(s(1),s(2)))
! ... do some stuff
end subroutine sboundsx
end program pboundsx
With preprocessor macros we can give the size expression a name,
but at the cost of adding multiple preprocessor lines, that
disturb the indentation pattern among other things.
program pboundsm
contains
subroutine sboundsm(x)
integer,intent(in) :: x(:,:)
#define s1 (size(x,1)/3)
#define s2 (size(x,2)/2)
integer y1(s1,s2)
integer y2(s1,s2)
integer y3(s1,s2)
#undef s1
#undef s2
! ... do some stuff
end subroutine sboundsm
end program pboundsm
With a second subroutine we can make the sizes an explicit
parameter, but this is probably the most verbose and obscure
solution; even more so considering that in real-world code 'x'
isn't the only parameter.
program pboundss
contains
subroutine sboundss(x)
integer, intent(in) :: x(:,:)
call sboundss2(x,size(x,1)/3,size(x,2)/2)
end subroutine sboundss
subroutine sboundss2(x,s1,s2)
integer, intent(in) :: x(:,:), s1, s2
integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
end subroutine sboundss2
! ... do stuff
end program pboundss
If it was allowed to mix declarations and initialization, the solution would be simple – but it is not:
program pboundsv
contains
subroutine sboundsv(x)
integer,intent(in) :: x(:,:)
integer s1 = size(x,1)/3, s2 = size(x,2)/3 ! INVALID DECLARATION
integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
! ... do stuff
end subroutine sboundsv
end program pboundsv
If the compiler allows (*), it may be an option to include the subroutine body entirely into a block (= a new scope) and mix declarations and assignment:
program pboundsv
contains
subroutine sboundsv(x)
integer,intent(in) :: x(:,:)
integer s1, s2
s1 = size(x,1)/3 ; s2 = size(x,2)/3
block
integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
! ... do stuff
endblock
endsubroutine
end program
(*) But, this is Fortran >95, and Oracle studio fortran 12.5 still cannot compile it (very sadly)... (gfortran and ifort seem OK).
A partial solution - while specification statements cannot depend on the value of a local variable(**), they can depend on previous specifications for other local variables. For example:
subroutine sbounds(x)
integer,intent(in) :: x(:,:)
integer y1(size(x,1)/3,size(x,2)/2)
integer y2(size(y1,1),size(y1,2))
integer y3(size(y1,1),size(y1,2))
! ... do some stuff
end subroutine sbounds
...
type(sometype), intent(in) :: obj
real :: arr1(obj%subfield%nmax, obj%subfield%nmax, obj%subfield%xmax, 3, 3)
real :: arr2(size(arr1,1), size(arr1,3))
In some cases this can make the logical structure of your declarations clearer - "the extent of this dimension of this variable is the same as the extent of this dimension of that variable", which might be a more relevant message to a reader of the code than the specific expression that calculates the extent.
** Note that it is various restrictions on specification and constant expressions that is the real issue with your last block of code. You can quite happily mix declarations with initializations and other declarations in Fortran (they are just specification statements), what you cannot do is mix specification statements with executable statements (block constructs and the like aside). Specification expressions cannot depend on the value of a local variable, in part because it otherwise becomes difficult to ensure a deterministic ordering, while constant expressions cannot depend on the value of any variable, because constant expressions are supposed to be constant (and able to be evaluated at compile time).

How to have a Function returning an array which starts at index 0

I have a function which returns an array:
PURE FUNCTION set_Xgrid() RESULT(x_grid)
REAL(dp), DIMENSION(:), ALLOCATABLE :: x_grid
INTEGER :: ii
ALLOCATE(x_grid(0:Nx-1))
DO ii=0,Nx-1
x_grid(ii) = x_o + ii*dx
END DO
END FUNCTION
I then just call this function like this:
REAL(dp), DIMENSION(:), ALLOCATABLE :: x
ALLOCATE(x(0:Nx-1))
x = set_Xgrid()
DO ii=0,Nx-1
PRINT '(I4,A10,F10.7)', ii, ", x(ii) = ", x(ii)
END DO
Output (first few lines):
0, x(ii) = 0.0000000
1, x(ii) = 0.0101010
2, x(ii) = 0.0202020
Even though the above code works as expected, I do not really understand how it works.
I thought the RESULT of a function was an implicit INTENT(OUT) and worked the same way: any value prior to the call of the function was ignored and the variable was automatically deallocated. If that is the case, the code somehow remembers that the variable x was initialized starting at index 0.
Is this the right way to have the returned array of a function start at index 0? Also, I'd like to understand what happens when a function returns an array.
There is not much point returning an array starting at a specific index. It is not important anyway:
x(3:5)
x = f()
will work for any function returning the array of the right size 3, even if it starts from 42 inside the function.
It definitely does not work as intent(out), allocatable argument. The values are just assigned to the right place of the array, counting from the array start, not using the actual indexes.
If the variable is not allocated at all prior the assignment
real, allocatable :: x(:)
x = f()
then it is allocated to start at 1 with the right size, again, where the result started inside the function does not matter.
Vladimir F's answer covers much of what you need to know. There is more detail to add, however.
It is important to repeat, first, that a function result is not at all like an intent(out) dummy variable. Except in cases that don't apply here the function result acts as an expression rather than a variable.
You ask
If that is the case, the code somehow remembers that the variable x was initialized starting at index 0.
This is a consequence of the rules of intrinsic assignment (see, for example, 7.2.1.3 of Fortran 2008). If the variable on the left of the assignment is an allocatable variable it may be deallocated first. This "may" is important: it isn't always deallocated.
If the variable array is deallocated then it will be reallocated with the bounds/shape of the expression on the right. The lower bound of the expression will be 1 regardless of the bounds of the function result.
However, in your case the shape of the variable array is the same as the shape of the expression. Here, the variable is not deallocated before it is reallocated. It thus keeps its original bounds (lower bound 0).

Pointer to subarray defined by a map

I want to define a pointer to a subarray. For a simple range this is easily done by pointer => array(i:j), but I can't figure out how to do this for a map like k=[k1,k2,k3]. If I would define another array I could use a loop like array2=[(array1(k(j)),j=1,size(k,1))]. But it isn't possible to assign a pointer in a similar way (pointer => [(array1(k(j)),j=1,size(k,1))]) since the r.h.s. of the expression seems to define another variabel which then not even has the target attribute. For simple tasks, a trick around this, is to first assign a pointer to the total array an to use the map on the readout. But in my case this doesn't seem to be possible.
I will attach to examples: The first one shows what I described above. The second one is a more complicated example, where the trick doesn't work anymore. And in addition a two dimensional map is required.
Minimal example:
program test
integer, parameter :: n=10,n_k=3
real,target :: a(1:n)
real :: b(1:n_k)
integer :: k(1:n_k)
integer :: j
real,pointer :: p(:)
! fill array a and define map k:
a=[(real(j),j=1,n)]
k=[((j+1)*2,j=1,n_k)]
! can be used to print the arrays:
!write(*,*) a
!write(*,*) k
! can be used to write only the part of a defined by k:
!write(*,*) (a(k(j)),j=1,n_k)
! this an similar things didn't work:
!p(1:n_k) => [(a(k(j)),j=1,n_k)]
! works, but not generally:
p => a
write(*,*) (p(k(j)),j=1,n_k)
! works, only for arrays:
b=(/(a(k(j)),j=1,n_k)/)
write(*,*) b
end program
More complicated (but also kind of minimal) example which shows (hopefully) the problem I really have. For an easy understanding some explanation leads through it. There are plenty of write commands to print the arrays. I appreciate for the amount of code, but I really don't see how to make a shorter and understandable working example:
module mod1
type base
real :: a
end type
type,extends(base) :: type1
end type
type,extends(base) :: type2
type(type1),allocatable :: b(:)
end type
type(type2),allocatable,target :: c(:)
contains
subroutine printer(z)
class(*),pointer,dimension(:) :: z
integer :: j,a_z,n_z
character(len=40) :: f,ff='(F10.2,1x))',form_z
! define format for printing:
a_z=lbound(z,1)
n_z=ubound(z,1)
write(f,'(I0)') (n_z-a_z+1)
form_z="("//trim(adjustl(f))//ff
! writing:
select type(z)
class is (base)
write(*,form_z) (z(j)%a,j=a_z,n_z)
end select
end subroutine
end module
program test
use mod1
integer,parameter :: n_b=8,n_c=6,n_js=3,n_ls=2
integer :: js(1:n_js),ls(1:n_ls)
integer :: j,l
class(*),pointer :: p(:)
character(len=40) :: f,ff='(F10.2,1x))',form_c,form_b
! define format for printing:
write(f,'(I0)') n_b
form_b="("//trim(adjustl(f))//ff
write(f,'(I0)') n_c
form_c="("//trim(adjustl(f))//ff
! creating and filling the arrays:
allocate(c(n_c))
c%a=[(2d0*real(j),j=1,n_c)]
do j=1,n_c
allocate(c(j)%b(n_b))
c(j)%b%a=[(real(l)*1d1**(j-1),l=1,n_b)]
end do
! write arrays to compare later:
write(*,form_c) c%a
write(*,*)
write(*,form_b) (c(j)%b%a,j=1,n_c)
write(*,*)
! denfining two maps (size and entries will be input in the final program):
js=[1,4,6]
ls=[2,7]
! using the maps to print only the desired entries:
write(*,*) (c(js(j))%a,j=1,n_js)
write(*,*)
write(*,*) ((c(js(j))%b(ls(l))%a,j=1,n_js),l=1,n_ls)
write(*,*)
! !!! here I want to use the maps as well, but so far I only know how to use ranges:
p => c(1:4)
call printer(p)
write(*,*)
p => c(2)%b(3:6)
call printer(p)
write(*,*)
end program
Edit:
Just for the record, I solved the problem now by using arrays of derived types including pointers and slightly changing the calling subroutines.
You cannot do this with pointer association (e.g. pointer1 => array1(vector_subscript). Section 7.2.2.2 of the Fortran 2008 standard that disallows this is:
R733 pointer-assignment-stmt is data-pointer-object [ (bounds-spec-list) ] => data-target
There are two other forms, but they do not match your use, nor would they change the outcome. Reading further:
R737 data-target is variable
C724 (R737) A variable shall have either the TARGET or POINTER attribute, and shall not be an array section with a vector subscript.
This is why you cannot perform the pointer association your are attempting. You can however work around this and with pointer allocation. See this code:
n_k = 3
k = [((j+1)*2,j=1,n_k)] ! a vector subscript
p => a(k) ! NOT OK. Violates C724
allocate(p(n_k)) ! Associate your pointer this way
p = a(k) ! This is OK.
write(*,*) p
Which yields (wrapped in your example program):
% ./ptrtest
4.00000000 6.00000000 8.00000000
This allocates p to be the proper size and then assigns from a with a vector subscript. This gets around the issue of directly associating p with a map of a. This snippet assumes the variables are declared and initialized per your example code. This shows that you can assign a vector subscript of an array to a pointer, but only one that is already associated, not during the association.
As noted in a comment to your Q, if you have a regular stride, you can make the pointer association directly. For your first test case, this would be equivalent and work:
p => a(4:2:8) ! Allocation to a strided array is allowed
If however, you have an irregular vector subscript then the method in this answer will be what you need to use to accomplish the pointer association.
Another workaround you can use is passing a pointer and the map to a procedure. Consider the following code:
program test
implicit none
integer, parameter :: nx = 10, nx_m = 3
integer,dimension(nx_m) :: x_map
integer :: i
real, dimension(nx),target :: a
real, dimension(:), pointer :: p
! initialize array
a = [(real(i*2),i=1,10)]
write (*,'(10(f5.1 x))') a
!define a map
x_map = [1, 9, 4]
! associate pointer
p => a
call print_map(p, x_map)
contains
subroutine print_map(apointer, map)
implicit none
real, dimension(:), pointer :: apointer
integer, dimension(:) :: map
write (*,*) apointer(map)
end subroutine print_map
end program test
In this case, p "knows" about a and the map of elements in a can be calculated in the caller. Rather than associating (=>) p as a map of a (which cannot be done), p is associated to a and the map passed along with it.
This code produces the output:
% ./ptrtest3
2.0 4.0 6.0 8.0 10.0 12.0 14.0 16.0 18.0 20.0
2.00000000 18.0000000 8.00000000

Resources