Calling routines from a Fortran Library using Python

March 28th, 2008 | Categories: Linux, programming, python | Tags:

Say you have just joined an applied-maths research group* where you are expected to write a lot of numerical simulations – numerical simulations that are going to require the use of big computers in large, air conditioned rooms with a great deal of impressive looking flashenlightenblinken. Naturally, you would like to use Python for all of your programming needs as you have recently fallen in love with it and you are convinced that it’s the future.

Your boss, Bob, completely disagrees with your take on the future of programming. He thinks that Python is a passing fad that will soon be forgotten. He sneeringly refers to it as a ‘ mere scripting language’ and constantly refers to you as the script kiddie. He thinks that Fortran is the only programming language worth bothering with when it comes to numerical simulations. According to Bob, Fortran is the past, present and future of computer programming – everything else is just mucking about.

You argue constantly with Bob concerning the relative merits of the two languages because, although you respect Fortran, you don’t think that it’s going to be much fun to use. Eventually he pulls out his trump card – “You can’t use Python in this group because all of our screamingly fast, highly accurate, tested and debugged numerical routines are written in Fortran. We won’t use any other libraries because these are the best so – give up on Python and start learning Fortran or get another job.”

Defeated…or so you thought…

After a bit of googling you realize that there is light at the end of the tunnel, this problem has been solved before by making use of the Python ctypes module. First of all let’s install this module on Ubuntu:

sudo apt-get install python-ctypes

Taking our cue from this solution lets say that one of the functions in the Fortran library is called ADD_II and has the following source code (filename add.f)

SUBROUTINE ADD_II(A,B)
INTEGER*4 A,B

A = A+B
END

Compile it into a shared library using gfortran as follows:

gfortran add.f -ffree-form -shared -o libadd.so

Now, create a file called add.py and copy the source code from our friendly usenet poster:

from ctypes import *
libadd = cdll.LoadLibrary(“./libadd.so”)
#
# ADD_II becomes ADD_II_
# in Python, C and C++
#
method = libadd.ADD_II_
x = c_int(47)
y = c_int(11)
print “x = %d, y = %d” % (x.value, y.value)
#
# The byref() is necessary since
# FORTRAN does references,
# and not values (like e.g. C)
#
method( byref(x), byref(y) )
print “x = %d, y = %d” % (x.value, y.value)

run the script as follows:

python add.py

and get the following error message:

AttributeError: ./libadd.so: undefined symbol: ADD_II_

So, despite what we may have thought, it looks like our function has not been given the name ADD_II_ in the shared library. So what name has it been given? We could just keep guessing what the compiler might have called it or we could just ask the library itself using the nm comand:

nm libadd.so

00001468 a _DYNAMIC
00001554 a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
00001458 d __CTOR_END__
00001454 d __CTOR_LIST__
00001460 d __DTOR_END__
0000145c d __DTOR_LIST__
00000450 r __FRAME_END__
00001464 d __JCR_END__
00001464 d __JCR_LIST__
00001570 A __bss_start
w __cxa_finalize@@GLIBC_2.1.3
00000400 t __do_global_ctors_aux
00000340 t __do_global_dtors_aux
00001568 d __dso_handle
w __gmon_start__
000003d7 t __i686.get_pc_thunk.bx
00001570 A _edata
00001574 A _end
00000434 T _fini
000002d8 T _init
000003dc T add_ii_
00001570 b completed.6030
000003a0 t frame_dummy
0000156c d p.6028

Now I have no idea what most of that output means but it looks like the .so file contains something called add_ii_ so if I use this instead of ADD_II_ in my python script I bet it will work.

python add.py

x = 47, y = 11
x = 58, y = 11

The sweet smell of success. You go to Bob and tell him that you have just come up with a test script that demonstrates that you are going to be able to use the group’s Fortran libraries in your Python scripts.

“That’s very nice” says Bob “but all of the really useful routines in the library make use of callback functions. Can you handle those yet?”

“yes I can” you reply smugly “but this post has gone on long enough so I’ll leave the details until another time”

*Note – In case you are my boss – I haven’t joined a research group so I won’t be quitting my job any day soon. I was just in a story telling mood. Oh..and this stuff will be useful for what we do – I promise!

  1. Moses O.
    January 11th, 2011 at 17:52
    Reply | Quote | #1

    Hey. I have just stumbled upon your page. Nice story telling and a nice example. However this did not work on my machine. Instead of typing
    “libadd = cdll.LoadLibrary(“./libadd.so”)”
    I had to type :
    libadd = CDLL(“./libadd.so”)
    Hope, this would also be helpful for others as well!
    Regards,
    Moses