5.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 tutorial source contains implementation of three components, CArrayOp, F77ArrayOp, and F90ArrayOp, implemented in C, F77, and F90 respectively.

5.3.1. The CArrayOp Component

Code for the CArrayOp component can be found in the directory $TUTORIAL_SRC/components/arrayOps.CArrayOp/, 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 {
   ...
   ... 
   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 bocca-generated component constructor method impl_arrayOps_CArrayOp__ctor

  struct arrayOps_CArrayOp__data *dptr =
       (struct arrayOps_CArrayOp__data*)malloc(sizeof(struct arrayOps_CArrayOp__data));
   if (dptr) {
      memset(dptr, 0, sizeof(struct arrayOps_CArrayOp__data));
   }
   arrayOps_CArrayOp__set_data(self, dptr);

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).

5.3.2. The F77ArrayOp Component

Code for the F77ArrayOp component can be found in the directory $TUTORIAL_SRC/components/arrayOps.F77ArrayOp/, 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.

        tmp = 0
        itmp = 0

        call sidl_int__array_create1d_f(1, intArray)
        if (intArray .ne. 0) then
           call sidl_opaque__array_set1_f(stateArray, 0, tmp)
           call sidl_int__array_set1_f(intArray, 0, itmp)
           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).

5.3.3. The F90ArrayOp Component

Code for the F90ArrayOp component can be found in the directory $TUTORIAL_SRC/components/arrayOps.F90ArrayOp, 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)

! Bocca generated code. bocca.protected.begin(arrayOps.F90ArrayOp.private_data)
  ! Handle to framework Services object
  type(gov_cca_Services_t) :: d_services
! Bocca generated code. bocca.protected.end(arrayOps.F90ArrayOp.private_data)

  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 bocca-generated code for the allocation and initialization of the private data associated with this component instance

  type(arrayOps_F90ArrayOp_wrap) :: dp
  ! Allocate memory and initialize
  allocate(dp%d_private_data)
  call set_null(dp%d_private_data%d_services)
  dp%d_private_data%myVectorP => NULL()
  call arrayOps_F90ArrayOp__set_data_m(self, dp)

The call to the built-in method arrayOps_F90ArrayOp__set_data_m associates the newly created structure pointed to via dp 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 ). 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.