Function in fortran, passing array in, receiving array out - fortran

I have this function, depicted below. It passes in two vectors with three values each, and should pass out one vector with three values as well. I call the function like this:
Fr = Flux(W(:,i),W(:,i+1))
What I have realized through messing around with the code, trying pure functions, and modules, and researching the error statement (that I will include at the bottom), is that fortran is reading my function Flux, and thinks that the input vectors are an attempt to call an entry from the array. That is my best guess as to what is going on. I asked around the lab and most people suggested using subroutines, but that seemed clunky, and I figured there should probably be a more elegant way, but I have not yet found it. I tried to define a result by saying:
DOUBLE PRECISION FUNCTION Flux(W1,W2) Result(FluxArray(3))
and then returning fluxarray but that does not work, as fortran cannot understand the syntax
The actual Function is this:
DOUBLE PRECISION FUNCTION Flux(W1,W2)
USE parameters
IMPLICIT NONE
DOUBLE PRECISION, DIMENSION(3), INTENT(IN)::W1, W2
DOUBLE PRECISION, DIMENSION(3), INTENT(OUT):: Flux
DOUBLE PRECISION, DIMENSION(3):: F1, F2
DOUBLE PRECISION::U1,U2,Rh1,Rh2,P1,P2,E1,E2,Rh,P,u,c,Lambda
INTEGER:: k
U1=W1(2)/W1(1)
U2=W2(2)/W2(1)
Rh1=W1(1)
Rh2=W2(1)
P1=(gamma_constant-1.d0)*(W1(3)-.5d0*Rh1*U1**2)
P2=(gamma_constant-1.d0)*(W2(3)-.5d0*Rh2*U2**2)
E1=W1(3)
E2=W2(3)
F1=[Rh1*U1,Rh1*U1**2+P1,(E1+P1)*U1]
F2=[Rh2*U2,Rh2*U2**2+P2,(E2+P2)*U2]
Rh=.5d0*(Rh1+Rh2)
P=.5d0*(P1+P2)
u=.5d0*(U1+U2)
c=sqrt(gamma_constant*P/Rh)
Lambda=max(u, u+c, u-c)
do k=1,3,1
Flux(k)=.5d0*(F1(k)+F2(k))-.5d0*eps*Lambda*(W2(k)-W1(k))
end do
RETURN
END FUNCTION Flux
Here is the error statement:
Quasi1DEuler.f90:191.51:
DOUBLE PRECISION, DIMENSION(3), INTENT(OUT):: Flux
1
Error: Symbol 'flux' at (1) already has basic type of REAL
Quasi1DEuler.f90:217.58:
Flux(k)=.5d0*(F1(k)+F2(k))-.5d0*eps*Lambda*(W2(k)-W1(k))
1
Error: Unexpected STATEMENT FUNCTION statement at (1)
Quasi1DEuler.f90:76.18:
Fr = Flux(W(:,i),W(:,i+1))
The last error occurs for both Fr and Fl. Thank you for your time and any help or consideration you can give!
EDIT/Follow-up::
Thanks for the help, I don't know a better way to present this so I'm going to edit the initial question.
I did as you suggested and It solved that issue, now it says:
Fr = Flux(W(:,i),W(:,i+1))
1
Error: The reference to function 'flux' at (1) either needs an explicit INTERFACE or the rank is incorrect
I saw a similar issue on SO at this link:
Computing the cross product of two vectors in Fortran 90
where they suggested that he put all his functions into modules. is there a better/simpler way for me to fix this error?

With RESULT(FluxArray), fluxArray is the name of the result variable. As such, your attempt to declare the characteristics in the result clause are mis-placed.
Instead, the result variable should be specified within the function body:
function Flux(W1,W2) result(fluxArray)
double precision, dimension(3), intent(in)::W1, W2
double precision, dimension(3) :: fluxArray ! Note, no intent for result.
end function Flux
Yes, one can declare the type of the result variable in the function statement, but the array-ness cannot be declared there. I wouldn't recommend having a distinct dimension statement in the function body for the result variable.
Note that, when referencing a function returning an array it is required that there be an explicit interface available to the caller. One way is to place the function in a module which is used. See elsewhere on SO, or language tutorials, for more details.
Coming to the errors from your question without the result.
DOUBLE PRECISION FUNCTION Flux(W1,W2)
DOUBLE PRECISION, DIMENSION(3), INTENT(OUT):: Flux
Here the type of Flux has been declared twice. Also, it isn't a dummy argument to the function, so as above it need not have the intent attribute.
One could write
FUNCTION Flux(W1,W2)
DOUBLE PRECISION, DIMENSION(3) :: Flux ! Deleting intent
or (shudder)
DOUBLE PRECISION FUNCTION Flux(W1,W2)
DIMENSION :: Flux(3)
The complaint about a statement function is unimportant here, following on from the bad declaration.

Related

Why do I always get the same result when using function and contains in fortran

I'm trying to do a basic calculation by calling a function using contains
Program main
implicit none
integer*8 Nmax,i
Parameter (Nmax=5)
real*8 x, f(Nmax), n
do i=1, Nmax
n=i
f=func(n,Nmax)
write(*,*) f(i)
end do
Contains
real*8 function func(x,Nmax)
integer*8 Nmax,i
real*8 x, f(Nmax)
do i=1, Nmax-1
f(i)=i**2d0-4d0*i-7d0
end do
end function
end program main
I get this result:
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
I think I'm making the wrong variable definitions. Thank you for your help.
There are multiple problems with your program.
First, you probably meant to write:
f(i)=func(n,Nmax)
in the main program. Without the subscript you assign the same value to each element of the array. You might think that explains the results, but it doesn't as you'd still see what you expect.
Another problem is highlighted by the following warning I get when I compile your code with Intel Fortran:
t.f90(14): warning #6178: The return value of this FUNCTION has not been defined. [FUNC]
real*8 function func(x,Nmax)
-------------------^
You never assign the value of func, so you get whatever garbage happens to be in the return register.
The function you have isn't really what you want, either. You probably want one that computes and returns a scalar (single) value and hence there is no need for an array inside func.
A third problem is that func is ignoring the n argument (which, contrary to convention, you have declared as a real.)
If you want a loop in the main program, have the function compute and return a single result based on the argument passed to it. There is no need to pass both the loop index and nmax each time. Other options, slightly more advanced, would be to keep the array assignment in the main program but do away with the loop there and either have the function return an array or make the function ELEMENTAL. I will leave it as an exercise for you once you figure out what you really intend here.
Lastly, I would discourage you from using nonstandard syntax such as "real*8". Please learn about KIND specifiers and the SELECTED_REAL_KIND intrinsic function.

Fortran 90 logical kind mismatch

I'm trying to pass an argument declared simply as logical :: invar
to a function where the receiving variable is declared as
logical(x) :: invar
Now x is defined as
INTEGER, PARAMETER :: x = KIND(.TRUE.)
What does that definition of x mean? I did a search for kind(.true.) but all results kind of brush this aspect aside. Would appreciate some clarification for an expert.
I'm using the Intel compiler, if this has something to do with the compiler.
Variables such as real numbers, integers, and even logicals can be different kinds. Typically, this is important to distinguish between single precision and double precision reals, for example. In my experience, there's no reason to fiddle with the kind of a logical.
Whoever wrote this code, obviously, thinks otherwise. When you declare a logical in the usual way, with
logical :: L1
the variable my_logical is of the default kind. When you declare it using
integer, parameter :: x = KIND(.TRUE.)
integer(x) :: L2
it has the kind of x, which is defined to be the kind of .true.. The tricky part is that .true. is almost certainly also of the default kind. (The standard requires that the default kind is the kind of .FALSE.)
So, in the examples above, L1 and L2 are of the same kind. I don't know why somebody would bother with defining the default logical kind as x, but you shouldn't worry about it.

Fortran: Variables changing on their own

I'm having a problem of variables getting over-written for I don't know what reason. I've posted a chunk of the code below so you can see how things are declared. The variables strain,Qi,Qf,Qd,tel and Gc are passed into the subroutine and used to calculate ssgrad,strn0,strss0.
My problem is that tel and Gc are passed into the subroutine OK but are for some reason change value during this chunk of code.
Using print statements I've found that the problem first occurs during the 2nd do loop. When I set strss0 to 0, Gc and tel change value from both being equal to 1, to seemingly random numbers: tel=11.52822 Gc=-8.789086 (Just shown for the sake of example)
Each time I run the code they are set to the same values though.
Just to let you know, this subroutine interfaces with a commercial finite element package.
Many thanks in advance for any help with this
subroutine initcalcs(strain,Qi,Qf,Qd,tel,Gc,ssgrad,strn0,strss0)
implicit none
integer :: i,j
real*8:: nstrn0,nstrs0,strn0,strnf,varsq,normvar,lmbda0,lmbdaf,
# ssgrad,t0,tt,tel,nstrnf,nstrsf,Gc
real*8, dimension(3) :: strain,stran0,stranf,strss0,strssf,var
real*8, dimension(3,3) :: Qd,Qi,Qf
lmbda0=1.0d0
nstrn0=0.0d0
do i=1,3
stran0(i)=0.0d0
stran0(i)=strain(i)*lmbda0
nstrn0=nstrn0+stran0(i)**2
end do
nstrn0=dsqrt(nstrn0)
do i=1,3
strss0(i)=0.0d0
end do
In Fortran, there are two common causes of the corruption of memory values. One is a subscript error, where you assign to an array element using an incorrect subscript value. This writes to a memory location outside of the array. The other is a disagreement between the arguments in the call to a procedure (subroutine or function) and the dummy arguments of the procedure. Either can cause problems to appear at source code locations different from the actual cause. Suggestions: inspect your code for these problems. Turn on stringent warning and error checking options of your compiler. The use of Fortran >=90 and modules gives Fortran much better ability to automatically find argument consistency problems. You could monitor the memory locations with a debugger and see what it modifying it.
I concur with M. S. B.: turn on stringent warnings and error checking and verify the subroutine calls are passing arguments that have the same type and shape (array dimensions) as the subroutine expects.
The colons in the variable declaration syntax imply this is Fortran90 or later. If that's the case, I strongly suggest using the INTENT modifier to specify whether arguments are intended to be read-only.
For example, let's assume that of the arguments passed to this routine, strain, Qi, Qf, Qd, tel, and Gc are read-only input and the arguments are ssgrad, strn0, and strss0 are returned as output; that is, whatever value they have is overwritten by this routine.
The variable declarations for the arguments would change to:
real*8, dimension(3), intent(in) :: strain
real*8, dimension(3,3), intent(in) :: Qi, Qf, Qd
real*8, intent(in) :: tel, Gc
real*8, intent(out) :: strn0, ssgrad
real*8, dimension(3), intent(out) :: strss0
The INTENT keyword is an addition to Fortran 90 which allows the user to specify which arguments are read-only (INTENT(IN)), initialized but which may be modified within the routine (INTENT(INOUT)) and which are treated as uninitialized and will be set within the routine (INTENT(OUT)).
If INTENT is not specified, it is defaults to INOUT which is consistent with FORTRAN 77 (Note that there are minor differences between INTENT(INOUT) and INTENT not being specified but they aren't relevant in this example).
A good compiler will throw an error if a routine tries to assign a value to a variable declared INTENT(IN) and will at least throw a warning if a variable declared INTENT(OUT) doesn't get assigned a value.
If possible, set INTENT(IN) on all the variables which are supposed to be read-only. This may not be possible, depending on how those variables are passed to other routines. If INTENT isn't specified on arguments to routines called within this routine, it will default to INOUT. If you pass an INTENT(IN) variable as an INTENT(INOUT) argument, the compiler will throw an error. If this is happening in code you control, you have have to specify INTENT in a number of routines. This may or may not be desirable depending on whether you want to generally improve your code or just fix this one problem really quickly.
I'm assuming some of these variables are passed to external routines in the finite element package which I 'm guessing is linked to your code rather than compiled; I'm not sure how compile-time intent checking is handled in that case.

Convert logical type to double in Fortran

I'm looking for a bulletproof way of converting logical type variables to real type that will work in both ifort and gfortran. The following works in ifort, but not in gfortran:
logical :: a
real :: b
a = .true.
b = dble(a)
The error thrown in gfortran is
b = dble(a)
1
Error: 'a' argument of 'dble' intrinsic at (1) must be a numeric type
Obviously, .true. should map to 1.d0, and .false. to 0.d0. What's the best way of doing this?
In addition to writing a function to handle this, you could also directly use the intrinsic merge function: b = merge(1.d0, 0.d0, a). Or you could write a defined assignment subroutine that does this, so that you can just type b = a.
I am not sure if there is an intrinsic tool that does this. I do not know why ifort accepts this, and my guess would be that it is a compiler specific functionality.
Edit: As pointed out in https://stackoverflow.com/a/15057846/1624033 below, there is the intrinsic merge function which is exactly what is needed here.
an option to to this, specifically since you want this to be bullet proof, is to create your own function.
I have not tested this, but the following might work:
elemental pure double precision function logic2dbl(a)
logical, intent(in) :: a
if (a) then
logic2dbl = 1.d0
else
logic2dbl = 0.d0
end if
end function logic2dbl
Edit: I added elemental to the function declaration based on advice below. I also added pure to this function as it adds the extra ability to use this in parallel situations and it is good documentation. This however is just my opinion and it is not necessary.
In gfortran, I am using the TRANSFER intrinsic for that type of job.
Assuming a integer variable my_int then:
my_int = transfer(.false.,my_int)
the result of my_int is 0 as expected.
Just a note, TRANSFER(.true.,1) correctly returns the value of 1 with Gfortran and (incorrectly?) values of -1 with the current versions of Intel and Portland compilers. Interestingly, TRANSFER(-1,logical) returns TRUE with the latter two compilers while throws a syntax error with Gfortran.

FFTW: Trouble with real to complex and complex to real 2D tranfsorms

As the title states I'm using FFTW (version 3.2.2) with Fortran 90/95 to perform a 2D FFT of real data (actually a field of random numbers). I think the forward step is working (at least I am getting some ouput). However I wanted to check everything by doing the IFFT to see if I can re-construct the original input. Unfortunately when I call the complex to real routine, nothing happens and I obtain no error output, so I'm a bit confused. Here are some code snippets:
implicit none
include "fftw3.f"
! - im=501, jm=401, and lm=60
real*8 :: u(im,jm,lm),recov(im,jm,lm)
complex*8 :: cu(1+im/2,jm)
integer*8 :: planf,planb
real*8 :: dv
! - Generate array of random numbers
dv=4.0
call random_number(u)
u=u*dv
recov=0.0
k=30
! - Forward step (FFT)
call dfftw_plan_dft_r2c_2d(planf,im,jm,u(:,:,k),cu,FFTW_ESTIMATE)
call dfftw_execute_dft_r2c(planf,u(:,:,k),cu)
call dfftw_destroy_plan(planf)
! - Backward step (IFFT)
call dfftw_plan_dft_c2r_2d(planb,im,jm,cu,recov(:,:,k),FFTW_ESTIMATE)
call dfftw_execute_dft_c2r(planb,cu,recov(:,:,k))
call dfftw_destroy_plan(planb)
The above forward step seems to work (r2c) but the backward step does not seem to work. I checked this by differencing the u and recov arrays - which ended up not being zero. Additionally the max and min values of the recov array were both zero, which seems to indicate that nothing was changed.
I've looked around the FFTW documentation and based my implementation on the following page http://www.fftw.org/fftw3_doc/Fortran-Examples.html#Fortran-Examples . I am wondering if the problem is related to indexing, at least that's the direction I am leaning. Anyway, if any one could offer some help, that would be wonderful!
Thanks!
Not sure if this is the root of all troubles here, but the way you declare variables may be the culprit.
For most compilers (this is apparently not even a standard), Complex*8 is an old syntax for single precision: the complex variable occupies a total of 8 bytes, shared between the real and the imaginary part (4+4 bytes).
[Edit 1 following Vladimir F comment to my answer, see his link for details:] In my experience (i.e. the systems/compiler I ever used), Complex(Kind=8) corresponds to the declaration of a double precision complex number (a real and an imaginary part, both of which occupy 8 bytes).
On any system/compiler, Complex(Kind=Kind(0.d0)) should declare a double precision complex.
In short, your complex array does not have the right size. Replace occurences of Real*8 and Complex*8 by Real(kind=8) and Complex(Kind=8) (or Complex(Kind=kind(0.d0)) for a better portability), respectively.