Calling Fortran variables from Julia

Hi all,

I am trying to call Fortran from Julia, but there’re some issues.

1. Fortran constants

cglobal works fine when calling Fortran static module variables, but it cannot find Fortran constants. For example, here is a simple Fortran module:

module simpleModule

implicit none

integer, parameter:: hparam = 10
integer :: h1 = 1
real :: r(2) = [1.0,2.0]
real, allocatable :: s(:)

contains

function foo(x)
  integer :: foo, x
  foo = x * 2
end function foo

subroutine init_var
  integer :: i

  if(.not.allocated(s)) then
    allocate(s(10))
  else
    write(*,*) 's has already been allocated!'
  endif

  do i=1,10
    s(i) = i
  enddo

  write(*,*) s

end subroutine init_var

subroutine double_var(x)
  real, intent(inout):: x(:)

  x = x * 2
  write(*,*) x

end subroutine double_var

end module simplemodule

which can be compile into a dynamic library with the following command:

gfortran simplemodule.f95 -o simplemodule.so -shared -fPIC

Then in Julia,

# import integer
a = cglobal((:__simplemodule_MOD_h1, "./simplemodule.so"), Int32)
b = unsafe_load(a)

# import integer array
a = cglobal((:__simplemodule_MOD_r, "./simplemodule.so"), Float32)
# method 1
b = [unsafe_load(a,i) for i in 1:2]
# method 2
b = unsafe_wrap(Array{Float32,1}, a, 2)

works fine, but

# import constant integer error?
a = cglobal((:__simplemodule_MOD_hparam, "./simplemodule.so"), Int32)

returns error, saying that no such variable is found in the library. How does Fortran deal with constant parameters, and is there a way to get the constant value in Julia?

2. Fortran allocatable arrays

The follow Julia calls can successfully allocate, initialize, and modify the Fortran array s:

ccall((:__simplemodule_MOD_init_var, "./simplemodule.so"), Cvoid, ())
s = cglobal((:__simplemodule_MOD_s, "./simplemodule.so"), Float32)
ccall((:__simplemodule_MOD_double_var, "./simplemodule.so"), Cvoid,
      (Ptr{Float64},), s)

However, when I tried to use the pointer to get the values of s, it showed wrong values:

b = unsafe_wrap(Array{Float32,1}, s, 10) # This is not working!

Why is that? Can I get the correct values through other methods?

Thanks!

These actually point to some kind of array descriptor (that has the array dimensions in addition to the raw data, I think).

There is a very recent Fortran standard that specifies how to access this data from C, but it isn’t supported yet by gfortran: https://gcc.gnu.org/onlinedocs/gfortran/Further-Interoperability-of-Fortran-with-C.html

1 Like

I don’t know of a reliable way to do this without changing the Fortran source, but this may help if you can.

If you are able to edit your source and it is practical to do so, I recommend using Fortran’s C interoperability (by declaring use iso_c_binding in the module before implicit none).

For the constants, I would write (or make the computer write if there are many such definitions needed) a wrapper function like so:

function get_hparam() bind(c)
  integer(C_INT) :: get_hparam

  get_hparam = int(hparam, C_INT)
end function get_hparam

which can be called in Julia like this:

julia> ccall((:get_hparam, "./simplemodule.so"), Cint, ())
10

For allocatable arrays, I believe you have to declare the array a pointer or target. You can then use c_loc to get a C pointer to the data. In the Fortran module:

function get_s() bind(c)
  type(c_ptr) :: get_s

  get_s = c_loc(s)
end function get_s

From Julia:

julia> ps = ccall((:get_s, "simplemodule.so"), Ptr{Cfloat}, ())
Ptr{Float32} @0x00007ff1254b8d50

julia> s = unsafe_wrap(Vector{Cfloat}, ps, 10)
10-element Array{Float32,1}:
  1.0
  2.0
  3.0
  4.0
  5.0
  6.0
  7.0
  8.0
  9.0
 10.0

I tried your suggestions, and they did work! For the get_s function, I need to further add the target attribute to s. The other tiny thing to notice is that if I switch the calling sequence (i.e. calling get_s in Julia before calling init_s), Julia will crash, which is understandable.