Calling Fortran from C
Eventually, I want to be able to call Fortran from Python. However, as a stepping stone, I first call Fortran from C and then use that for the next step. As an example I'll be using a fractal calculating program. The code can be accessed with
git clone https://maurow@bitbucket.org/maurow/mauro_learning_fortran.git cd mauro_learning_fortran git checkout v0.1
and is in the folder mandel/
.
The Fortran side
The function calc_num_iter
in Fortran module mandel
computes
whether the points in the grid (re,im)
are part of the Mandelbrot
set.
mandel.f90
:
module mandel implicit none integer, parameter :: dp=kind(0.d0) ! double precision contains pure function mandel_frac(z, c) result(out) ! The Mandelbrot function z -> z^2 + c complex(dp), intent(in):: z, c complex(dp):: out out = z**2 + c end function mandel_frac pure function calc_num_iter(re, im, itermax, escape) result(out) ! Iterates on mandel_frac real(dp), intent(in):: re(:), im(:), escape integer, intent(in):: itermax integer:: out(size(re), size(im)) integer:: ii, jj, kk, itt complex(dp):: zz, cc do ii=1,size(re) do jj=1,size(im) zz = 0 cc = cmplx(re(ii), im(jj), dp) itt = 0 do kk=1,itermax zz = mandel_frac(zz, cc) itt = kk if (abs(zz)>escape) then exit end if end do out(ii,jj) = itt end do end do end function calc_num_iter end module mandel
This can, of course, be used in Fortran like so:
run_mandel_from_fortran.f90
:
program mandelbrot use mandel implicit none integer, parameter :: nre=31, nim=21 real(dp), parameter :: rer(2)=[-2._dp, 1._dp], imr(2)=[-1._dp,1._dp] integer :: ii, jj, kk, out(nre,nim), itermax=99 real(dp) :: re(nre), im(nim), escape=2._dp complex(dp) :: zz re = [ (dble(ii)/(nre-1)*(rer(2)-rer(1)) + rer(1) , ii=0, nre-1, 1) ] im = [ (dble(ii)/(nim-1)*(imr(2)-imr(1)) + imr(1) , ii=0, nim-1, 1) ] out = calc_num_iter(re, im, itermax, escape) ! plot array as x/y coordinates do jj=nim,1,-1 print '(30000I3.2)', out(:,jj) end do end program mandelbrot
Compile it with
gfortran mandel.f90 run_mandel_from_fortran.f90 -o fout
The wrapper
To make the function calc_num_iter
available in C it needs to be
wrapped using the iso_c_binding
module (in the Fortran 2003
standard). Resources for the dumb Fortran programmer, like myself,
are a bit sparse (e.g. gfortran docs). The fortran wrapper is
mandel_wrap.f90
:
module mandel_wrap ! to wrap calc_num_iter for use in C use iso_c_binding, only: c_double, c_int use mandel, only: calc_num_iter implicit none contains ! need to make a subroutine as only scalars can be returned subroutine c_calc_num_iter(nre, re, nim, im, itermax, escape, out) bind(c) real(c_double), intent(in):: re(nre), im(nim) real(c_double), intent(in), value:: escape integer(c_int), intent(in), value:: itermax, nre, nim ! note that in C the indices will be reversed!: integer(c_int), intent(out):: out(nim, nre) ! thus the transpose here: out = transpose(calc_num_iter(re, im, itermax, escape)) end subroutine c_calc_num_iter end module mandel_wrap
There are a few things to note:
bind(c)
needs to be appended to the wrapper function- the
c_
variable types are needed to make them C interoperable - The attribute
value
specifies that this variable uses call by value, which is the standard for C (but not Fortran). - I define
out
with the indices reversed compared tocalc_num_iter
as Frotran uses column-major and C row-major array storage. I then transpose the result ofcalc_num_iter
to make it fit intoout
.
The C side
Now it is possible to call c_calc_num_iter
from C.
run_mandel_from_c.c
:
#include <stdio.h> #include <math.h> #define NRE 31 #define NIM 21 int main ( void ) { int i, j, itermax=99; // NOTE: index not reversed! int res[NRE][NIM]; double re[NRE], im[NIM], escape=2., dx, dy; // make real and imaginary vectors dx = 3.0/(NRE-1); re[0] = -2.; for (i=1; i<NRE; i++) re[i] = re[i-1] + dx; dy = 2.0/(NIM-1); im[0] = -1.; for (i=1; i<NIM; i++){ im[i] = im[i-1] + dy; if (im[i]<1e-6 && im[i]>-1e-6) im[i] = 0.; // otherwise result is not equal to Fortran's } //printvec(NRE, re); //printvec(NIM, im); // call to fortran c_calc_num_iter(NRE, re, NIM, im, itermax, escape, res); // print result printarr(NRE, NIM, res, re, im); return 0; }
This needs to be compiled:
gcc -c run_mandel_from_c.c mandel.f90 mandel_wrap.f90
Produces the object files *.o
, which need to be linked into an
executable with
gfortran run_mandel_from_c.o mandel.f90 mandel_wrap.f90 -o cout
Note the use of gfortran
here. Running the same command using gcc
does not work as the linker does not find the Fortran functions.
Probably some flags for gcc
could solve this?
Next I call c_calc_num_iter
from python through a cython interface.