15

I would like to call some functions from a Fortran shared library in Python. I have found some links on the net and read them, and according what I found, I should do

libadd = cdll.LoadLibrary('./libbin.so') 

to load the shared object. However, this shared object includes some symbols from another shared library. I read the help of cdll however it does not seem possible to load several shared object files at the same time. How may I call functions from this Fortran library, which is most probably compiled by the Intel Fortran compiler?

M. S. B.
  • 28,968
  • 2
  • 46
  • 73
Umut Tabak
  • 1,862
  • 4
  • 26
  • 41

3 Answers3

21

You'll need to know the signatures of the functions in the shared object. Do you have the source code, or some reference which explains the function names and argument types?

For example, I have this source code (mult.f90):

integer function multiply(a, b)
    integer, intent(in) :: a, b
    multiply = a * b
end function multiply

.. and to demonstrate how you can load and use multiple shared objects at once, I also have (add.f90):

integer function addtwo(a, b)
    integer, intent(in) :: a, b
    addtwo = a + b
end function addtwo

Compile, examine symbols:

% gfortran-4.4 -shared -fPIC -g -o mult.so mult.f90
% gfortran-4.4 -shared -fPIC -g -o add.so add.f90
% nm -ao mult.so | grep multiply
mult.so:00000000000005cc T multiply_

Notice the symbol name in the shared object has an underscore appended. Since I have the source, I know that the signature is multiply_(int *a, int *b), so it is easy to invoke that function from ctypes:

from ctypes import byref, cdll, c_int

mult = cdll.LoadLibrary('./mult.so')
add = cdll.LoadLibrary('./add.so')
a = c_int(2)
b = c_int(4)
print mult.multiply_(byref(a), byref(b))
print add.addtwo_(byref(a), byref(b))

Output:

8
6
samplebias
  • 37,113
  • 6
  • 107
  • 103
  • Thanks for the wonderful answer. I have a quick followup question. When I inspect the namelist for the libraries (`nm mult.so`), it shows `_multiply_` rather than `multiply_`, although the latter still works in C and python. Do you know why this is the case? – marshall.ward Apr 29 '11 at 02:21
  • You're welcome. Different compilers may prepend/append underscores to the symbol names. I know in gfortran's case you can provide `-fno-underscoring` to turn this off; resulting in the symbol `multiply` for that function. Your compiler+platform may have this option. I'll update the answer with another possible solution if you're dealing with a shared object of unknown origin. – samplebias Apr 29 '11 at 02:56
  • Actually, I'll hold off further comment I hear back on the compiler option as that may fix the issue for you. Bottom line is you need to know the signature of the function you're calling, so you want to minimize the guesswork by correcting things upstream. – samplebias Apr 29 '11 at 03:15
  • @samplebias I should have mentioned that I was using OS X. The leading underscores don't appear in Linux libraries, so it is probably an OS X (or BSD) thing. Since this is outside the scope of the question, I should probably leave it at that. Thanks for looking into it. – marshall.ward Apr 29 '11 at 05:30
  • @samplebias: there are some symbols that are referenced in one of the shared object files that are still undefined. And theses are located in another object file however trying load that shared object gives an error that the object file is not recognized, have any idea? – Umut Tabak Apr 29 '11 at 07:11
  • @Umut Hmm, sounds like it may not have been compiled correctly for your platform, e.g. not using `-shared` and `-fPIC`, maybe wrong cpu architecture for your platform, etc. Hard to tell from here. Another issue you may run into down the road: when you're loading a shared object from a local directory, and that .so is linked to other shared objects, you need to set `LD_LIBRARY_PATH` so the loader can find all required libs and link them into the executable. – samplebias May 04 '11 at 23:57
5

I would add to @sameplebias answer, that one can use the iso_c_binding module to force (any) fortran compiler to produce the correct C signature. Example of usage:

module fmesh_wrapper

use iso_c_binding, only: c_double, c_int
use fmesh, only: mesh_exp

implicit none

contains

subroutine c_mesh_exp(r_min, r_max, a, N, mesh) bind(c)
real(c_double), intent(in) :: r_min
real(c_double), intent(in) :: r_max
real(c_double), intent(in) :: a
integer(c_int), intent(in) :: N
real(c_double), intent(out) :: mesh(N)
call mesh_exp(r_min, r_max, a, N, mesh)
end subroutine

! wrap more functions here
! ...

end module

this will have the following C signature:

void c_mesh_exp(double *r_min, double *r_max, double *a, int *N,
        double *mesh);

and then you can call it from Python as usual. The advantage of this approach is that it works on all platforms (without using any special compiler options).

Ondřej Čertík
  • 780
  • 8
  • 18
1

For f2py (from NumPy) to work, borrow both the mult.f90 and add.f90 examples from @samplebias. From a shell, compile the Python importable shared libraries:

f2py -c -m mult mult.f90
f2py -c -m add add.f90

Now use them in Python:

>>> import add
>>> import mult
>>> add.addtwo(4, 5)
9
>>> mult.multiply(4, 5)
20
Mike T
  • 41,085
  • 18
  • 152
  • 203