Error Message about Array Arguments in Code for Mean - arrays

I have a variable length (L) that changes for each particle in a group of a few hundred thousand particles with every time step. I'm trying to add a code that will tell me the average length of the particles at each time step in the model. I keep getting this error: "An array-valued argument is required in this context" for my mean equation. What does this mean? It seems to have a problem with my length variable, which I extract from the model using get_state. Here is my code:
function checkstatus(ng,g,time) result(validsim)
use utilities
implicit none
logical :: validsim
integer, intent(in) :: ng
type(igroup), intent(in) :: g(ng)
real(sp), intent(in) :: time
real(sp), pointer :: L(:)
real(sp), dimension(ng) :: mean
integer, pointer :: istatus(:)
integer, allocatable :: mark(L)
integer :: n,NI
integer, save :: dumphead = 0
do n=1,ng
NI = g(n)%Nind
call get_state('L',g(n),L)
mean(NI) = sum(L(NI)) / size(L(NI))
end do
write(*,103)time,mean(NI)

In your code, L is a pointer to a one-dimensional real array, and so L(NI) is a scalar real. The intrinsics sum and size take array arguments, so the statement sum(L(NI)) / size(L(NI)) is invalid.

Related

Fortran shape matching rules violated

What am I trying to achieve
Im trying to write a subroutine that takes an Matrix (2D array) as input and prints it nicely to the standard console output.
Problem
error #6634: The shape matching rules of actual arguments and dummy arguments have been violated. ['U']
Code
The subroutine printing the matrix is in this module
Module
MODULE LinearSystems
IMPLICIT NONE
private
...
public showMatrix
...
contains
subroutine showMatrix(a, n, m, name)
implicit none
double precision, dimension(:,:), intent(in) :: a
character, dimension(:), intent(in), optional :: name
integer, intent(in) :: n,m
integer :: i, j
write(*,*) "*** Show Matrix ", name, " ***"
do i = 1, n
do j = 1, m
write(*,'(F8.4)',advance="no") a(i,j)
end do
write(*,*)
end do
end subroutine showMatrix
and the main program calls it
Main Program
program PoisonEquation
...
use LinearSystems
implicit none
double precision, dimension(:,:), allocatable :: u,...
integer :: n = 700
allocate(u(n-1,n-1))
...
call showMatrix(u, n-1,n-1, "U")
I'm looking forward to receive tipps on how to improve this code snipped and get it bug free.
The name dummy argument is an assumed shape array (see the dimension(:) declaration). The "U" literal used for the actual argument is scalar (the error message refers to this literal).
If a dummy argument is an assumed shape array, the rank of the actual argument shall be the same as the rank of the dummy argument (F2018 15.5.2.4p16).
Figure out whether you want to pass/receive an array or a scalar, and fix the code.
Problem solved
The problem was in the character initialisation and after fixing this issue as well as fixing a issue in the main program, which lead to u being deallocated before it was passed to the subroutine the code now works and the subroutine for printing looks like this:
subroutine showMatrix(a, name)
implicit none
double precision, dimension(:,:), intent(in) :: a
character(*), intent(in), optional :: name
integer :: i, j
write(*,*) "*** Show Matrix ", name, " ***"
do i = 1, size(a,1)
do j = 1, size(a,2)
write(*,'(F8.4)',advance="no") a(i,j)
end do
write(*,*)
end do
end subroutine showMatrix

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?

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

finding specific indices with pointer array

I am relatively new to Fortran and break my head about one thing for hours now:
I want to write a subroutine for finding the indexes for specific elements in a real 1D array (given to the routine as input).
I have generated an array with 100 random reals, called arr, and now want to determine the indexes of those elements which are greater than a real value min, which is also passed to subroutine.
Plus, in the end I would like to have a pointer I'd allocate in the end, which I was said would be better than using an array indices containing the indexes once found.
I just didn't find how to solve that, I had following approach:
SUBROUTINE COMP(arr, min)
real, intent(in) :: arr(:)
real, intent(in) :: min
integer, pointer, dimension(:) :: Indices
integer :: i, j
! now here I need a loop which assigns to each element of the pointer
! array the Indices one after another, i don't know how many indices there
! are to be pointed at
! And I dont know how to manage that the Indices are pointed at one after another,
! like Indices(1) => 4
! Indices(2) => 7
! Indices(3) => 32
! Indices(4) => 69
! ...
! instead of
! Indices(4) => 4
! Indices(7) => 7
! Indices(32) => 32
! Indices(69) => 69
! ...
DO i = 1, size(arr)
IF (arr(i) > min) THEN
???
ENDIF
ENDDO
allocate(Indices)
END SUBROUTINE COMP
If succinctness (rather than performance) floats your boat... consider:
FUNCTION find_indexes_for_specific_elements_in_a_real_1D_array(array, min) &
RESULT(indices)
REAL, INTENT(IN) :: array(:)
REAL, INTENT(IN) :: min
INTEGER, ALLOCATABLE :: indices(:)
INTEGER :: i
indices = PACK([(i,i=1,SIZE(array))], array >= min)
END FUNCTION find_indexes_for_specific_elements_in_a_real_1D_array
[Requires F2003. Procedures that have assumed shape arguments and functions with allocatable results need to have an explicit interface accessible where they are referenced, so all well behaved Fortran programmers put them in a module.]
A simple way to get the indices of a rank 1 array arr for elements greater than value min is
indices = PACK([(i, i=LBOUND(arr,1), UBOUND(arr,1))], arr.gt.min)
where indices is allocatable, dimension(:). If your compiler doesn't support automatic (re-)allocation than an ALLOCATE(indices(COUNT(arr.gt.min)) would be needed before that point (with a DEALLOCATE before that if indices is already allocated).
As explanation: the [(i, i=...)] creates an array with the numbers of the indices of the other array, and the PACK intrinsic selects those corresponding to the mask condition.
Note that if you are doing index calculations in a subroutine you have to be careful:
subroutine COMP(arr, min, indices)
real, intent(in) :: arr(:)
real, intent(in) :: min
integer, allocatable, intent(out) :: indices(:)
!...
end subroutine
arr in the subroutine will have lower bound 1 regardless of the bounds of the actual argument (the array passed) (which could be, say VALS(10:109). You will also then want to pass the lower bound to the subroutine, or address that later.
[Automatic allocation is not an F90 feature, but in F90 one also has to think about allocatable subroutine arguments
I think you're on the right track, but you're ignoring some intrinsic Fortran functions, specifically count, and you aren't returning anything!
subroutine comp(arr, min)
real, intent(in) :: arr(:)
real, intent(in) :: min
! local variables
integer, allocatable :: indices(:)
integer :: i,j, indx
! count counts the number of TRUE elements in the array
indx = count(arr > min)
allocate(indices(indx))
! the variable j here is the counter to increment the index of indices
j=1
do i=1,size(arr)
if(arr(i) > min) then
indices(j) = i
j = j+1
endif
enddo
end subroutine comp
Then you can use the array indices as
do i=1,size(indices)
var = arr(indices(i))
enddo
Note that since you are not returning indices, you will lose all the information found once you return--unless you plan on using it in the subroutine, then you're okay. If you're not using it there, you could define it as a global variable in a module and the other subroutines should see it.

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.

Resources