We have covered calling C from Julia, and calling Fortran is not that different. Now it is not that uncommon for glue languages such as Python and Julia to be able to access existing code from other languages. It makes sense from the viewpoint of not having to actually re-engineer code that is known to work. However in Python you have to use something like f2py, the Fortran to Python interface generator. Or make the Fortran code callable from C and then bind that with Cython (see link). Seems like a pain right?
Julia makes this way easier, with no “wrappers” or anything similar required. Works in the same way as calling C code except for a couple of differences. So imagine a piece of Fortran code (contained in SG.f90) with three subroutines:
- savgol() – performs Savitzky-Golay smoothing on 1D data.
- ludcmp() – performs LU decomposition (called by savgol())
- lubksb() – solves a series of linear equations (called by savgol())
Now we want to create a piece of Julia code which calls savgol(), without the need to translate the code to Julia. The first thing that needs to happen of course is that the Fortran code needs to be compiled into an object file.
gfortran -shared -fPIC SG.f90 -o SG.so
This creates the object file SG.so. However if you try and use the function savgol in ccall, you will likely get an error of the form:
ERROR: ccall: could not find function savgol in library
This is because Fortran tends to mangle, or disfigure names. This has to do with case insensitivity in Fortran, and is only problematic in the sense that every compiler does things a little differently. In the case of GNU Fortran, it converts all characters to lowercase, and adds an underscore. If you aren’t sure, you can always check using the utility nm, which checks the symbol table (or name list) of an object file. So if it is applied to SG.so, we obtain:
>nm SG.so U ___powidf2 U __gfortran_st_write U __gfortran_st_write_done U __gfortran_transfer_array_write U __gfortran_transfer_character_write 0000000000000ecd T _lubksb_ 00000000000011cf T _ludcmp_ 000000000000189d T _savgol_ U dyld_stub_binder
You can see savgol has an underscore prepended to it as well, this can be ignored. Now it is possible to define the call to ccall() (covered in the next post).