Domanda

I am working with R, but need to do a lot of number crunching, which I want to do in fortran. I am relatively new to R and a newbie to fortran... I already have a working R program, which I would like to optimize. I created a Fortran program solving a system of ODE's, which I keep in a subroutine. I additionally use a module called aux.f90 to store the parameters and a function which creates a signal that is fed into the equations. This works as intended and the data is saved in a .txt file.

What I would like to do now is to create an R front-end that tosses the Fortran program the parameters, such as the length of the simulation or the number of steps used in the solution. Then Fortran does the heavy lifting, saves the results in the file and I can use R to visualize the data from the file. See the Fortran code below:

! The auxiliary module contains all parameters
module aux
implicit none

integer,parameter :: n = 2**8       ! number of steps 
real(kind=4) :: jout = 0.5          ! for normal metabolism
real(kind=4) :: alp = 4.0           ! factor for growth
real(kind=4) :: bet = 1.0           ! benefit value
real(kind=4) :: etay = 0.1          ! cost value y
real(kind=4) :: etaz = 0.10         ! cost value z
real(kind=4) :: h  = 3.0            ! Hill coefficient
real(kind=4) :: Kx = 0.07           ! saturation coefficient
real(kind=4) :: t1 = 0.0, t2 = 30.0 ! start and end point of the simulation

contains                            ! function f(t) to create a step signal

real(kind=4) function f(t)
    implicit none
    real(kind=4), intent(in)  :: t  ! taking time value
    real(kind=4)              :: tt 
    !real(kind=4), intent(out) :: s  ! giving out the signal 
    real(kind=4) :: period = 5      ! defining the period length

    tt = MODULO(t,period)

        ! Signal function
        if (tt > 0.5*period) then 
            f = 1
        else
            f = 0
        endif
end function

end module aux

! The program solving the ODE system and giving the output as a fileprogram ffl
program ffl
use aux         ! Use module aux
implicit none
integer                     :: m,j          ! iteration variable
real(kind=4), dimension(6)  :: b =(/0.0, 0.2, 0.4, 0.6, 0.8, 1.0/) ! expression
real(kind=4)                :: dt                                  ! time resolution
real(kind=4), dimension(n)  :: t                                   ! time vector
real(kind=4), dimension(4)  :: x_new, x_aux, y_new, y_aux, q0      ! vectors 

! computing the time vector 
dt=(t2-t1)/real(n) ! calculating time resolution
t(1) = t1          ! setting first time value to t1 = 0

do m = 1,n         ! filling the time vector
    t(m) = t(m-1)+dt
end do
open(unit = 10,file = 'ffl.txt', status = 'unknown')
do j=1,6
    k = b(j)
    ! initial conditions
    q0(1) = 0   ! x
    q0(2) = k   ! y
    q0(3) = 0   ! z
    q0(4) = 0   ! w

    !open(unit = 10,file = 'ffl.txt', status = 'unknown')
    x_new = q0                ! set initial conditions
    write(10,*)t(1),x_new(1),x_new(2),x_new(3),x_new(4) ! saving data

    do m = 2,n
        ! Solving with a second order method 
        call derivate(t(m-1),x_new,y_new) ! call the derivate routine
        x_aux = x_new + dt*y_new

        call derivate(t(m),x_aux,y_aux)
        x_new = x_new + 0.5*dt*(y_new+y_aux)
        write(10,*)t(m),x_new(1),x_new(2),x_new(3),x_new(4) ! saving data

    end do
 end do
 close(10)
end program ffl

! The subroutine derivate gives the system of ODE's to be solved

subroutine derivate(time,y,z)
use aux              ! Use module aux
implicit none
real(kind=4), intent(in)               :: time ! input: time vector  
real(kind=4), dimension(4), intent(in) :: y    ! input: initial conditions vector
real(kind=4), dimension(4), intent(out):: z    ! output: results vector

z(1) = f(time)-y(1)                                                             !dx/dt 
z(2) = k+(1-k)*((1+Kx)*(y(1)**h))/((y(1)**h)+Kx)-y(2)                           !dy/dt
z(3) = ((1+Kx)*(y(1)**h)/((y(1)**h)+Kx)*((1+Kx)*(y(2)**h))/((y(2)**h)+Kx)-y(3)) !dz/dt
z(4) = f(time)*y(3)-etay*(k+(1-k)*((1+Kx)*(y(1)**h))/((y(1)**h)+Kx)) &          !dw/dt
       -etaz*(((1+Kx)*(y(1)**h))/((y(1)**h)+Kx)*((1+Kx)*(y(2)**h))/((y(2)**h)+Kx)) 

end subroutine derivate

I have read the "Writing R extensions" document, but did not find it very helpful...

NOW to the questions: Since R needs a Fortran subroutine, i would like to create a wrapper subroutine in fortran that makes use of my existing files, which I can then call from R. However, i can't find a way to create this wrapper subroutine in the first place. Is it even possible to call an actual program in a subroutine? I couldn't find anything helpful online.

È stato utile?

Soluzione

A program is supposed to be linked as an executable, so you can't call it from a subroutine - or you call the executable (with SYSTEM in gfortran), but you could do that directly from R.

The easy way to call Fortran from R is the .Fortran R function, which calls a Fortran subroutine (not a function, nor a program).

The basic steps are :

  • compile a Fortran DLL, exporting the subroutines you need (of course they may be wrappers for other subroutines or functions)
  • put the DLL in a directory in your system path
  • from R, load the DLL with dyn.load
  • call your subroutine with .Fortran.

If you use gfortran, you may just install Rtools, which has everything you need. If you want to use another compiler, you may have some trouble, especially with names.

From your comment to user2188538's answer, I see you already know all these steps, but be very careful with symbol names. From the .Fortran help: Use .Fortran with care for compiled Fortran 9x code: it may not work if the Fortran 9x compiler used differs from the Fortran 77 compiler used when configuring R, especially if the subroutine name is not lower-case or includes an underscore. It is also possible to use .C and do any necessary symbol-name translation yourself.

Also, I suspect your wrapper subroutine should not reside inside a module, or you may have extra trouble with names. But this is only a limitation for the wrapper function, which must be visible from R.

You can check the exported names in your DLL (send objdump -x your.so to a file and look for exported symbols). And check also in R, with is.loaded("your.symbol"), after loading the DLL. Be aware that usually, gfortran appends an extra underscore to names, whereas it's not needed when you call .Fortran from R. As described above, you may use .C instead (but then, remember Fortran arguments are passed by reference).

To check that you understand the whole process, I suggest you test it on a trivial example, such as a unique subroutine mysub(x,y,z) that does only z=x+y. When this one runs, you can elaborate on it to call more complex routines.

edit You should not use assumed-shape or deferred-shape arrays, when you pass arrays arguments from R to Fortran, but only assumed-size arrays, that is, the usual array passing in Fortran 77. This is because R knows only how to pass a pointer to raw data, whereas assumed-shape and deferred-shape need more information, and R does not know the data structure to do that.

For example, you can do that:

subroutine mysub(n, a)
    real :: a(n, n)
    ...
end subroutine

But this will amost certainly fail:

subroutine mysub(a)
    real :: a(:, :)
    ...
end subroutine

Also, you can't pass function arguments from R to Fortran, as that would need a special data structure for the callback (under the hood, R is a Scheme dialect, and it uses S-expressions). You may do that in C, through .C or .Call (see help for .Call and R Internals).

Altri suggerimenti

You can indeed call Fortran from R using the .Foreign function in the R base package (see ?.Foreign). For some clear examples on how to do this, see the following page on how to call Fortran (as well as C) from R: http://users.stat.umn.edu/~geyer/rc/

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top