I am writing a small piece of Fortran 90 code to compute some quantities using complex variables.
I have a subroutine with the following instructions:
complex, dimension(3) :: v
integer :: i
real:: tmp
do i = 1,3
tmp = vg(i)
v(i) = (tmp, 0.0)
enddo
v is a complex array of length 3. vg is an array of length 3 too whose elements are real.
When I compile the above code with gfortran 4.7.3 I get the following error:
v(i) = (tmp,0.0)
Error: Expected PARAMETER symbol in complex constant at (1)
I do not understand what's the problem.
You have to use
v(i) = cmplx(tmp, 0.0)
Your syntax (re, im) works only for constant expressions, i.e. when re and im are real or integer constants.
This means you cannot make a complex constant from a real variable and a real constant. You have to use the intrinsic function cmplx which converts real variables to complex ones, or builds complex variables from pairs of real variables (or integer).
Related
I always assumed that giving values in automatic sized dummy arguments within subroutine definitions has some benefit in error detection, when the routine is defined inside a module that gives an explicit interface. But following example shows that there is not really a checking of array bounds:
module test_mod
Implicit none
contains
!> Testing routine that has an automatic array arr of length n. The integer n is also given.
!> It is included in a module and has an explicit interface.
subroutine print_array(n, arr)
Integer, intent(in) :: n !< Size of arr
Integer, Dimension(n), intent(in) :: arr !< A test array that should be of size n
Integer :: i
do i = 1, n
print *, 'number:', i, arr(i)
enddo
End Subroutine print_array
end module test_mod
program playground
use test_mod, only : print_array
! Call print_array with an array smaller than 3 elements
Call print_array(3, [8, 9])
end program playground
arr is created with the dimension of n in subroutine print_array, but it is not checked if the dimension of the given array is correct. In my example the output is:
number: 1 8
number: 2 9
number: 3 -858993460
So the 3rd value is taken outside of arr bounds and in my case -858993460 is printed. I understand that such behaviour can not generally be catched at compile time. But even using ifort with /check:bounds option doesn't give an error message at run time.
Therefore, I would like to ask if it is good practice to specify arr with Dimension(n) or is it even better to define it with Dimension(:). What is the advantage of giving the dimension in such way?
I am trying to adapt a Fortran code (Gfortran) to make use of OpenMP. It is a particle based code where the index of arrays can correspond to particles or pairs. The code uses a derived type to store a number of matrices for each particle. It is very common to come across loops which require the use of a matrix stored in this derived type. This matrix may be accessed by multiple threads. The loop also requires a reduction over an element in this derived type. I currently have to write a temporary array in order to do this reduction and then I set the element of the derived type equal to this temporary reduction array. If not using OpenMP no temporary array is needed.
Question: Is it possible to do a reduction over an element of a derived type? I don't think I can do a reduction over the entire derived type as I need to access some of the elements in the derived type to do work, which means it needs to be SHARED. (From reading the specification I understand that when using REDUCTION a private copy of each list item is created.)
Complete minimal working example below. It could be more minimal but I feared that removing more components might over simplify the problem.
PROGRAM TEST_OPEN_MP
USE, INTRINSIC :: iso_fortran_env
USE omp_lib
IMPLICIT NONE
INTEGER, PARAMETER :: dp = REAL64
INTEGER, PARAMETER :: ndim=3
INTEGER, PARAMETER :: no_partic=100000
INTEGER, PARAMETER :: len_array=1000000
INTEGER :: k, i, ii, j, jj
INTEGER, DIMENSION(1:len_array) :: pair_i, pair_j
REAL(KIND=dp), DIMENSION(1:len_array) :: pair_i_r, pair_j_r
REAL(KIND=dp), DIMENSION(1:no_partic) :: V_0
REAL(KIND=dp), DIMENSION(1:ndim,1:no_partic) :: disp, foovec
REAL(KIND=dp), DIMENSION(1:ndim,1:len_array) :: dvx
REAL(KIND=dp), DIMENSION(1:2*ndim,1:len_array):: vec
REAL(KIND=dp), DIMENSION(1:ndim) :: disp_ij,temp_vec1,temp_vec2
REAL(KIND=dp), DIMENSION(1:ndim,1:ndim) :: temp_ten1,temp_ten2
REAL(KIND=dp), DIMENSION(1:no_partic,1:ndim,1:ndim):: reduc_ten1
REAL(KIND=dp) :: sum_check1,sum_check2,cstart,cend
TYPE :: matrix_holder !<-- The derived type
REAL(KIND=dp), DIMENSION(1:ndim,1:ndim) :: mat1 !<-- The first element
REAL(KIND=dp), DIMENSION(1:ndim,1:ndim) :: mat2 !<-- The second element, etc.
END TYPE matrix_holder
TYPE(matrix_holder), DIMENSION(1:no_partic) :: matrix
! Setting "random" values to the arrays
DO k = 1, no_partic
CALL random_number(matrix(k)%mat1(1:ndim,1:ndim))
CALL random_number(matrix(k)%mat2(1:ndim,1:ndim))
END DO
CALL random_number(pair_i_r)
CALL random_number(pair_j_r)
CALL random_number(disp)
CALL random_number(vec)
CALL random_number(dvx)
CALL random_number(V_0)
disp = disp*10.d0
vec = vec*100.d0
dvx = dvx*200.d0
V_0 = V_0*10d0
pair_i = FLOOR(no_partic*pair_i_r)+1
pair_j = FLOOR(no_partic*pair_j_r)+1
! Doing the work
cstart = omp_get_wtime()
!$OMP PARALLEL DO DEFAULT(SHARED) &
!$OMP& PRIVATE(i,j,k,disp_ij,temp_ten1,temp_ten2,temp_vec1,temp_vec2,ii,jj), &
!$OMP& REDUCTION(+:foovec,reduc_ten1), SCHEDULE(static)
DO k= 1, len_array
i = pair_i(k)
j = pair_j(k)
disp_ij(1:ndim) = disp(1:ndim,i)-disp(1:ndim,j)
temp_vec1 = MATMUL(matrix(i)%mat2(1:ndim,1:ndim),&
vec(1:ndim,k))
temp_vec2 = MATMUL(matrix(j)%mat2(1:ndim,1:ndim),&
vec(1:ndim,k))
DO jj=1,ndim
DO ii = 1,ndim
temp_ten1(ii,jj) = -disp_ij(ii) * vec(jj,k)
temp_ten2(ii,jj) = disp_ij(ii) * vec(ndim+jj,k)
END DO
END DO
reduc_ten1(i,1:ndim,1:ndim)=reduc_ten1(i,1:ndim,1:ndim)+temp_ten1*V_0(j) !<--The temporary reduction array
reduc_ten1(j,1:ndim,1:ndim)=reduc_ten1(j,1:ndim,1:ndim)+temp_ten2*V_0(i)
foovec(1:ndim,i) = foovec(1:ndim,i) - temp_vec1(1:ndim)*V_0(j) !<--A generic reduction vector
foovec(1:ndim,j) = foovec(1:ndim,j) + temp_vec1(1:ndim)*V_0(i)
END DO
!$OMP END PARALLEL DO
cend = omp_get_wtime()
! Checking the results
sum_check1 = 0.d0
sum_check2 = 0.d0
DO i = 1,no_partic
matrix(i)%mat2(1:ndim,1:ndim)=reduc_ten1(i,1:ndim,1:ndim) !<--Writing the reduction back to the derived type element
sum_check1 = sum_check1+SUM(foovec(1:ndim,i))
sum_check2 = sum_check2+SUM(matrix(i)%mat2(1:ndim,1:ndim))
END DO
WRITE(*,*) sum_check1, sum_check2, cend-cstart
END PROGRAM TEST_OPEN_MP
The only other alternative I can think of would be to remove all the derived types and replace these with large arrays similar to reduc_ten1 in the example.
Unfortunately, what you want is not possible. At least if I understood your (very complicated for me!) code correctly.
The problem is that you have an array of derived types each have an array. You cannot reference that.
Consider this toy example:
type t
real :: mat(3)
end type
integer, parameter :: n = 100, nk = 1000
type(t) :: parts(n)
integer :: i
real :: array(3,n,nk)
do k = 1, nk
array(:,:,nk) = k
end do
do i = 1, n
parts(i)%mat = 0
end do
!$omp parallel do reduction(+:parts%mat)
do k = 1, nk
do i = 1, n
parts(i)%mat = parts(i)%mat + array(:,i,nk)
end do
end do
!$omp end parallel do
end
Intel Fortran gives a more concrete error:
reduction6.f90(23): error #6159: A component cannot be an array if the encompassing structure is an array. [MAT]
!$omp parallel do reduction(+:parts%mat)
--------------------------------------^
reduction6.f90(23): error #7656: Subobjects are not allowed in this OpenMP* clause; a named variable must be specified. [PARTS]
!$omp parallel do reduction(+:parts%mat)
--------------------------------^
Remember that it is not even allowed to do this, completely without OpenMP:
parts%mat = 0
Intel:
reduction6.f90(21): error #6159: A component cannot be an array if the encompassing structure is an array. [MAT]
gfortran:
Error: Two or more part references with nonzero rank must not be specified at (1)
You must do this:
do i = 1, n
parts(i)%mat = 0
end do
The reason for the error reported by Intel above is very similar.
Actually no derived type components are allowed in the reduction clause, only variable names can be used. That is the reason for the syntax error reported by gfortran. It does not expect any % there. Intel again gives a clearer error message.
But one could make a workaround around that, like passing it to a subroutine and do the reduction there.
I am given this code:
...
IMPLICIT REAL*8(a-h,o-z)
DIMENSION L1(L), L2(M), L3(N)
...
I want to use IMPLICIT NONE but I don't know how to declare variable type using DIMENSION and maintain one line declaration of L1 to L3.
Something like:
INTEGER, DIMENSION :: L1(L), L2(M), L3(N) !(this doesn't work)
The syntax for the dimension statement differs from that of specifying the dimension attribute in a declaration statement.
So, whereas
dimension i(4) ! Implicitly typed
gives i array nature of size 4,
integer, dimension(4) :: i
is the way to go.
Now to come to your question about declaring multiple arrays in one line:
integer i(4), j(5), k(6)
Finally, one can still use
integer, dimension(4) :: i, j(5), k(6), l
making i and l arrays of size 4 and j and k arrays of size 5 and 6.
Let A and B be matrices of size 1 times n and n times 1, respectively.
Then the multiplication of A with B is a 1 times 1 matrix.
Which is the better way to assign the value of MATMUL(A,B) to a real number x?
I would like to write:
x=MATMUL(A,B) ! <<--- but this is wrong.
The above expression is wrong because I'm trying to assign a 1 times 1 matrix to a real number.
My solution is to define a 1 times 1 matrix C and with this:
C=MATMUL(A,B)
x=C(1,1) ! <--- this solution is ok, but is too long
But, there exists a better way to assign MATMUL(A,B) to the real number x?
The entire code with my question is as follow:
PROGRAM testing
!
IMPLICIT NONE
REAL :: A(1,2),B(2,1),C(1,1),x
!
A(1,1)=1.0; A(1,2)=3.5
B(1,1)=2.0; B(2,1)=5.0
C=MATMUL(A,B) ! it is ok
x=MATMUL(A,B) ! it is wrong
x=C(1,1) ! it is ok <--- exists a better way ??
!
END PROGRAM testing
You have noticed that it is not possible to do intrinsic assignment of an array to a scalar (and C is a rank-2 array of size 1). x=C(1,1) is the correct way to do such assignment from the single element of C to the scalar x.
There are other ways to abstract that correct assignment statement, but probably little value in doing so.
In your specific case, however, there is alternative. Rather than matmul, consider dot_product.
x = DOT_PRODUCT(A(1,:), B(:,1)) ! Scalar result, intrinsic assignment allowed.
per my comment, you can write a very simple function to extract the first element of an array:
real function first(matrix) !return the (1,1,1,..) element of an array
real, intent(in) :: matrix(*)
first=matrix(1)
end function
simply use as:
real :: a(1,2),b(2,1),x
...
x=first(matmul(a,b))
note if you want to make sure this is only used for a dimension(1,1) array you need to use an explicit interface and do:
real function first(matrix)
real, intent(in) :: matrix(:,:)
if(.not.all(shape(matrix).eq.[1,1]))reporterror()
first=matrix(1,1)
end function
I would like to calculate the Manhattan distance between 2 arrays in Fortran according to the formula:
d = Sum(|P(i)-R(i)|)
So I made a code in Fortran:
function DistM(v, u, dim)
integer dim
real(8) v(dim), u(dim), DistM
DistM=sum(abs(v-u))
end function DistM
I call this function by using this:
Coeff=DistM(tempvector1,tempvector2, dim)
But this doesn't seem to work (I don't get any return). I also tried by taking the power of 2 and then doing the sqrt of it, but it gets stuck at the sqrt then (if I run it in steps).
I tried another function and that one worked (see here after), but this one doesn't work :(:
function Roznica(v, u, dim)
integer dim
real(8) v(dim), u(dim), Rozn
Rozn=sum((v-u)**2)/dim
end function Roznica
Anyone an idea?
I would write a Manhattan distance function simply like this (with assumed-shape arrays you no longer have to use automatic arrays like you do, provided the function has an explicit interface):
pure function L1(v)
real, intent(in) :: v(:) ! <- shape assumed from actual argument
real :: L1
L1 = sum(abs(v))
end function
And then if you have two vectors you simply call the function as foo = L1(p - q).