Reputation: 2743
When I call the following function, it returns 1
as expected:
integer function my_func() result(myresult)
myresult = 1
end function my_func
But when I modify the name of the return value to start with the letter "r" then the function returns 0
.
integer function my_func() result(rresult)
rresult = 1
end function my_func
What is causing this? My first thought was that it related to implicit typing, but the function is within a module that specifies implicit none
.
Here is the complete module
module my_mod
implicit none
contains
integer function my_func() result(myresult)
myresult = 1
end function my_func
end module my_mod
I am using Fortran 90 and compiling with gfortran.
EDIT
Here is a complete program to demonstrate the problem
Makefile:
.PHONY: pytest clean
CYTHON_LIB = fortran_mods.cpython-37m-x86_64-linux-gnu.so
FFLAGS += -fdefault-real-8
pytest: $(CYTHON_LIB)
./tests.py
$(CYTHON_LIB): my_mod.F90
f2py -c -m fortran_mods my_mod.F90 --f90flags="$(FFLAGS)"
clean:
rm *.so
my_mod.F90:
module my_mod
implicit none
contains
!********************************************************
integer function my_func_without_r() result(myresult)
myresult = 1
end function
integer function my_func_with_r() result(rresult)
rresult = 1
end function
end module my_mod
tests.py
#!/usr/bin/env python3
import fortran_mods
from fortran_mods import *
print("with r:", my_mod.my_func_with_r())
print("without r:", my_mod.my_func_without_r())
The output when make pytest
is run and FFLAGS += -fdefault-real-8
is included in the Makefile is
with r: 0.0
without r: 1
and otherwise is
with r: 1.0
without r: 1
Upvotes: 3
Views: 787
Reputation: 1263
The problem here is definitely with how F2PY wraps the Fortran functions, not the Fortran code itself.
To gain a bit more insight into how F2PY wraps a function (especially if things don't work out as expected), it always helps to split the process into parts (see The smart way). So first create a signature file allowing you to see how F2PY interpreted your code. For your particular example, run:
f2py -m fortran_mods -h my_mod.pyf my_mod.F90
This will produce a signature file my_mod.pyf
looking something like this:
python module fortran_mods ! in
interface ! in :fortran_mods
module my_mod ! in :fortran_mods:my_mod.F90
function my_func_without_r() result (myresult) ! in :fortran_mods:my_mod.F90:my_mod
integer :: myresult
end function my_func_without_r
function my_func_with_r() result (rresult) ! in :fortran_mods:my_mod.F90:my_mod
real :: rresult
end function my_func_with_r
end module my_mod
end interface
end python module fortran_mods
Clearly F2PY has misidentified my_func_with_r
's result variable rresult
as real
. You could simply replace the real :: rresult
with the intended integer :: rresult
in my_mod.pyf
, take the next/second step of F2PY wrapping, and compile using the corrected signature file:
f2py -c my_mod.pyf my_mod.F90
Your python script should now give the expected output.
This approach of modifying the signature file may not be desired if you have many functions to wrap. What may be causing difficulty for F2PY is that your function definitions use result variables, without type definitions for them appearing in the body of the function (i.e. a F2PY problem, not a Fortran problem). If you change your function definitions to e.g.:
function my_func_with_r() result(rresult)
integer :: rresult
rresult = 1
end function
or
integer function my_func_with_r()
my_func_with_r = 1
end function
you can do the F2PY wrapping in one step as you did originally, and should still get the correct output.
Finally, I'll add another vote to the concerns raised in the comments: using -fdefault-real-8
when wrapping functions with F2PY is asking for trouble. To make a Fortran procedure callable from Python, F2PY creates:
a Python C/API extension module (called as wrapper module) that implements a Python extension function (written in C, called as wrapper function) which in turn calls the given Fortran procedure.
This whole process is based on how F2PY interprets data types from your Fortran source code - if source code is compiled with compiler flags that change the declared data types, things are bound to end up broken (directly similar to the real
/integer
mismatch your original question is about). The data types for your variables and functions should therefore be explicitly set in the Fortran code itself, see here and here about Fortran's kind
parameter in general, and here related to F2PY in particular.
Upvotes: 6