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.