Passing Fortran Array Slices to C

Sometimes when instrumenting Fortran simulations with Libsim, key arrays in the simulation will have a number of ghost zone layers that you may not want to pass to VisIt. It is common in Fortran to pass array slices to various routines when you want to focus on subsets of an array. The question here is "can I pass an array slice to a C/C++ function and what does that look like?"

Program

The following Fortran program makes a 2D array and populates it with known values. The array is passed to a subroutine that prints the array and again to a C function that prints the array from C. We then pass a slice of the array to the C function to see what that gives us. The main question we're looking to answer is: "what is the Fortran array slice?".

      program slice
      implicit none
      integer MAX
      parameter (MAX = 5)
      external cprintarr
      real arr(1:MAX,1:MAX)
      integer i,j,index

! Populate my 2D array with some values.
      index = 1
      do j = 1,MAX
          do i = 1,MAX
              arr(i,j) = index
              index = index + 1
          enddo
      enddo

      call printarr(arr, MAX)
      call cprintarr(arr, MAX, MAX)
# Pass an array slice to C
      call cprintarr(arr(2:MAX-1,2:MAX-1), MAX-2, MAX-2)

      end program slice

      subroutine printarr(arr, MAX)
      real arr(MAX,MAX)
      write (6,*) "MAX=",MAX
      do j = 1,MAX
          do i = 1,MAX
              write (6,*) "arr(",i,",",j,")=", arr(i,j)
          enddo
      enddo
      end subroutine printarr

Here is a routine to print an array in C:

#include <stdio.h>

void
cprintarr_(float *arr, int *nx, int *ny)
{
    int i, j;
    printf("cprintarr_: arr=%p, nx=%d, ny=%d\n", arr, *nx, *ny);
    for(j = 0; j < *ny; ++j)
        for(i = 0; i < *nx; ++i)
        {
            int idx = j * *nx + i;
            printf("arr[%d] = %f\n", idx, arr[idx]);
        }
}
gcc -c -o cprintarr.o cprintarr.c
gfortran -o slice slice.f cprintarr.o

Program Output

Here is the output of the program:

 MAX=           5
 arr(           1 ,           1 )=   1.0000000    
 arr(           2 ,           1 )=   2.0000000    
 arr(           3 ,           1 )=   3.0000000    
 arr(           4 ,           1 )=   4.0000000    
 arr(           5 ,           1 )=   5.0000000    
 arr(           1 ,           2 )=   6.0000000    
 arr(           2 ,           2 )=   7.0000000    
 arr(           3 ,           2 )=   8.0000000    
 arr(           4 ,           2 )=   9.0000000    
 arr(           5 ,           2 )=   10.000000    
 arr(           1 ,           3 )=   11.000000    
 arr(           2 ,           3 )=   12.000000    
 arr(           3 ,           3 )=   13.000000    
 arr(           4 ,           3 )=   14.000000    
 arr(           5 ,           3 )=   15.000000    
 arr(           1 ,           4 )=   16.000000    
 arr(           2 ,           4 )=   17.000000    
 arr(           3 ,           4 )=   18.000000    
 arr(           4 ,           4 )=   19.000000    
 arr(           5 ,           4 )=   20.000000    
 arr(           1 ,           5 )=   21.000000    
 arr(           2 ,           5 )=   22.000000    
 arr(           3 ,           5 )=   23.000000    
 arr(           4 ,           5 )=   24.000000    
 arr(           5 ,           5 )=   25.000000    
cprintarr_: arr=0x7fffffffdb30, nx=5, ny=5
arr[0] = 1.000000
arr[1] = 2.000000
arr[2] = 3.000000
arr[3] = 4.000000
arr[4] = 5.000000
arr[5] = 6.000000
arr[6] = 7.000000
arr[7] = 8.000000
arr[8] = 9.000000
arr[9] = 10.000000
arr[10] = 11.000000
arr[11] = 12.000000
arr[12] = 13.000000
arr[13] = 14.000000
arr[14] = 15.000000
arr[15] = 16.000000
arr[16] = 17.000000
arr[17] = 18.000000
arr[18] = 19.000000
arr[19] = 20.000000
arr[20] = 21.000000
arr[21] = 22.000000
arr[22] = 23.000000
arr[23] = 24.000000
arr[24] = 25.000000
cprintarr_: arr=0x602b50, nx=3, ny=3
arr[0] = 7.000000
arr[1] = 8.000000
arr[2] = 9.000000
arr[3] = 12.000000
arr[4] = 13.000000
arr[5] = 14.000000
arr[6] = 17.000000
arr[7] = 18.000000
arr[8] = 19.000000

Results

Note that the starting address of the array passed to the C function is different for the original array vs the array slice. Also note that when we pass the smaller array dimensions to describe the size of the array slice, we do in fact get the right values to print out. This means that the array slice is a new array and its values come from the original array but have been rearranged into the smaller slice array's new memory shape. This certainly makes passing array slices to C much easier to deal with as there is no need to know the size of the original array and rely on fancy indexing to traverse it. In an in situ context, however, the array slice is likely a temporary construct so it is probably warranted to make a copy of the array slice data when exposing the array data to VisIt. That is done using the VISIT_OWNER_COPY flag when storing data into a VisIt_VariableData object.