is it possible to have function pointers in Fortran? Right now I have a code with some lines like this:
subroutine flag(iflag,a,b)
integer n, a, b, ii, iflag
do ii = 1, n
if (iflag.eq.0) a+b
else a-b
end do
return
end
The variable "n" has a huge value and so to run this code, I feel like I am wasting a lot of time with the "if" command. Is it possible to write something like a function pointer (I am not sure what I mean by that), such that at the code does something like this:
subroutine flag(iflag,a,b)
*depending on the iflag the subroutine flag is automatically
precompiled to call either flag_plus or flag_minus*
return
end
subroutine flag_plus(a,b)
integer n, a, b, ii
do ii = 1, n
a+b
end do
return
end
subroutine flag_minus(a,b)
integer n, a, b, ii
do ii = 1, n
a-b
end do
return
end
If it is possible I can save a lot of time by avoiding the "if" loop. Is anything like this remotely possible?
In Fortran 77 you can have a limited variant of function pointers, namely that you can pass the name of a procedure as an argument to another procedure (a function pointer, essentially). You cannot have a variable that contains the address of a procedure and then "call" that variable, though.
As of Fortran 2003, procedure pointer variables are part of the language.
That being said, I think your example problem could be solved even easier by something like
if (iflag == 0) then ! Why is iflag not of type logical?
do ii = 1, n
a + b
end do
else
do ii = 1, n
a - b
end do
end if
Make sure to profile it to see whether it has any effect, as well. Modern CPU's have pretty good branch predictors, and a branch test which doesn't change during the entire loop is pretty much the best case scenario (rule of thumb: predicted branches are close to free). Heck, your compiler might even be able to do the above kind of optimization..
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
I want to write a subroutine which could be used in 1D or 2D geometry.
Consequently I have variables that will be used only in the latter case.
I wish I could write a declaration section in that style:
real ( kind = double ) :: uleft,vleft,uright,vright
real ( kind = double ) :: distanceinx
real ( kind = double ) :: ...
if (ndim == 2) then
real ( kind = double ) :: ulow,vlow,uup,vup
real ( kind = double ) :: distanceiny
end if
but it doesn't compile.
Is there a syntax to do what I want to do in a proper way ?
A big part of the code will be similar in the two cases (ndim = 1 or 2) so I think that makes sense.
Just leave the variables there and don't use them in 1D. The syntax you propose does not exist and I don't know of any other similar one.
In Fortran 2003 you can also make them allocatable, but that is worth it only for arrays, to save some space.
Do not care about couple of unused bytes on the stack. They can remain unused in 1D.
You could treat the 1D array as a special two-dimensional array with Nx1. Then you could always use the 2D version of your code. You can still check for the length of the second dimension to determine the dimension. This even allows for extension to the third dimension!
Routines/functions like norm2 also work on both types, so this could further simplify your code.
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).
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)