I'm writing a linear inverse program using geophysical data. I'm new to programming in Fortran although I have created programs relating to geophysical problems with Fortran before.
I'm encountering the error : "Error: Incompatible ranks 0 and 1 in assignment" when compiling. I know this has to do with lengths not agreeing, but I have been unable to resolve it. I want to assign the row of Prism_r(i,pp) with the values previously calculated, namely r1-r4. The error is as followed:
Prism_r(i,pp)=(/ r1(pp),r2(pp),r3(pp),r4(pp) /)
1
Error: Incompatible ranks 0 and 1 in assignment at (1)
Here is the relevant code:
real, dimension(0:P-1) :: r1, r2, r3, r4
real, dimension(0:D-1,0:3) ::Prism_r, Prism_theta
.....
do i=0,D-1
do pp=0,P-1
r1(pp)=sqrt((x2+2*PP-0.2*i)**2+z1**2)
r2(pp)=sqrt((x2+2*PP-0.2*i)**2+z2**2)
r3(pp)=sqrt((x1+2*PP-0.2*i)**2+z2**2)
r4(pp)=sqrt((x1+2*PP-0.2*i)**2+z1**2)
Prism_r(i,pp)=(/ r1(pp),r2(pp),r3(pp),r4(pp) /)
enddo
enddo
The calculations are being performed correctly when I comment Prism_r out, but it will not assign values to it. Does anyone have advice to how I need to correctly define r1-r4 so their values will be assigned to Prism_r?
It actually doesn't have to do with lengths not agreeing, but instead with ranks not agreeing, just as the error message says.
Prism_r(i,pp) is a single element of the array: it's a scalar, i.e. rank 0.
(/ r1(pp),r2(pp),r3(pp),r4(pp) /) is a rank 1 array (of length four).
In fortran you can't assign an array to a scalar.
Related
Related questions Temporary array creation and routine GEMM
Warning message (402) : An array temporary created for argument
For the following Fortran code (modified from dsyev in fortran 90)
program test_dsyev
implicit none
integer, parameter :: dp = selected_real_kind(15, 307)
real(dp), allocatable :: A(:,:), A2(:,:,:), work (:), w(:)
real(dp) :: c1
integer :: i, j
integer :: lwork, info, n, lda
character :: jobz, UPLO
n=5
allocate(A(n,n))
allocate(A2(1,n,n))
A = 0.0_dp
c1 = 1.0_dp
do i = 1, n
do j = 1, n
A(i, j) = i * c1 + j * c1
end do
end do
A2(1,:,:) = A
lda = n
lwork = 10 * n
jobz = 'N'
UPLO = 'U'
allocate(work(lwork))
allocate(w(n))
call dsyev(jobz,uplo,n,A,lda,w,work,lwork,info)
write (*,*) w
call dsyev(jobz,uplo,n,A2(1,:,:),lda,w,work,lwork,info)
write (*,*) w
end program test_dsyev
called d.f90.
ifort -mkl -warn all -check all d.f90 gives me
-1.58312395177700 -1.465651932086313E-015 8.229374109843719E-018
4.803282588162841E-016 31.5831239517770
forrtl: warning (406): fort: (1): In call to DSYEV, an array temporary was created for argument #4
Image PC Routine Line Source
a.out 0000000000408E86 Unknown Unknown Unknown
a.out 000000000040580B Unknown Unknown Unknown
a.out 0000000000403812 Unknown Unknown Unknown
libc-2.17.so 00002B10A2A31555 __libc_start_main Unknown Unknown
a.out 0000000000403729 Unknown Unknown Unknown
-1.58312395177700 -1.465651932086313E-015 8.229374109843719E-018
it seems the warning message is related to A2(1,:,:) and memory non-continuous. Yes, Fortran is column-major. But, the first dimension of A2 is 1. A2 should follow A2(1,1,1), A2(1,2,1),..., I mean, the first index of A2 does not play a role in memory allocation. Or am I totally wrong?
Let's consider a much simpler program to look at what's going on:
implicit none
integer i(1,1)
call s(i(1,:))
contains
subroutine s(j)
integer j(*)
end subroutine s
end program
Compiled with -check to enable temporary copying checks we can see exactly the same warning:
forrtl: warning (406): fort: (1): In call to S, an array temporary was created for argument #1
Like the example of the question, the dummy argument has assumed size and the actual argument is an array section of the same rank, but one rank smaller than the whole array.
The array section i(1,:) actual argument is contiguous, so perhaps there's no need for a temporary copy for the dummy j? Looking at this, ifort makes a temporary copy when i is not simply contiguous, even if it's "obviously" contiguous.1
However, because the dummy argument j is assumed-size, we don't need to worry about passing the array section i(1,:): we can pass the whole array i, or the simply contiguous section i(:,:). We can do this because the ranks of the actual and dummy arguments do not need to match when the dummy is assumed-size (or explicit-size).
Note that if the dummy is assumed-shape, then the ranks do need to match, but the compiler may handle the argument association by passing an array descriptor/dope instead of making a temporary copy: unlike assumed- and explicit-size array arguments, it's not necessary for an assumed-shape dummy to be contiguous. (The compiler may still make, and warn about making, a temporary copy when it decides that is beneficial, but it isn't always going to happen.)
Finally, I am speculating that the trigger for a warning is simple contiguity (happy for someone in the know to confirm/dispute), but there's some evidence that you can avoid the runtime check warning with a simple alternative approach as here. (It makes complete sense for the copy to be made if not simply contiguous: simple contiguity is defined in a way to be easy to check.)
1 i(1,:) is not simply contiguous, even when the first extent is 1, because of the rule of simple contiguity (F2018, 9.5.4):
no subscript-triplet is preceded by a section-subscript that is a subscript.
The : in second place is a subscript triplet, yet 1 in first place is a subscript.
I'm working in a finite difference method on an irregular grid, this is the important part of the code:
IMPLICIT DOUBLE PRECISION (A-Z)
REAL*16 IPSI,ICORR,POT(20000),VA(20000),delta1(20000),
$delta2(20000),R(20000),a,b,d
COMPLEX Y(20000),TY2(50000),Z(20000),PSI0(20000),RES,DPSI,C,
$CORR,OPK
DO I=3,NR-1
delta1=R(I)-R(I-1)
delta2=R(I+1)-R(I)
a=(2/(delta1*(delta1+delta2)))
b=(-2/(delta1*delta2))
d=(2/(delta2*(delta1+delta2)))
TY2(I)=((d*Z(I+1))+(b*Z(I))+(a*Z(I-1)))
ENDDO
When I try to compile I got Error: Incompatible ranks 0 and 1 in assignment at (1) for a,b,d and TY2. Any solution will be appreciated. Thanks!
a=(2/(delta1*(delta1+delta2)))
b=(-2/(delta1*delta2))
and the following lines are illegal. On the right you have arrays, on the left a scalar.
Maybe you forgot some index like delta1(I) or delta1 should be a scalar. We can't say without knowing more about your code.
I need a special library for Fortran so that I can code and visualize on the fly, instead of writing to a text file then use either Python or Matlab to plot. I followed this:
https://people.sc.fsu.edu/~jburkardt/f_src/gnufor/gnufor.html
The instructions are not cleared so I did not use their examples, so I wrote my own code. There are 2 files: gnu.f90 from website, and myplot.f90, which I wrote like this:
program myplot
!Declare data types
implicit none
integer, parameter :: N1 = 50
real(kind=8) :: x1(N1),x2(N1)
real(kind=8) :: y1(N1)
real(kind=8) :: y2(N1)
integer :: i
!Generate 2D plot
do i = 1,N1
x1(i) = i
x2(i) = i
end do
y1 = x1**2
y2 = x2**3
!print *, 'Plotting'
!call plot(x1,y1,x2,y2)
call write_xy_data(x1,y1)
end program myplot
Apparently, from their gnufor.f90 file, I only need to do:
write_xy_data(X,Y) and it should work. In their example code, they did not use gnufor.f90 as a module so I didnt put: use gnufor at the beginning. Although, I tried that and it didn't work as well. So in my current directory, I have:
gnufor.90
myplot.f90
And to compile it, I am on Linux: gfortran myplot.f90 -o test
The error was:
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
Backtrace for this error:
#0 0x7FB0B051DE08
#1 0x7FB0B051CF90
#2 0x7FB0AFF574AF
#3 0x40302F in write_xy_data_
#4 0x400D7E in MAIN__ at myplot.f90:?
Segmentation fault (core dumped)
Any help is greatly appreciated. I expected the problem to be in my own code, although all array sizes are declared.
The subroutine write_xy_data in gnufor.f90 from the website you linked has a signature
subroutine write_xy_data(data_filename, n, x, y, ierror)
implicit none
character ( len = * ) data_filename
integer ( kind = 4 ) n
real ( kind = 8 ) x(n)
real ( kind = 8 ) y(n)
integer(kind = 4) ierror
Which means instead of passing only x,y, you have to call it as
call write_xy_data('myfile',N1,x1,y1,ierr)
passing as arguments the name of the file to write to ('myfile' in the example), the number of points to write (N1), the data (x1,y1) and an integer that carries information on the success of the write (ierror, this one has to be declared, too)
Also, there is no module gnufor, so no use clause is required, all functions in gnufor.F90 are global, which is also the reason why you need to pass the number of points as an extra argument, as assumed shape would require an explicit interface.
I'm working in a finite difference method on an irregular grid, this is the important part of the code:
IMPLICIT DOUBLE PRECISION (A-Z)
REAL*16 IPSI,ICORR,POT(20000),VA(20000),delta1(20000),
$delta2(20000),R(20000),a,b,d
COMPLEX Y(20000),TY2(50000),Z(20000),PSI0(20000),RES,DPSI,C,
$CORR,OPK
DO I=3,NR-1
delta1=R(I)-R(I-1)
delta2=R(I+1)-R(I)
a=(2/(delta1*(delta1+delta2)))
b=(-2/(delta1*delta2))
d=(2/(delta2*(delta1+delta2)))
TY2(I)=((d*Z(I+1))+(b*Z(I))+(a*Z(I-1)))
ENDDO
When I try to compile I got Error: Incompatible ranks 0 and 1 in assignment at (1) for a,b,d and TY2. Any solution will be appreciated. Thanks!
a=(2/(delta1*(delta1+delta2)))
b=(-2/(delta1*delta2))
and the following lines are illegal. On the right you have arrays, on the left a scalar.
Maybe you forgot some index like delta1(I) or delta1 should be a scalar. We can't say without knowing more about your code.
I was doing an svd decomposition of a square matrix A, with A=U S Vdag, and in the fortran code, the line reads
lwork = -1
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
lwork = int(work(1));deallocate(work); allocate(work(lwork))
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
When I compiled with gfortran, it went through with no error or warning. However when I run the program, it shows error with message:
" ** On entry to ZGESVD parameter number 11 had an illegal value "
I could not figure out what went wrong.
For reference, the definitions of the parameters:
type cmatrix
integer(4) d
complex(8), allocatable :: m(:,:)
end type
type (cmatrix) A,U,Vdag
allocate(A%m(dim,dim),U%m(dim,dim),Vdag%m(dim,dim))
A%d = dim; U%m = dim; Vdag%d = dim
real(8) S(dim)
Thanks in advance!
Xiaoyu
p.s. It should be mentioned that such a program runs smoothly when compiled with ifort, but gfortran gives an runtime error as shown above
--- Problem solved!
It seems that the issue lies in how ifortran and gfortran allocates memory. I defined in the code USV type which:
type USV
integer is_alloc
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
When initializing by
type(USV) Test_usv(:)
allocate(Test_usv(3)),
the value of is_alloc is 0 using intel fortran compiler, while arbitrary number for gfortran. I need to use this value as a criterion for allocating U V matrices:
if (is_alloc.eq.0) then
allocate(U%m(dim,dim))
end if
The fundamental problem is not a difference between ifort and gfortran. Your approach to the initialization of the variables isn't valid Fortran. Unless you initialize a variable with a declaration, assignment statement, etc., its value is undefined. One way to fix this would be to add a default initialization to the type definition:
type USV
integer is_alloc = 0
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
Another approach would be to not track the allocation status yourself and to rely upon the intrinsic function that Fortran provides for this purpose:
if (.NOT. allocated (U%m) ) allocate(U%m(dim,dim))
P.S. Best practice is not to rely upon specific numeric values for kinds. The kind values are arbitrary and are not necessarily the number of bytes of the type. Some compilers use the number of bytes, others don't. One method to specify the number of bytes and to have portable code is to use types provided by the ISO Fortran environment:
use, intrinsic :: ISO_FORTRAN_ENV
integer(int32) :: d
real(real64), allocatable :: S(:)
The types are named after the number of bits. A list of the available types is in the "Intrinsic Modules" chapter of the gfortran manual.