Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allocating memory in C for a fortran array

Tags:

c

fortran

I am new to fortran programming.. I have a 3 dimensional array declared as follows

REAL*4, DIMENSION(:,:,:), ALLOCATABLE :: a1

I want to pass the array by reference to a C or C++ function and allocate memory in C rather than in fortran. Is it possible or I am wrong in understanding the array concept in fortran ?

like image 713
Sagar Masuti Avatar asked May 05 '13 14:05

Sagar Masuti


People also ask

How is memory allocated to arrays in C?

To allocate memory for an array, just multiply the size of each array element by the array dimension. For example: pw = malloc(10 * sizeof(widget)); assigns pw the address of the first widget in storage allocated for an array of 10 widget s.

How do I dynamically allocate an array in Fortran?

A dynamic array is an array, the size of which is not known at compile time, but will be known at execution time. Dynamic arrays are declared with the attribute allocatable. The rank of the array, i.e., the dimensions has to be mentioned however, to allocate memory to such an array, you use the allocate function.

What is allocation in Fortran?

The ALLOCATE statement dynamically creates storage for array variables having the ALLOCATABLE or POINTER attribute. If the object of an ALLOCATE statement is a pointer, execution of the ALLOCATE statement causes the pointer to become associated.


2 Answers

It depends, but probably not, depending on your circumstances. Definitely not in a portable way, if you are limited to Fortran 90 as per the question tag.

Note that your Fortran variable is an ALLOCATABLE.

As you've asked, it requires a Fortran compiler (and companion C compiler) that implement the relatively recently published Technical Specification (TS 29113:2012). You are operating at the bleeding edge of the development of the language - and the number of compilers that currently support this TS is small (if not zero). It is expected that the contents of this TS would be incorporated into next revision of the Fortran standard, so try again in a few years time and you might have better luck.

Note though, that the TS limits the way in which C can do the allocation for Fortran - basically the C code needs to call a Fortran compiler supplied routine. You can not use any old allocator (including malloc), so this still may not suit.

However, Fortran 2003 added the possibility for the memory allocation of Fortran POINTER's (not allocatables as you've asked) to be done in C. The C code returns a pointer to a chunk of memory of the appropriate size, which the Fortran procedure receives back as object of type C_PTR. The Fortran code then calls the procedure C_F_POINTER to associate the C address in the C_PTR object with the Fortran pointer. C_PTR and C_F_POINTER are entities in the intrinsic ISO_C_BINDING module.

A crude example:

USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER
INTERFACE
  FUNCTION get_some_memory() BIND(C, NAME='get_some_memory')
    IMPORT :: C_PTR
    TYPE(C_PTR) :: get_some_memory
  END FUNCTION get_some_memory
END INTERFACE

! REAL*4 isn't Fortran.  Correct syntax below.
REAL(4), POINTER, DIMENSION(:,:,:) :: a1

! get_some_memory would typically be told how much memory was needed.
! n1, n2, n3 are integer with the relevant extents for each dimension.
CALL C_F_POINTER(get_some_memory(), a1, [n1,n2,n3])

! Equivalent call to free required at some later stage.


void *get_some_memory()
{
  return malloc(some_number_of_bytes);   
} 

Beyond this, there are processor dependent (i.e. non-portable) tricks that you can play, but they are outside of the standard language.

like image 174
IanH Avatar answered Oct 14 '22 21:10

IanH


As the answer by IanH says, the Technical Specification TS 29113 allows a way for the allocation of a Fortran allocatable object to be performed in a C function, this having a lasting effect on the Fortran side.

Since that earlier answer was written support for this TS has increased (although it's still not large and the C descriptor part we'll use in this answer is notably not supported by current gfortran). That means it's now probably worth writing an example. This example is not valid under Fortran 2008.

We consider the C structure given by CFI_desc_t (from the source file ISO_Fortran_binding.h) and the C function CFI_allocate.

Assume that our allocation of the rank-3 array occurs in a C procedure compatible with the following Fortran interface

interface
   subroutine allocating_sub(x) bind(c)
     use, intrinsic :: iso_c_binding, only : c_float
     real(c_float), allocatable :: x(:,:,:)
   end subroutine allocating_sub
end interface

We note that such a Fortran interface is compatible with a C function prototype which has the formal argument as a pointer to CFI_cdesc_t.

Define this C function as (with magic numbers, and missing lots of other sensible checking and such):

#include "ISO_Fortran_binding.h"

void allocating_sub(CFI_cdesc_t* x) {
  if (x->base_addr) CFI_deallocate(x);
  CFI_index_t lower[3]={-1, 5, 1};
  CFI_index_t upper[3]={20, 9, 5};
  CFI_allocate(x, lower, upper, 0);
}

CFI_index_t is a "typedef name for a standard signed integer type capable of representing the result of subtracting two pointers".

Allocation happens with the CFI_allocate function which here allocates x with the lower and upper bounds given by the obvious variables. The final number (0) is ignored in our case with x not corresponding to a Fortran character type.

To complete the example, take the Fortran program:

  use, intrinsic :: iso_c_binding, only : c_float
  implicit none

  interface
     subroutine allocating_sub(x) bind(c)
       import c_float
       real(c_float), allocatable :: x(:,:,:)
     end subroutine allocating_sub
  end interface

  integer i
  real(c_float), allocatable :: x(:,:,:)

  allocate(x(6,2,5))
  print 1, (LBOUND(x,i), UBOUND(x,i), i=1,3)

  call allocating_sub(x)
  if (allocated(x)) print 1, (LBOUND(x,i), UBOUND(x,i), i=1,3)

1 format ("x has been allocated like x(",2(I0,":",I0,","),I0,":",I0,").")

end

The example is a little non-minimal as it also shows the CFI_deallocate function and the fact that, although we have this extra level on the C side, we can still access the memory in that C function through the component x->base_addr. This is a null pointer if the Fortran variable is not allocated; otherwise it's the C address of the first element of the Fortran array, as long as there is at least one element.

Much more detail can be found in the TS document or the draft upcoming Fortran standard.

like image 23
francescalus Avatar answered Oct 14 '22 21:10

francescalus