Calling Fortran subroutines from R
February 2020
Back to index.html.
Calling Fortran subroutines from R
Steps
The R language can call a Fortran subroutine as follows.
- Prepare a Fortran subroutine as an external subroutine.
- Compile the program to generate shared library (or shared object).
- Load the shared library in R.
- Run the R program to call the subroutine and to receive the values from the subroutine.
Note that a Fortran function is not supported by R. For details, see the section of Foreign Function Interface in the official documentation available online.
Note that, as I mention at the bottom of this chapter, calling Fortran may not be an optimal solution. Please read through this article to figure out if Fortran solves your issue or not.
Fortran subroutine
There are several restrictions on the Fortran subroutine to be called in R.
- You can only use scalars and arrays as arguments. A derived-type variable can be defined inside the subroutine, but you can not use it as an argument.
- When you pass an array as the argument, you should also provide the size of the array as extra arguments. In other words, you cannot use a shape-assumed array.
You can put as many subroutines as possible in a source file. See the following example, assuming these subroutines saved in fortsub.f90
.
! acceptable
subroutine minus1(m,n,x)
integer,intent(in) :: m,n
double precision,intent(inout) :: x(m,n)
x = x - 1.0
end subroutine minus1
! not supported
subroutine minus1_wrong(x)
double precision,intent(inout) :: x(:,:)
x = x - 1.0
end subroutine minus1_wrong
You should care about the type of arguments passed by R. In R, numeric variables usually are double-precision, and it is the reason why the above program uses double precision
instead of real
. You can safely use integer, double precision, and logical arguments (see the official manual). Note that the character argument is not supported. The restriction means that R developers expect Fortran to focus only on pure numerical computations.
Compilation of Fortran program
The Fortran subroutine should compile to give a shared library, also known as a dynamically-linked library, an object file that can be dynamically linked by R at runtime. To generate a shared library, you should use some compiler options. Both GFortran and Intel Fortran Compiler support the same option names: -fpic
for position-independent code and -shared
for a shared library. You do not need -c
in this case. The shared library has an extension, .so
in Linux. In the following example, we try to compile fortsub.f90
.
# GFortran
gfortran -fpic -shared fortsub.f90 -o fortsub.so
# Intel Fortran Compiler
ifort -fpic -shared fortsub.f90 -o fortsub.so
There is another way of being compliant with R. The following command calls a compiler to generate the shared library. The compiler is the same one that has been used to compile the R software. If you do not like it, please use the manual compilation shown above.
# compilation with R
R CMD SHLIB fortsub.f90
Loading the shared library in R
Before using the Fortran subroutines in R, you have to load the shared library. The following R function can link the shared library to call the subroutines.
dyn.load("fortsub.so")
The above example is valid only when the shared library in the current directory. You should specify the full path if the library is in a different directory.
dyn.load("/home/john/lib/fortsub.so")
Call Fortran subroutine in R
An R function .Fortran()
call s a Fortran subroutine. With this function, you supply the name of Fortran subroutine that you are calling, and required arguments aligned to Fortran types. You do not have to use the same variable names defined in the Fortran subroutine. Using the above example, we can call subroutine minus1
as follows.
m <- 3
n <- 2
mat <- matrix( as.double(seq(1,m*n)),c(m,n) )
result <- .Fortran("minus1",m,n,mat)
The returned object is a list containing the returned arguments. For example, the above code returns the following list.
[[1]]
[1] 3
[[2]]
[1] 2
[[3]]
[,1] [,2]
[1,] 0 3
[2,] 1 4
[3,] 2 5
Or, you can generate a variable when you call the subroutine.
result <- .Fortran("minus1", m=as.integer(3), n=as.integer(2), mat=matrix(as.double(seq(1,6)),c(m,n))
)
The resulting list has the same variable names as you have defined in the call.
$m
[1] 3
$m
[1] 2
$mat
[,1] [,2]
[1,] 0 3
[2,] 1 4
[3,] 2 5
Some issues on memory allocation
You might notice that .Fortran()
returns a copy of the original arguments. In the above example, you have the original m
, n
, and mat
as well as the returned n
, m
, and mat
. The R software creates a copy R handles an array in a unique method that is different from the one in Fortran. It has no impact on performance if mat
is relatively small (and this is a case you meet in many applications). However, if mat
is huge, R consumes much memory, and the program becomes inefficient.
It is unavoidable if the argument is updated. There are a few options to solve some of these issues. If the big array is read-only (unchanged in the subroutine) or write-only (empty-in, something-out), you can avoid the copy using a function in an R package, dotCall64
. If the array is updated (read-and-write), the function still makes a copy. This package is useful when you have a big read-only input and a small output, e.g., Gibbs sampling, iterative solutions of linear equations. We do not explain this package here, so please read its manual if you are interested in it.
There are some more workarounds, and all of them are advanced. The essential solution to this issue is C/C++ to manage R objects directly. Instead of writing the whole program in C/C++, you can call the Fortran subroutine in the C/C++ program. However, the cascading call makes the programs hard to maintain. Also, notably, C++ has a steep learning curve for non-programmers. So, my suggestion is that you use Fortran only when putting read-only data and getting a small output.
Back to index.html.