0

I have an already existing Fortran source code that I am working on adding C to, and am having difficulty with passing an array from C into a Fortran function and then receiving another array back.

It compiles without any errors, but when I attempt to run the code, it says that:

"Dimension of array 'p2' (In Fortran Function) has extent 3 instead of -4537421815 (or some other similar number)"

I am not sure what is going on here. I will attach the two codes below.

NOTE: I have removed a lot of the extra variable initialization lines which I think seems unnecessary for finding the problem.

C function:

#include <stdio.h>
#include <stdlib.h>    

extern "C" double* __multiphase_tools_MOD_project(double p1[],double *mydt,int *myi,int *myj,int *myk);

extern "C" void cuda_(int *ptr_band, double *ptr_u, double *ptr_v, double *ptr_w)
{

     double *pt_out;
     double pt_in[3];


     // Loop over the domain and compute fluxes near interfaces
     //=======================================================================================
     //  X FACE
     //=======================================================================================

     for (k = kmin; k <= kmax; k++)
     {
          for (j = jmin; j <= jmax; j++)
          {
               for (i = imin; i <= imax; i++)
               {
                    if (abs(band[i-1][j][k]) <= nband_CFL && abs(band[i][j][k]) <= nband_CFL )
                    {
                         for (int n = 1; n < 10; n++)
                         {
                              pt_in[0] = pt[0][n][1];   pt_in[1] = pt[1][n][1];  pt_in[2] = pt[2][n][1];
                              pt_out = __multiphase_tools_MOD_project(pt_in,&neg_dt_uvw,&i,&j,&k);
                         }
                    }
               }
          }
     }
     return;
}

Fortran Function:

  function project(p1,mydt,myi,myj,myk) result(p2)
    use math
    use iso_c_binding
    implicit none
    real(WP), dimension(3) :: p2
    real(WP), dimension(3), intent(in) :: p1
    real(WP),               intent(in) :: mydt
    integer,                intent(in) :: myi,myj,myk
    real(WP), dimension(3) :: v1,v2,v3,v4
    v1=get_velocity(p1               ,myi,myj,myk)
    v2=get_velocity(p1+0.5_WP*mydt*v1,myi,myj,myk)
    v3=get_velocity(p1+0.5_WP*mydt*v2,myi,myj,myk)
    v4=get_velocity(p1+       mydt*v3,myi,myj,myk)
    p2=p1+mydt/6.0_WP*(v1+2.0_WP*v2+2.0_WP*v3+v4)
    return
  end function project
9
  • 2
    Why not add bind(c) to the Fortran function definition and avoid (what I assume to be) manual name mangling? This may address the call issue too. I've just noticed: why are you using iso_c_binding, but then not referencing any of the entities from it? That module perhaps doesn't do what you think it does. Commented Jul 21, 2016 at 19:16
  • 1
    Tried adding bind(C) to fortran function, it said the return type can't be an array. Commented Jul 21, 2016 at 19:44
  • 2
    That's a very good point (which I missed) about the return arrayness. But potentially the source of your problem: Fortran arrays are not the same thing as C arrays. Perhaps you could re-write the function to return a C pointer, but that really will mess up your other Fortran calls. [I won't say more now, as I'm clearly just guessing.] Commented Jul 21, 2016 at 19:59
  • 1
    If you write a wrapper to project() that receives p2(:) as a dummy argument (such that the wrapper is C-interoperable), then I guess there is no need to re-write project() in C... Commented Jul 21, 2016 at 20:05
  • 1
    Depending on the size of your code base, my suggestion may not be feasible. But, why not have the function return a type(c_ptr) then use call c_f_pointer(c_loc(old_c_ptr), fortran_ptr) to ensure correct C-language style casting. Commented Jul 21, 2016 at 20:41

1 Answer 1

3

Fortran functions which return an array actually use a hidden argument and are implemented similarly to subroutines. This is why you can't easily write a compatible C function, because you can't portably determine the hidden argument properties.

For example

function f() result(res)
  real :: res(3)
  res = [1.,2.,3.]
end

looks in the GCC internal code as

f (struct array1_real(kind=4) & __result)
{
}

I suggest to convert the function to a subroutine. That way you can exactly control the argument (parameter) for the array and you can make a C interface:

  subroutine project(p1,p2,mydt,myi,myj,myk) bind(C)
    real(WP), dimension(3), intent(out):: p2
    ...

or you can write a wrapper subroutine which calls your function to maintain the Fortran functionality unchanged.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.