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

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?

Related

Can I pass an array of arbitrary rank to a subroutine in Fortran?

I'm writing a general output for a numerical model I've written. I produce arrays that are either 1,2,3 or 4 dimensions on unknown length. I'm running into issues in how to define the array in the subroutine (found in a different module).
gfortran -g -O0 -I/usr/include -c analysis.f90
analysis.f90:136:32:
call write_netcdf('domain', domain, 'binary_cloud_domain', 0)
1
Error: Rank mismatch in argument ‘data_out’ at (1) (scalar and rank-4)
The error comes from this subroutine:
subroutine binary_cloud_analysis
use cloud, only: cloud_domain, cloud_smooth_domain
use write, only: write_netcdf
real, allocatable, dimension(:,:,:,:) :: domain, smth_domain
allocate(domain(x_dim,y_dim,z_dim,t_dim), smth_domain(x_dim,y_dim,z_dim,t_dim))
call cloud_domain(domain)
if (cloud_smooth) then
call cloud_smooth_domain(domain,smth_domain)
domain = smth_domain
endif
call write_netcdf('domain', domain, 'binary_cloud_domain', 0)
end subroutine binary_cloud_analysis
At the moment in the subroutine write_netcdf I've assigned data_out as
subroutine write_netcdf(var_name, data_out, output_name, output_type)
!--------------------------------------------------------------------------!
! input variables !
!--------------------------------------------------------------------------!
real(kreal), allocatable, intent (in) :: data_out
Understandably the compiler is taking this as a scalar as I haven't included a dimensions(:) type specifier because I don't know what type of array I'm passing in generality and I'd hoped it might be able to massage the dimensions due to the variable being allocatable. The output_type variable specifies what type of array we have i.e. 3d, 4d etc to help define the NetCDF file.
I think I would be able to do it by reshaping the array into a 1d array and then reshaping back based on defining the number of dimensions and passing the lengths but ideally I wouldn't have these additional steps. Is there a way the define an allocatable array in sufficient generality for these purposes?

Adding to an array of characters in Fortran

I'm trying to write a class procedure that adds a new character to an array of characters, but keep stumbling across "different character length in array constructor" errors (compiling with GFortran), even when the characters lengths are, as far as I can see, the same.
Here's my function:
subroutine addToArray(this, newElement)
class(MyClass), intent(inout) :: this
character(len=*), intent(in) :: newElement
character(len=256) :: tempElement
character(len=256), dimension(:), allocatable :: temp
tempElement = newElement ! Needed otherwise newElement is of the wrong size
allocate(temp(size(this%charArray)+1) ! Make the char array bigger by 1
temp = [this%charArray, tempElement]
call move_alloc(from=temp, to=this%charArray)
end subroutine
This results in the error Fortran runtime error: Different CHARACTER lengths (538976288/256) in array constructor. However, if I print len(this%charArray) or len(tempElement), they are both 256 characters long. So where is the 538976288 coming from?
I'm typically calling this procedure using something like myObject%addToArray('hello'). this%charArray is declared in the type definition as character(len=256), dimension(:), allocatable :: charArray, and allocated using allocate(this%charArray(0)).
It appears it is an error which I reported to GCC more than a year ago https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70231
The workaround is to compile with optimizations at least -O1.
If it is not the same error, an exact reproduction case is needed including the compilation flags and all relevant details.

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

error #6366: The shapes of the array expressions do not conform

I create a program to solve two-body problem by runge-kutt method. I was faced with the problem: when I call the function which return the ELEMENT of two-dimension array from expression, which must give the ELEMENT of two-dimension array too (sorry for confusion with terms), I get this message:
error #6366: The shapes of the array expressions do not conform.
[X1]
X1(DIM,i)=X1(DIM,i-1)+0.5D0*ABS(i/2)*H*K1(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,2,maxnu)
I have a external interface for this function and it's apparently that compiler consider this as a function.
I must clarify some things:
Yes, it's not quite Fortran, it's preprocessor Trefor, which is used by astronomers in Moscow University (I'm just a student). This language is very similar to fortran, but a bit closer to C (Semi-colons for example), which is studied by many students.
Runge-Kutta method can be briefly written as:
We have initial value problem
dy/dt=f(t,y), y(t0)=y0
y is unknown vector, which contains 12 components in my case (3 coordinates and 3 velocities for each body)
next step is
y(n+1)=y(n)+1/6*h*(k1+2k2+2k3+k4), t(n+1)=t(n)+h
where
k1=f(tn,yn),
k2=f(tn+1/2h,yn+h/2*k1)
k3=f(tn+1/2h,yn+h/2*k2)
k4=f(tn+1/2h,yn+k3)
I.e. in my code X1,2 and V1,2 and K_1,2 should be vectors, because each of them must be have 3 spatial components and 4 components for each 'order' of method.
The full code:
FUNCTION K1(DIM,i,X1,X2,V1,V2,NDIM,ORD,nu,maxnu)RESULT (K_1);
integer,intent(in) :: i,DIM,nu;
real(8) :: K_1;
real(8) :: B1;
real(8) :: R;
real(8),intent(in) :: X1,X2,V1,V2;
COMMON/A/M1,M2,Fgauss,H;
integer,intent(in) :: NDIM,ORD,maxnu;
Dimension :: B1(NDIM, ORD);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD),V1(NDIM,ORD),V2(NDIM,ORD);
Dimension :: K_1(NDIM,ORD);
IF (nu>=2) THEN;
B1(DIM,i)=V1(DIM,i);
ELSE;
R=((X1(1,i)-X2(1,i))**2.D0+(X1(2,i)-X2(2,i))**2.D0+(X1(3,i)-X2(3,i))**2.D0)**0.5D0;
B1(DIM,i)=Fgauss*M2*(X2(DIM,i)-X1(DIM,i))/((R)**3.D0);
END IF;
K_1(DIM,i)=B1(DIM,i);
RETURN;
END FUNCTION K1;
FUNCTION K2(DIM,i,X1,X2,V1,V2,NDIM,ORD,nu,maxnu)RESULT (K_2);
integer,intent(in) :: i,DIM,nu;
real(8) :: K_2;
real(8) :: B2;
real(8) :: R;
real(8),intent(in) :: X1,X2,V1,V2;
COMMON/A/M1,M2,Fgauss,H;
integer,intent(in) :: NDIM,ORD,maxnu;
Dimension :: B2(NDIM,ORD);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD),V1(NDIM,ORD),V2(NDIM,ORD);
Dimension :: K_2(NDIM,ORD);
IF (nu>=2) THEN;
B2(DIM, i)=V2(DIM,i);
ELSE;
R=((X1(1,i)-X2(1,i))**2.D0+(X1(2,i)-X2(2,i))**2.D0+(X1(3,i)-X2(3,i))**2.D0)**0.5D0;
B2(DIM, i)=Fgauss*M1*(X2(DIM,i)-X1(DIM,i))/((R)**3.D0);
END IF;
K_2(DIM,i)=B2(DIM, i);
RETURN;
END FUNCTION K2;
PROGRAM RUNGEKUTT;
IMPLICIT NONE;
Character*80 STRING;
real(8) :: M1,M2,Fgauss,H;
real(8) :: R,X1,X2,V1,V2;
integer :: N,i,DIM,NDIM,maxnu,ORD;
integer :: nu;
PARAMETER(NDIM=3,ORD=4,maxnu=2);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD);
Dimension :: V1(NDIM,ORD),V2(NDIM,ORD);
INTERFACE;
FUNCTION K1(DIM,i,X1,X2,V1,V2,NDIM,ORD,nu,maxnu)RESULT (K_1);
integer,intent(in) :: i,DIM,nu;
real(8) :: K_1;
real(8) :: R;
real(8) :: B1;
real(8),intent(in) :: X1,X2,V1,V2;
COMMON/A/M1,M2,Fgauss,H;
integer,intent(in) :: NDIM,ORD,maxnu;
Dimension :: B1(NDIM, ORD);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD),V1(NDIM,ORD),V2(NDIM,ORD);
Dimension :: K_1(NDIM,ORD);
END FUNCTION K1;
FUNCTION K2(DIM,i,X1,X2,V1,V2,NDIM,ORD,nu,maxnu)RESULT (K_2);
integer,intent(in) :: i,DIM,nu;
real(8) :: K_2;
real(8) :: R;
real(8) :: B2;
real(8),intent(in) :: X1,X2,V1,V2;
COMMON/A/M1,M2,Fgauss,H;
integer,intent(in) :: NDIM,ORD,maxnu;
Dimension :: B2(NDIM,ORD);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD),V1(NDIM,ORD),V2(NDIM,ORD);
Dimension :: K_2(NDIM,ORD);
END FUNCTION K2;
END INTERFACE;
open(1,file='input.dat');
open(2,file='result.res');
open(3,file='mid.dat');
READ(1,'(A)') STRING;
READ(1,*) Fgauss,H;
READ(1,*) M1,M2;
READ(1,*) X1(1,1),X1(2,1),X1(3,1),V1(1,1),V1(2,1),V1(3,1);
READ(1,*) X2(1,1),X2(2,1),X2(3,1),V2(1,1),V2(2,1),V2(3,1);
WRITE(*,'(A)') STRING;
WRITE(3,'(A)') STRING;
WRITE(3,'(A,2G14.6)')' Fgauss,H:',Fgauss,H;
WRITE(3,'(A,2G14.6)')' M1,M2:',M1,M2;
WRITE(3,'(A,6G17.10)')' X1(1,1),X1(2,1),X1(3,1),V1(1,1),V1(2,1),V1(3,1):',X1(1,1),X1(2,1),X1(3,1),V1(1,1),V1(2,1),V1(3,1);
WRITE(3,'(A,6G17.10)')' X2(1,1),X2(2,1),X2(3,1),V2(1,1),V2(2,1),V2(3,1):',X2(1,1),X2(2,1),X2(3,1),V2(1,1),V2(2,1),V2(3,1);
R=((X1(1,1)-X2(1,1))**2.D0+(X1(2,1)-X2(2,1))**2.D0+(X1(3,1)-X2(3,1))**2.D0)**0.5D0;
N=0;
_WHILE N<=100 _DO;
i=2;
_WHILE i<=ORD _DO;
DIM=1;
_WHILE DIM<=NDIM _DO;
X1(DIM,i)=X1(DIM,i-1)+0.5D0*ABS(i/2)*H*K1(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,2,maxnu);
X2(DIM,i)=X2(DIM,i-1)+0.5D0*H*ABS(i/2)*K2(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,2,maxnu);
V1(DIM,i)=V1(DIM,i-1)+0.5D0*H*ABS(i/2)*K1(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,1,maxnu);
V2(DIM,i)=V2(DIM,i-1)+0.5D0*H*ABS(i/2)*K2(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,1,maxnu);
DIM=DIM+1;
_OD;
i=i+1;
_OD;
_WHILE DIM<=NDIM _DO;
X1(DIM,1)=X1(DIM,1)+1.D0/6.D0*H*(K1(DIM,1,X1(DIM,1),X2(DIM,1),V1(DIM,1),V2(DIM,1),NDIM,ORD,2,maxnu)+2.D0*K1(DIM,2,X1(DIM,2),X2(DIM,2),V1(DIM,2),V2(DIM,2),NDIM,ORD,2,maxnu)+2.D0*K1(DIM,3,X1(DIM,3),X2(DIM,3),V1(DIM,3),V2(DIM,3),NDIM,ORD,2,maxnu)+K1(DIM,4,X1(DIM,4),X2(DIM,4),V1(DIM,4),V2(DIM,4),NDIM,ORD,2,maxnu));
X2(DIM,1)=X2(DIM,1)+1.D0/6.D0*H*(K2(DIM,1,X1(DIM,1),X2(DIM,1),V1(DIM,1),V2(DIM,1),NDIM,ORD,2,maxnu)+2.D0*K2(DIM,2,X1(DIM,2),X2(DIM,2),V1(DIM,2),V2(DIM,2),NDIM,ORD,2,maxnu)+2.D0*K2(DIM,3,X1(DIM,3),X2(DIM,3),V1(DIM,3),V2(DIM,3),NDIM,ORD,2,maxnu)+K2(DIM,4,X1(DIM,4),X2(DIM,4),V1(DIM,4),V2(DIM,4),NDIM,ORD,2,maxnu));
V1(DIM,1)=V1(DIM,1)+1.D0/6.D0*H*(K1(DIM,1,X1(DIM,1),X2(DIM,1),V1(DIM,1),V2(DIM,1),NDIM,ORD,1,maxnu)+2.D0*K1(DIM,2,X1(DIM,2),X2(DIM,2),V1(DIM,2),V2(DIM,2),NDIM,ORD,2,maxnu)+2.D0*K2(DIM,3,X1(DIM,3),X2(DIM,3),V1(DIM,3),V2(DIM,3),NDIM,ORD,2,maxnu)+K2(DIM,4,X1(DIM,4),X2(DIM,4),V1(DIM,4),V2(DIM,4),NDIM,ORD,2,maxnu));
V2(DIM,1)=V2(DIM,1)+1.D0/6.D0*H*(K2(DIM,1,X1(DIM,1),X2(DIM,1),V1(DIM,1),V2(DIM,1),NDIM,ORD,1,maxnu)+2.D0*K2(DIM,2,X1(DIM,2),X2(DIM,2),V1(DIM,2),V2(DIM,2),NDIM,ORD,1,maxnu)+2.D0*K2(DIM,3,X1(DIM,3),X2(DIM,3),V1(DIM,3),V2(DIM,3),NDIM,ORD,1,maxnu)+K2(DIM,4,X1(DIM,4),X2(DIM,4),V1(DIM,4),V2(DIM,4),NDIM,ORD,1,maxnu));
_OD;
R=((X1(1,5)-X2(1,5))**2.D0+(X1(2,5)-X2(2,5))**2.D0+(X1(3,5)-X2(3,5))**2.D0)**0.5D0;
N=N+1;
write(2,'(A,1i5,6g12.5)')' N,X1(1,1),X1(2,1),X1(3,1),X2(1,1),X2(2,1),X2(3,1):',N,X1(1,1),X1(2,1),X1(3,1),X2(1,1),X2(2,1),X2(1,1),X2(2,1),X2(3,1);
_OD;
END PROGRAM RUNGEKUTT;
Please, help, it seems, I don't understand something in using functions!
M.S.B. is on the right track, but I think there's enough here to figure out the problem. As noted, function K1 returns a two-dimension array. But all of the other operands in the expression are scalars (well, I don't know what H is, but it likely doesn't matter.) What ends up happening is that the expression evaluates to an array, the scalars getting expanded as needed to match up. You then end up with assigning an array to a scalar, and that's the cause of the error.
I am not familiar enough with Runge-Kutta to be able to suggest what you want instead. But it is likely that you want the function to return a scalar, not an array.
Are you calculating a scaler? If I understand what you are trying to do, the function returns a 2D array, but you only assign to one element of it. Why not have the function return a scaler value instead of an array?
The array message is about an inconsistency between the shapes of arrays in the expression. You haven't shown all of the declarations so we can't figure that out.
Coding style tips: 0) Is there a typo? Should it be Function K1? 1) Semi-colons aren't necessary on the end of each line. Fortran isn't C. 2) At least to me, your code would be more readable if you put all of the declarations pertaining to each variable on one line, instead of separate lines for type, intent and dimension. For example:
real, dimension (NDIM,ORD), intent (in) :: X1
EDIT after the edit of the question:
The machine written code is ugly.
It is clear that you need to do the calculation for all the dimensions. The question is where. The code shows the loops containing the function call rather than the function containing the loops. With this overall design it would make sense that you calculate a single element of the output array (i.e., a scaler variable) and have that be the function return instead of having the function return an array. For this design, it makes little sense to return a 2D array containing only a single used element. And since your statement in the main program expects a scaler, you are getting the error message from the compiler. So redesign your function to return a scaler.
And it looks that you are calling K1 with the actual argument being single elements when arrays are expected. For example, you have X1(DIM,i-1) as a third argument when the function expects an array of size X1(NDIM,ORD). This also a problem, as an inconsistency in actual (i.e., call) and dummy arguments (i.e., function). If function K1 is to do the work of selecting the appropriate array elements, you need to pass it the entire array. If the call is to select the appropriate array elements, then rewrite K1 to have scalers instead of arrays as input arguments. You need a consistent design.

keeping array limits in fortran during subroutine call

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:)

Resources