I currently need to wrap Fortran subroutines into Python. I want to do it using Cython, and I'm working on smaller tasks for now to get some basic understanding of what I'm doing before working on the actual scripts, as they are very large involving dozens of derived types and subroutines.
I'm very new to Fortran, C, and Cython, and I'm trying to wrap a basic derived type and subroutine. However when I try to compile, I get errors as my derived type is "not interoperable".
The initial module can be found below: "summation.f90"
module gfunc_module
implicit none
type :: geo
real, dimension(1:2) :: coordinates
real :: weight
end type
contains
subroutine gfunc(v1, v2, final_v)
type(geo), intent(in) :: v1, v2
type(geo), intent(out) ::final_v
final_v%coordinates = v1%coordinates * v1%weight + v2%coordinates + v2%weight
final_v%weight = v1%weight + v2%weight
end subroutine
end module
Then, I wrap it using iso_c_binding: (pysummation.f90)
module gfunc1_interface
use iso_c_binding
use gfunc_module
implicit none
type, bind(c) :: geo
real(c_float), dimension(1:2) :: coordinates
real(c_float) :: weight
contains
subroutine c_gfunc(v1, v2, final_v) bind(c)
type(geo), intent(in) :: v1, v2
type(geo), intent(out) :: final_v
call gfunc(v1, v2, final_v)
end subroutine
end module
Then, I write a header file: (pysummation.h)
struct geo {
float coordinates[2];
float weight;
}
extern void c_gfunc(geo *v1, geo *v2, geo *final_v);
My cython file (pysummation.pyx):
cdef extern from "pysummation.h":
cdef struct geo:
float coordinates[2]
float weight
void c_gfunc(geo *v1, geo *v2, geo *final_v)
def f(geo v1, geo v2):
cdef:
geo value
c_gfunc(<geo*>&v1, <geo*>&v2, <geo*>&value)
return value
And finally, my setup file (setup.py):
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
# This line only needed if building with NumPy in Cython file.
from numpy import get_include
from os import system
# compile the fortran modules without linking
fortran_mod_comp = 'gfortran summation.f90 -c -o summation.o -O3 -fPIC'
print fortran_mod_comp
system(fortran_mod_comp)
shared_obj_comp = 'gfortran pysummation.f90 -c -o pysummation.o -O3 -fPIC'
print shared_obj_comp
system(shared_obj_comp)
ext_modules = [Extension(# module name:
'pygfunc',
# source file:
['pysummation.pyx'],
# other compile args for gcc
extra_compile_args=['-fPIC', '-O3'],
# other files to link to
extra_link_args=['summation.o', 'pysummation.o'])]
setup(name = 'pygfunc',
cmdclass = {'build_ext': build_ext},
# Needed if building with NumPy.
# This includes the NumPy headers when compiling.
include_dirs = [get_include()],
ext_modules = ext_modules)
When I try to compile my scripts, I get the following errors:
- Derived type definition of 'geo' at (1) has already been defined.
I know that because I told Fortran to use my module, which already defined the "geo" type, but I thought that because I wanted to use iso_c_binding, I'd have to redefine it.
The second error I get is
- Variable 'v1' is a dummy argument to the BIND(C) procedure, but is not c interoperable because derived type 'geo' is not C interoperable.
This occurs for all variables of type(geo). I'm just wondering if someone could shed some light on how c interoperability works with regards to arrays and derived types?
I recognize that there are probably dozens of other errors in my code. However I'd like to at least work through these few before I start troubleshooting others.
So, to summarize, in python you are trying to bind to a c-function, that again is bound to a fortran function? Right? And at the moment you are having trouble creating the c-wrapper to the fortran function? Right?
I am not very familiar with fortran, but it seems that the fortran compiler is complaining about two different definitions of the type
geo. And indeed you do have two definitions. One insummation.f90, and one inpysummation.f90. I would recommend to rename the one inpysummation.f90toc_geo. That also means that the parametersv1,v2,final_vofc_gfuncshould be of typec_geoand notgeo. And naturally you have to convertv1andv2togeobefore passing them togfunc, and then convert the return value back.As I said, I don't write fortan, so this code is more pseudo code rather than a correct fortran program:
Some other hints:
Your
pysummation.his wrong if you expect it to be readable by a c-compiler.structkeyword is mandatory when declaring variable/parameters of type struct:If
v1andv2are not supposed to modify what they are pointing at, I would also addconstkeywords:And finally:
I would also recommend that you test your c-wrapper in c before you try to re-wrap it for python.