I have
module mymod
contains
subroutine mysub(matrix_dum, i_size, j_size)
integer :: i, j, i_size, j_size
real(8), dimension(:,:) matrix_dum
do j=1, j_size
do i = 1, i_size
matrix_dum(i,j) = 11.d0*matrix_dum(i,j)
end do
end do
end subroutine mysub
end module mymod
program main
use mymod
implicit none
real(8), dimension(:,:,:), pointer :: matrix
integer :: i, num_matrices, i_size, j_size
num_matrices = 11
i_size = 5000
j_size = 6000
!only one of them are uncommented in actual practice
!I have two choices this choice takes a very very long time
allocate(matrix(num_matrices,i_size,j_size))
matrix = 11.d0
do i = 1, num_matrices
call mysub(matrix(i,:,:),i_size,j_size)
end do
!this one does the same job instantly
allocate(matrix(i_size,j_size,num_matrices))
matrix = 11.d0
do i = 1, num_matrices
call mysub(matrix(:,:,i),i_size,j_size)
end do
deallocate(matrix)
end program main`
Why does the second work as I expected but the first does not. I know it must be the way Fortran stores multidimensional arrays in a single line with memory locations one after the other. But I am not sure about my syntax. Am I correctly passing the addresses of the matrices? or am I telling it to copy the entire matrix instead of giving it the address. How do you pass the pointer to a section of a multidimensional array? what is going on here?
Typically Fortran processors store arrays in the array element order defined by the language - such that the left most subscript varies fastest as you move from element to element.
So all the elements of matrix(:,:,1) will be contiguous - located next to each other.
The elements of matrix(1,:,:) will not be contiguous if SIZE(matrix,1) /= 1.
Operations on arrays that are not in array element order or that are not contiguous are often slower (e.g. they may be less cache friendly or may, in some cases, require the construction of intermediate temporary arrays).
Related
Some context. I have this piece of code :
function areeq(array1,array2) result(eq)
real :: array1(1:100,1:100), array2(1:100,1:100)
logical :: eq
integer :: x,y,f
do x=1,100
do y = 1,100
print *,array1(x:x,y:y)
print *,array2(x:x,y:y)
if(.not.(array1(x:x,y:y) == array2(x:x,y:y))) then
eq = .false.
return
end if
read *,f
end do
end do
eq = .true.
return
end function
However, when I try to run it, it throws this error message:
if(.not.(array1(x:x,y:y) == array2(x:x,y:y))) then
1
Error: IF clause at (1) requires a scalar LOGICAL expression
This is the second time that I've encountered trouble with something needing to be Scalar, and though I managed to hack together a makeshift work around for the last time, I really ought to, and need to, be able to handle them properly.
So, TL;DR: What is wrong with this piece of code, and what should I do in situations like this more generally?
Given
integer n
real x(5)
then, given appropriate definition of n
x(n)
is an array element of x, and
x(n:n)
is an array section of x.
The array element is a scalar whereas the array section is itself an array of size 1.
As Steve Lionel says, in the case of the question,
array1(x:x,y:y) == array2(x:x,y:y)
is an array-valued expression (albeit again of size 1) which can be reduced to a scalar expression with ALL. However
array1(x,y) == array2(x,y)
is a scalar expression, with both operands scalar array elements.
In the reference x(n) we have an array element for scalar n. With n an array we would instead have an array being a vector subscript of x.
What is wrong is, as the compiler complains, your expression has an array result, with one element for each comparison. What you want is to wrap the expression in ALL(). For example:
if(.not.(all(array1(x:x,y:y) == array2(x:x,y:y)))) then
This is my code:
Program Arrays
Implicit none
Integer::i,j
Integer,dimension(2)::V_Max
Complex,dimension(0:7,3)::V_cvo
Complex,dimension(7,3)::V_cvo_temp
Do concurrent(i=0:7,j=1:3)
V_cvo(i,j)=cmplx(i+j,2*i-j)
End Do
V_cvo_temp=V_cvo(1:,:)
V_Min=minloc(abs((/((V_cvo_temp(i,j),j=1,3),i=2,5)/)))
Stop
End Program Arrays
After compiling I got a this message:
Error: Different shape for array assignment on dimension 1 (2 and 1)|
What is wrong here? If I want to find location of minimal element in some array in specific sector of that array how it is possible?
This could be one of the possible solution for the problem:
Program Arrays
Implicit none
Integer::i,j
Integer,dimension(2)::V_Max
Complex,dimension(0:7,2)::V_cvo
Logical,dimension(0:7,2) :: lmask = .FALSE.
forall(i=2:5,j=1:2)lmask(i,j) = .TRUE.
Do concurrent(i=0:7,j=1:2)
V_cvo(i,j)=cmplx(i+j,2*i-j)
End Do
V_Max = Maxloc(abs(V_cvo),mask=lmask)-(/1,0/)
Open(1,File='Output.txt',Status='Unknown')
Write(1,'(2x,i0,2x,i0)') V_max
Write(1,*)
Do concurrent(i=2:5,j=1:2)
Write(1,'(1x,i0,1x,i0,2x,f7.4)')i,j,abs(V_cvo(i,j))
End Do
Close(1)
Stop
End Program Arrays
Output file is:
5 1
2 1 4.2426
3 1 6.4031
4 1 8.6023
5 1 10.8167
2 2 4.4721
3 2 6.4031
4 2 8.4853
5 2 10.6301
Opinions about this?
This expression
minloc(abs((/((V_cvo_temp(i,j),j=1,3),i=2,5)/)))
returns a rank-1 array with 1 element. The lhs of the assignments is a rank-1 array with 2 elements. Fortran won't assign incompatible arrays -- hence the compiler error message.
(#gdlmx's answer is subtly wrong in its diagnosis, if the expression returned a scalar Fortran would happily broadcast its value to every element of an array.)
If the expression did return a scalar it would still not return the location of the minimum element in that section of V_cvo. The sub-expression
(/((V_cvo_temp(i,j),j=1,3),i=2,5)/)
produces a rank-1 array containing the specified elements of V_cvo_temp, it essentially flattens the array into a vector and loses their locations along the way. This is why the first expression returns a rank-1 array with 1 element - it's the location of an element in a rank-1 array.
The problem with this solution
V_Min=minloc(abs(V_cvo(2:5,1:3)))
is that the expression abs(V_cvo(2:5,1:3)) will return a (temporary) array indexed, as Fortran arrays are by default, from 1 on each rank. When I try the code it returns the location (1,1) which appears to be outside the section considered. That's the location of the minimum element of the temporary array.
The problem with the 'clever' solutions I've tried has been that abs(V_cvo(2:5,1:3)) always returns, even if hidden from view, a temporary array indexed from 1 on each rank. Any application of minloc or similar functions uses those indices, not the indices that v_cvo uses. The best solution might be to make an explicit temporary array (suitably declared) like this:
allocate(abstemp(LBOUND(v_cvo,1):UBOUND(v_cvo,1),LBOUND(v_cvo,2):UBOUND(v_cvo,2)))
then
v_min = minloc(abstemp(2:5,1:3))
and
deallocate(abstemp)
It seems that the right side of
V_Min=minloc(abs((/((V_cvo_temp(i,j),j=1,3),i=2,5)/)))
returns a scalar instead of a vector of 2 components. What you need is array slicing: V_cvo_temp(1:3,2:5)
V_Min=minloc(abs(V_cvo_temp(2:5,1:3)))
or simpler
V_Min=minloc(abs(V_cvo(2:5,1:3))) ! without temp array
Also you don't need the stop at the end.
Edit1:
minloc returns the index relative to (1,1). To understand this behavior, try this example:
Program Hello
Implicit none
Integer,dimension(2)::V_Min
Complex,dimension(0:7,3)::V_cvo
V_cvo = cmplx(10,10)
V_cvo(3,2) = cmplx(0,0) ! the minimum index = [3,2]
V_Min=minloc(abs(V_cvo))
print *, 'minloc for whole array: ', V_Min
V_Min=minloc(abs(V_cvo(3:,2:)))
print *, 'minloc for sub-array: ', V_Min
End Program Hello
It outputs:
minloc for whole array: 4 2 ! base index=[-1,0]
minloc for sub-array: 1 1 ! base index=[2,2]
So if passing a sub-array to minloc, you need to add your base index to get the 'correct' answer.
This solution also works fine (best maybe):
forall(i=1:7,j=1:3) V_cvo_temp(i,j)=abs(V_cvo(i,j))
V_Min = MINLOC(V_cvo_temp(m:n,:))+(/m-1,0/)
Code are correct for every m and n if they are in interval 1:7 for this case or in some other interval.
Have some experience with MPI, but not with some of the more advanced aspects like derived types, which is what my question is related to.
The code I am working on has several arrays dimensioned (-1:nx+2,-1:ny+2,-1:nz+2). To make it clear, each process has its own values of nx, ny, and nz. There is overlap between the arrays. For instance x(:,:,-1:2) on one proc will represent the same information as x(:,:,nz-1:nz+2) on the proc just "below" it.
A derived cell_zface type has been defined:
idir = 3
sizes = (/nx_glb, ny_glb, nz_glb/) !These nums are the same for all procs.
subsizes = (/nx, ny, 2/)
mpitype = MPI_DATATYPE_NULL
CALL MPI_TYPE_CREATE_SUBARRAY(3, sizes, subsizes, starts, &
MPI_ORDER_FORTRAN, mpireal, mpitype, errcode)
CALL MPI_TYPE_COMMIT(mpitype, errcode)
cell_zface = mpitype
Now, this derived type gets used, successfully, in several MPI_SENDRECV calls. For example
CALL MPI_SENDRECV( &
x(-1,-1, 1), 1, cell_zface, proc_z_min, tag, &
x(-1,-1,nz+1), 1, cell_zface, proc_z_max, tag, &
comm, status, errcode)
As I understand it, this call is sending and receiving two "horizontal" slices (i.e. x-y slices) of the array between procs.
I want to do something a little different, namely sending four "horizontal" slices. So I try
call mpi_send(x(-1,-1,nz-1), 2, cell_zface, &
proc_z_max, rank, comm, mpierr)
with an accompanying receive.
And finally, my problem: The code runs, but erroneously. AFAICT, this sends only two horizontal slices, even though I use "2" instead of "1" as the count argument. I can fix this by making two calls to mpi_send:
call mpi_send(x(-1,-1,nz-1), 1, cell_zface, &
proc_z_max, rank, comm, mpierr)
call mpi_send(x(-1,-1,nz+1), 1, cell_zface, &
proc_z_max, rank, comm, mpierr)
with accompanying receives, but this is certainly not pretty.
So, why does the mpi_send send only two horizontal slices, even though I set the count argument to "2"? And is there a clean way to do what I want to do here?
Every MPI datatype has two sizes, so to speak. One is the true size, i.e. the amount of memory it takes to store all the significant data referred by the datatype. One can think of it as of the amount of space in the actual message that an element of that datatype takes.
Another size is the so-called extent. Each datatype in MPI is a collection of instructions of the type: "go to offset dispi from the provided buffer location and read/write an element of basic type typei". The set of all (typei, dispi) pairs is called the type map of the datatype. The minimum offset is called the lower bound and the maximum offset + the size of the of the basic type at that offset + any padding needed is called the upper bound. The extent of a datatype is the difference between the upper bound and the lower bound and gives the size of the shortest contiguous memory region, which includes all locations accessed by the datatype.
As MPI mandates that no memory location is read from or written to more than once during any communication operation, the pairs in the typemap have to refer to disjoint locations. Therefore, the true extent of a datatype is always bigger than or equal to its size.
MPI uses the extent of the datatype when accessing consecutive elements of that datatype. The following statement:
MPI_SEND(buf, n, dtype, ...)
results in:
MPI takes one element of type dtype from location buf following the rules encoded as the typemape of dtype;
MPI takes the next element starting from location buf + extent(dtype);
...
MPI takes the n-th element starting from location buf + (n-1)*extent(dtype).
Primitive datatypes such as MPI_INTEGER, MPI_REAL, etc. have their extent matching the size of the basic type (INTEGER, REAL, etc.) + any padding mandated by the architecture, which makes it possible to send arrays of the basic type by simply specifying the count of elements.
Now, back to your case. You are creating a datatype that covers an nx x ny x 2 subarray from an nx_glb x ny_glb x nz_glb array. The size of that datatype is indeed nx * ny * 2 times the size of mpireal, but the extent is actually nx_glb * ny_glb * nz_glb times the extent of mpireal. In other words:
MPI_SEND(buf, 2, cell_zface, ...)
will not extract two consecutive nx x ny x 2 slabs from the big array at buf. Rather, it will extract one slab from each of two consecutive arrays of size nx_glb x ny_glb x nz_glb, starting from location (startx, starty, startz) in each array. If your program doesn't segfault when run, consider yourself lucky.
Now comes the tricky part. MPI allows one to give each datatype a fake extent (that's why I called the extent as defined earlier "true") by artificially setting the value of the lower and the upper bounds. Doing so does not affect the size of the datatype or its typemap (i.e. MPI still goes to the same offsets and manipulates elements of the same basic types), but affects the strides in memory that MPI makes while accessing consecutive elements of the given datatype. Earlier, setting the extent was done by "sandwiching" the datatype in a stricture between elements of the special pseudotypes MPI_LB and MPI_UB. Ever since MPI-2, the MPI_TYPE_CREATE_RESIZED is used to achieve the same.
integer(kind=MPI_ADDRESS_KIND) :: lb, extent
integer :: newtype
! First obtain the extent of the old type used to construct cell_zface
call MPI_TYPE_GET_EXTENT(mpireal, lb, extent, errcode)
! Adjust the extent of cell_zface
extent = (nx_glb * ny_glb * subsizes(3)) * extent
call MPI_TYPE_CREATE_RESIZED(cell_zface, lb, extent, newtype, errcode)
call MPI_TYPE_COMMIT(newtype, errcode)
! Get rid of the previous type
call MPI_TYPE_FREE(cell_zface, errcode)
cell_zface = newtype
You can now use cell_zface to send several consecutive slabs.
An alternative and presumably simpler approach is to set the size of 3-rd dimension of the array equal to the size of the 3-rd dimension of the subarray while calling MPI_TYPE_CREATE_SUBARRAY:
idir = 3
subsizes = (/nx, ny, 2/)
sizes = (/nx_glb, ny_glb, subsizes(3)/) !These nums are the same for all procs.
mpitype = MPI_DATATYPE_NULL
CALL MPI_TYPE_CREATE_SUBARRAY(3, sizes, subsizes, starts, &
MPI_ORDER_FORTRAN, mpireal, mpitype, errcode)
CALL MPI_TYPE_COMMIT(mpitype, errcode)
cell_zface = mpitype
In both cases I assume that starts(3) is equal to 0.
I want to assign complex array as variable.
My code is like
complex indx(3,3)
integer i,j
do i=1,3
do j=1,3
indx(i,j) = (i,j)
write(*,*) indx(i,j)
end do
end do
and in this case I am getting an error like
A symbol must be a defined parameter in this context. [I]
indx(i,j) = (i,j)
You must use function cmplx to build a complex value you want to assign.
complex indx(3,3)
integer i,j
do i=1,3
do j=1,3
indx(i,j) = cmplx(i,j)
write(*,*) indx(i,j)
end do
end do
The syntax you tried is only valid for constant literals.
The answer by Vladimir F tells the important part: for (i,j) to be a complex literal constant i and j must be constants.1 As stated there, the intrinsic complex function cmplx can be used in more general cases.
For the sake of some variety and providing options, I'll look at other aspects of complex arrays. In the examples which follow I'll ignore the output statement and assume the declarations given.
We have, then, Vladimir F's correction:
do i=1,3
do j=1,3
indx(i,j) = CMPLX(i,j) ! Note that this isn't in array element order
end do
end do
We could note, though, that cmplx is an elemental function:
do i=1,3
indx(i,:) = CMPLX(i,[(j,j=1,3)])
end do
On top of that, we can consider
indx = RESHAPE(CMPLX([((i,i=1,3),j=1,3)],[((j,i=1,3),j=1,3)]),[3,3])
where this time the right-hand side is in array element order for indx.
Well, I certainly won't say that this last (or perhaps even the second) is better than the original loop, but it's an option. In some cases it could be more elegant.
But we've yet other options. If one has compiler support for complex part designators we have an alternative for the first form:
do i=1,3
do j=1,3
indx(i,j)%re = i
indx(i,j)%im = j
end do
end do
This doesn't really give us anything, but note that we can have the complex part of an array:
do i=1,3
indx(i,:)%re = [(i,j=1,3)]
indx(i,:)%im = [(j,j=1,3)]
end do
or
do i=1,3
indx(i,:)%re = i ! Using scalar to array assignment
indx(i,:)%im = [(j,j=1,3)]
end do
And we could go all the way to
indx%re = RESHAPE([((i,i=1,3),j=1,3))],[3,3])
indx%im = RESHAPE([((j,i=1,3),j=1,3))],[3,3])
Again, that's all in the name of variety or for other applications. There's even spread to consider in some of these. But don't hate the person reviewing your code.
1 That's constants not constant expresssions.
Consider the following:
program main
integer, parameter :: n=10, m=20
integer ints(n,m)
real floats(m,n)
!... initialize ints
! ...
floats=transpose(ints)
!... do stuff with floats
end
looking at the documentation for gfortran, it seems that transpose(ints) will return an integer array which will then be cast to reals. In this operation, the compiler (gfortran) creates a temporary array for the transposed array which seems like a waste (compile with gfortran -O3 -Warray-temporaries -o test test.f90). Also note that if you change the real array "floats" into an integer array, the warning goes away.
Is there a way to do this (for arbitrary types) without generating a temporary array? (I also tried floats(:,:)=transpose(ints) because I read somewhere that it mattered ... ). Does it behave this way with other compilers?
You could try
floats = transpose(real(ints))
but I wouldn't be very surprised if gfortran (or any other compiler) generated a temporary array to implement this. I'd be more surprised if it didn't.
You might also try
forall (J=1:N, K=1:M) floats(K, J) = real(ints(J, K))
Again, I wouldn't be surprised if a compiler created a temporary array to implement this.
do i = 1, n
do j = 1, m
floats(j,i) = real(ints(i,j))
enddo
enddo
You could make your own transpose interface for handling different data types, although it would have to be a subroutine and not a function.
interface transpose_
module procedure transpose_ints_to_reals
end interface
subroutine transpose_ints_to_reals(ints_in, reals_out)
...
end subroutine
call transpose_(ints,floats)