0

I am having issues transferring 3D arrays from Fortran to C. I have variables which represent the size of the array (idim, jdim, kdim) but I am having a tough time writing a C code to receive arrays that have size of these values. I will attach the Fortran & C code I have right now.

FORTRAN:

  ! Transfer u,v,w to CPU

  idim = imax_ - imin_
  jdim = jmax_ - jmin_
  kdim = kmax_ - kmin_
  CALL CUDA(idim,jdim,kdim,u,v,w)

and C:

extern "C" void cuda_(int *idim, int *jdim, int *kdim, float U[*kdim][*jdim][*idim], float V[*kdim][*jdim][*idim], float W[*kdim][*jdim][*idim])
{

I am working on accelerating an already existing code with GPU programming, but am still relatively new to programming, so please take that into consideration.

Thanks in advance!

LeeVining
  • 1
  • 1
  • New to programming? New to Fortran and C and pointers and malloc and free? That's a lot to ask. – duffymo Jul 06 '16 at 20:22
  • 2
    Probably a duplicate of http://stackoverflow.com/questions/27582715/passing-a-two-dimentional-array-from-fortran-to-c – High Performance Mark Jul 06 '16 at 22:56
  • 2
    Seriously if you are new to programming learn to walk before you can jog. Learn how to pass arguments/parameters within the languages of choice, then learn how to pass entities between C and Fortran, and only then add CUDA to the mix. This will save you much pain in the long run. – Ian Bush Jul 07 '16 at 20:01

2 Answers2

0

Why is the function name in the FORTRAN program not the same? I would use the same name in both programs but perhaps this is FORTRAN-related. I will use the C function name you wrote for this answer and yes, I agree that this is quite a task for a beginner. This will not be a complete solution but should steer you in the right direction (hopefully). First... arrays in C have fixed dimensions determined at compile time. So your function declaration will not compile. If your intention is to dereference the dimension pointers in the array dimention parameters, this will not work because it's not executable code; those are parameter declarations used only for compilation.

Second, it seems like you want to pass arrays that can have different dimensions from one call to another or that you don't know in advance how many elements will be in your arrays. Assuming this, you need only pass the address of your arrays (assuming your dimensions are always the right ones). Your function prototype could then be:

void cuda_(int *idim, int *jdim, int *kdim, float *U, float* V, float* W);

Your function could be implemented as follow (so to show you the pointer arithmetic):

void cuda_(int *idim, int *jdim, int *kdim, float* U, float* V, float* W)
{
  int i, j, k;

  // Assuming you want to process each individual array 
  // items one after the other; this to show pointer arithmetic

  for (i=0; i<*idim; i++)  {

    for (j=0; j<*jdim; j++)  {

      for (k=0; k<*kdim; k++)  {

          // Do whatever with the i,j,kth element;   
          // We assign unique values so you can check 
          // that this example accesses the right array elements            

          *(U + i * sizeof(float) + j * sizeof(float) + k) = i + j + k;        
          *(V + i * sizeof(float) + j * sizeof(float) + k) = 2*i + 2*j + 2*k;
          *(W + i * sizeof(float) + j * sizeof(float) + k) = 3*i + 3*j + 3*k;

      }

    }

  }

  return;
}

Of course, you need to test this example thoroughly to make sure that this is what you really intend on doing and that I did map the 3D array properly (I mapped it over a one-dimentional array assuming that this is how it is implemented in your FORTRAN program; otherwise, you will have to come up with the proper pointer arithmitic but the principle will be the same as in the examples). I did very superficial testing of this code and be warned that pointer manipulation is tricky and error prone! Good luck.

Now, and this is of secondary importance but I feel the point should be made. I don't see why you want to pass the array dimensions as pointers; perhaps it's a requirement for FORTRAN (that is maybe you cannot pass parameters by value/copy). If you can pass parameters by copy (as opposed to by reference), then I suggest the following change:

// Prototype:

void cuda_(int idim, int jdim, int kdim, float* U, float* V, float* W);

// Implementation:

void cuda_(int idim, int jdim, int kdim, float* U, float* V, float* W)
{
  int i, j, k;


  for (i=0; i<idim; i++)  {

    for (j=0; j<jdim; j++)  {

      for (k=0; k<kdim; k++)  {

          *(U + i * sizeof(float) + j * sizeof(float) + k) = i + j + k;        
          *(V + i * sizeof(float) + j * sizeof(float) + k) = 2*i + 2*j + 2*k;
          *(W + i * sizeof(float) + j * sizeof(float) + k) = 3*i + 3*j + 3*k;

      }

    }

  }

}
clarasoft-it
  • 201
  • 1
  • 5
  • Hey clarasoft-it, Thanks for the help. I ended up posting my solution as well. From what I have learned, Fortran values are passed by reference so pointers are necessary. Also the underscore is required after the function name becuase that's what the internet told me to do :) Thanks again! – LeeVining Jul 12 '16 at 19:00
0

The easiest way I could figure out to do this was to pass the array as a 1D array. Then in the c function to create new 3D arrays with the proper dimensions and the then fill them using the 1 D arrays that were passed. I set the modified arrays to be referenced in the same way as the FORTRAN array to hopefully make accessing the array easier. I will attach my C solution below:

extern "C" void cuda_(double *ptr_u, double *ptr_v, double *ptr_w, int *nx, int *ny, int *nz)
{


     //Creating new arrays for U,V,W
     int i,j,k,imax,jmax,kmax,ct=0;
     imax = *nx;
     jmax = *ny;
     kmax = *nz;

     double V[imax][jmax][kmax], W[imax][jmax][kmax], U[imax][jmax][kmax];

     for (k = 0; k < kmax; k++)
     {
          for (j = 0; j < jmax; j++)
          {
               for (i = 0; i < imax; i++)
               {
                    U[i][j][k] = ptr_u[ct];
                    V[i][j][k] = ptr_v[ct];
                    W[i][j][k] = ptr_w[ct];
                    ct++;
               }
          }
     }

If anyone has any comments on this solution, feel free to comment. I'm sure there is a better way of doing this somewhere out there!

LeeVining
  • 1
  • 1