Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass MPI communicator handle from Fortran to C using iso_c_binding

Tags:

I am trying to link a parallel MPI Fortran program to a parallel C library which uses MPI too. The software architecture is Fortran-centered so I'm trying to keep as much as I can on the Fortran side.

So I need to pass the C routines a handle to the MPI communicator. They are of the form

  int CFunction(int *something, MPI_Comm *Ccomm)

MPI comes with interfaces to translate a Fortran to a C communicator handle:

MPI_Comm MPI_Comm_f2c(MPI_Fint comm)

However, these routines are supposed to be called from C so right now I've had to add C wrapper functions I can pass the Fortran communicator to:

int CFunction_FMPI(int *something, MPI_Fint *Fcomm)
{   MPI_Comm Ccomm; int status;
    Ccomm = MPI_Comm_f2c(*Fcomm); // Convert Fortran->C communicator
    status = CFunction(*something,*Ccomm); // Call original function
    return status;
}

then I had to write a 2nd interface -- to CFunction_FMPI -- using Fortran's C bindings to allow it to be called from Fortran.

My question is: isn't there any better way to do this, i.e., to avoid the C wrapper with Fortran->C communicator conversion? I think calling MPI_Comm_f2c directly from Fortran and storing the result in a type(c_ptr) or integer(c_int) variable would be best, but I haven't been able to do it since there is no straightforward/general binding between the MPI_Comm type and Fortran.

like image 378
Federico Perini Avatar asked Mar 01 '17 11:03

Federico Perini


1 Answers

No, I don't think there is a much better way to do it. And it is not that much complicated that I would be concerned about it. You can look at a similar function I use at

https://github.com/LadaF/PoisFFT/blob/master/src/f_mpi_comm_c2f.c

It is the opposite direction to your CFunction_FMPI and just translates the communicator. It is from C to Fortran.

// This function is callable from Fortran. MPI_Comm_c2f itself may be just a macro.

MPI_Fint f_MPI_Comm_c2f(MPI_Comm *comm) {
  return MPI_Comm_c2f(*comm);
}

It is than called from Fortran as

interface
    integer function MPI_Comm_c2f(c_handle) bind(C, name="f_MPI_Comm_c2f")
      use iso_c_binding
      type(c_ptr), value :: c_handle
    end function
end interface

The important point is that MPI_Comm_c2f is in some MPI libraries a C macro, not a function, so you cannot really call it from Fortran. I am quite sure MPI_Comm_f2c is also allowed to be a macro and you cannot therefore call it from Fortran.


What you can do is to create a Fortran function, which just calls the C wrapper for MPI_Comm_f2c and than call your C function in Fortran using a bind(C) interface as

status = CFunction(something, c_comm)

and thus avoid creating a wrapper for each C function. You just need a Fortran interface block for them.

The problem is that you don't have MPI_Comm in Fortran* in (in practice it is a pointer or an int) so you must use an opaque pointer.

MPI_Comm* f_MPI_Comm_f2c(MPI_Fint Fcomm)
{   MPI_Comm* Ccomm;
    Ccomm = malloc(sizeof(MPI_Comm));
    *Ccomm = MPI_Comm_f2c(Fcomm);
    return Ccomm;
}

which returns an opaque pointer type(c_ptr). (Check for potential C coding errors I just even forgot to use semicolons.)

You than just once convert the Fortran communicator to the pointer to the C communicator.

 type(c_ptr) :: c_comm
 c_comm = f_MPI_Comm_f2c(comm)

* There is a derived type type(MPI_Comm) in MPI-3, but it contains an integer component which must be converted by the conversion routines anyway.

like image 145
Vladimir F Героям слава Avatar answered Sep 23 '22 10:09

Vladimir F Героям слава