7.3. Linear Array Operations Components

In this section, we present some of the implementation details of (non-driver) components that provide ports with SIDL arrays as arguments. The student source contains implementation of three components, CArrayOp, F77ArrayOp, and F90ArrayOp, implemented in C, F77, and F90 respectively.

7.3.1. The CArrayOp Component

Code for the CArrayOp component can be found in the directory $STUDENT_SRC/components/arrayOps/c, in the two Impl files arrayOps_CArrayOp_Impl.c and arrayOps_CArrayOp_Impl.h. Private component state is represented by entries in the struct arrayOps_CArrayOp__data in the header file arrayOps_CArrayOp_Impl.h

struct arrayOps_CArrayOp__data {
   /* DO-NOT-DELETE splicer.begin(arrayOps.CArrayOp._data) */
   gov_cca_Services  frameworkServices;
   double            *myVector;
   int               myVecLen;
   /* DO-NOT-DELETE splicer.end(arrayOps.CArrayOp._data) */
};

Private component data is initialized and associated with the component instance in the component constructor method impl_arrayOps_CArrayOp__ctor

   struct arrayOps_CArrayOp__data *pd = (struct arrayOps_CArrayOp__data*) 
                 calloc(1, sizeof(struct arrayOps_CArrayOp__data));
   arrayOps_CArrayOp__set_data(self, pd);
   arrayop_LinearOp_init(self);
   return;

Note the use of the built-in method arrayOps_CArrayOp__set_data to associate the newly allocated struct with this component instance. A corresponding method, arrayOps_CArrayOp__get_data is used to access this private data.

The method impl_arrayOps_CArrayOp_mulMatVec uses SIDL raw arrays (array A, and vectors x and y). Multi-dimension SIDL raw arrays are assumed to be stored in column-major order, as shown in the code to multiply array A and vector x

   for (i= 0; i <= m; i++){
     y[i] = 0.0;
     for (j = 0 ; j <= n; j++){
       y[i] += alpha * A[j*m + i] * x[j];  /* Raw array A is column-major */
     }
     pd->myVector[i] += y[i];
     y[i] = pd->myVector[i];
   }

The method impl_arrayOps_CArrayOp_addVec uses the more flexible SIDL normal arrays. SIDL normal arrays are represented in C using a struct sidl_XXX__array, where XXX is the actual type of array elements. In this example, the SIDL out normal array *r is created (and underlying memory allocated) in the call

   *r = sidl_double__array_create1d(n);

Direct access to a SIDL normal array's underlying memory is acheived via the C macro sidlArrayAddr1 (for 1-dimensional arrays *r and v).

[Note] Note

When implementing a method that has SIDL normal arrays as arguments, it should not be assumed that the array is contiguous in memory (stride=1). SIDL normal arrays allow for different strides in all dimensions. As such, the correct code for vector addition has the form

   vstride = sidlStride(v, 0);
   for ( i = 0; i <= n; i++){
      rdata[i] = pd->myVector[i] += beta * vdata[i*vstride];
   }

No stride is used when accessing the vector r since it is created inside the addVec routine with a stride=1 (implied in the call to sidl_double__array_create1d).

7.3.2. The F77ArrayOp Component

Code for the F77ArrayOp component can be found in the directory $STUDENT_SRC/components/arrayOps/f77, in Impl file arrayOps_f77ArrayOp_Impl.f. Private component state is represented by entries an an array of SIDL opaque types. It is the responsibility of the programmer to ensure consistency of the treatment of entries in this array across method calls (this is similar to the way entries into common blocks are manipulated). Code for the creation and initialization of the private component state can be found in the component constructor method arrayOps_F77ArrayOp__ctor_fi.

   integer *8  stateArray, intArray, tmp
   tmp = 0
   call sidl_opaque__array_create1d_f(3, stateArray)
   call sidl_int__array_create1d_f(2, intArray)
   if ((statearray .ne. 0) .and. (intArray .ne. 0)) then
     call sidl_opaque__array_set1_f(statearray, 0, tmp)
     call sidl_opaque__array_set1_f(statearray, 1, intArray)
     call sidl_opaque__array_set1_f(statearray, 2, tmp)
   else
      . . .

The SIDL built-in method arrayOps_F77ArrayOp__set_data_f is used to associate the newly created SIDL opaque array with this instance of the component. The method arrayOps_F77ArrayOp__get_data_f is used to retrieve this private data for further manipulation.

The method arrayOps_F77ArrayOp_mulMatVec_fi uses SIDL raw arrays arguments. In F77 implementation, SIDL raw arrays appear as regular F77 arrays, with zero-based indexing. The component uses the SIDL normal array accVector to store the running sum of the linear matrix operations. Note that this enables the dynamic sizing of this vector at runtime to match the dimensions of the array and vector arguments. Direct access to the underlying memory for SIDL normal arrays is done through the sidl_double__array_access_f method (for arrays of SIDL type double). This method computes uses a reference array (nativeVec) of size one, and computes the offset (refindex) that needs to be added to indices into nativeVec to access memory associated with SIDL normal array accVector.

       call sidl_double__array_access_f(accVector, nativeVec, 
      $       lower, upper, stride, refindex)
       do i = 0, m-1
          y(i) = nativeVec(refindex + i)
          do j = 0, n-1
             y(i) = y(i) + alpha * A(i, j) * x(j)
          end do
          y(i) = y(i) + nativeVec(refindex + i)
          nativeVec(refindex + i) = y(i)
       end do

Accesssing entries in a normal SIDL array can also be done through accessor subroutine calls. In the case of arrays of SIDL type double, the accessor subroutines are sidl_opaque__array_set1_f and sidl_opaque__array_get1_f (for single dimensional arrays).

   if (accVector .eq. 0) then
      call sidl_double__array_create1d_f(m, accVector)
      call sidl_int__array_set1_f(intArray, 0, m)
      call sidl_opaque__array_set1_f(stateArray, 2, accVector)
      dblTmp = 0.0
      do i = 0, m-1
         call sidl_double__array_set1_f(accVector, i, dblTmp)
      end do
   else
      . . .

[Note] Note

When implementing a method that has SIDL normal arrays as arguments, it should not be assumed that the array is contiguous in memory (stride=1). SIDL normal arrays allow for different strides in all dimensions. As such, the correct code for vector addition in addVec has the form

     do i = 0, m-1
       nativeR(refindexR + i) = nativeVec(refindex + i) + 
   $                         beta * nativeV(refindexV +i*strideV(1))
       nativeVec(refindex + i) = nativeR(refindexR + i)
     end do

No stride is used when accessing the array r since it is created inside the addVec routine with a stride=1 (implied in the call to sidl_double__array_create1d_f).

7.3.3. The F90ArrayOp Component

Code for the F90ArrayOp component can be found in the directory $STUDENT_SRC/components/arrayOps/f90, in the Impl files arrayOps_F90ArrayOp_Impl.F90and arrayOps_F90ArrayOp_Mod.F90. Private component state is represented by the type arrayOps_F90ArrayOp_priv in the file arrayOps_F90ArrayOp_Mod.F90

type arrayOps_F90ArrayOp_priv
  sequence
  ! DO-NOT-DELETE splicer.begin(arrayOps.F90ArrayOp.private_data)
  ! Handle to framework Services object
  type(gov_cca_Services_t) :: frameworkServices
  real (selected_real_kind(15, 307)), dimension(:), pointer :: myVectorP 
  integer (selected_int_kind(9)) :: myVecLen
  ! DO-NOT-DELETE splicer.end(arrayOps.F90ArrayOp.private_data)
end type arrayOps_F90ArrayOp_priv

The constructor subroutine arrayOps_F90ArrayOp__ctor_mi contains the code for the allocation and initialization of the private data associated with this component instance

   type(arrayOps_F90ArrayOp_wrap) :: dataWrap
   type(arrayOps_F90ArrayOp_priv), pointer :: pd
   
   allocate(dataWrap%d_private_data)
   pd => dataWrap%d_private_data
   ! Allocate memory and initialize
   call set_null(pd%frameworkServices)
   pd%myVectorP => NULL()
   pd%myVecLen = 0
   call arrayOps_F90ArrayOp__set_data_m(self, dataWrap)

Note that private data is accessed through the pointer pd, accessed through the variable dataWrap of type arrayOps_F90ArrayOp_wrap. The call to the built-in method arrayOps_F90ArrayOp__set_data_m associates the newly created structure pointed to via pd with this instance of the component. The corresponding method arrayOps_F90ArrayOp__get_data_m is used to retrieve this private data for further processing.

The subroutine that implements the mulMatVec method uses SIDL raw arrays (note that the name of this subroutine is altered by Babel to accomodate F90 identifier length restrictions as outlined in Section 3.3, “Implementation of the F90Driver in Fortran 90”). SIDL raw arrays manifest themselves in F90 implementations as regular F90 arrays that use zero-based indexing.

   real (selected_real_kind(15, 307)), dimension(0:m-1, 0:n-1) :: A ! in
   real (selected_real_kind(15, 307)), dimension(0:n-1) :: x ! in
   real (selected_real_kind(15, 307)), dimension(0:m-1) :: y ! inout

The subroutine that implements the addVec method uses SIDl normal arrays. SIDL normal arrays are represented as user defined types, with a pointer data member (d_datathat points to an F90 array built on top of the underlying SIDL array memory. While access to SIDL normal array entries can be achieved via accessor subroutines (set and get - defined for all native SIDL types and user defined classes and interfaces), it is more convenient (and efficient) to access those entries directly via the d_data pointer.

   vdata => v%d_data
   rdata => r%d_data
   rdata = pd%myVectorP + beta * vdata
   pd%myVectorP = rdata

[Note] Note

When implementing a method that has SIDL normal arrays as arguments, it should not be assumed that the array is contiguous in memory (stride=1). SIDL normal arrays allow for different strides in all dimensions. The Babel runtime build the correct F90 array descriptor (dope vector) that correctly reflects the strides used to create the SIDL array.