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
valuespecifies that this variable uses call by value, which is the standard for C (but not Fortran). - I define
outwith the indices reversed compared tocalc_num_iteras Frotran uses column-major and C row-major array storage. I then transpose the result ofcalc_num_iterto 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.