使用 f2py 包装 Fortran MPI 程序

在上一篇中我们介绍了用 Boost.Python 包装 C++ 语言 MPI 程序以供 mpi4py 调用的方法,下面我们将介绍使用 f2py 包装 Fortran MPI 程序的方法。

f2py (Fortran to Python interface generator) 是 numpy 中自带的一个 Fortran 到 Python 的接口生成工具,支持 Fortran 77/90/95,可以使用它将 Fortran 程序包装成可供 Python 调用的扩展模块。感兴趣的读者可以参考其文档,这里不多作介绍。我们只会以简单的例子展示如何使用 f2py 包装 Fortran MPI 程序以供 mpi4py 调用。

假设我们有以下 Fortran 程序文件 helloworld.f90,其中定义了子例程 sayhello,其接受一个 MPI 通信子作为参数。

! helloworld.f90
!
! $ f2py --f90exec=mpif90 -m helloworld -c helloworld.f90
!

subroutine sayhello(comm)
  use mpi
  implicit none
  integer :: comm
  integer :: rank, size, nlen, ierr
  character (len=MPI_MAX_PROCESSOR_NAME) :: pname
  if (comm == MPI_COMM_NULL) then
     print *, 'You passed MPI_COMM_NULL !!!'
     return
  end if
  call MPI_Comm_rank(comm, rank, ierr)
  call MPI_Comm_size(comm, size, ierr)
  call MPI_Get_processor_name(pname, nlen, ierr)
  print *, 'Hello, World!', &
       ' I am process ', rank, &
       ' of ', size, &
       ' on ', pname(1:nlen), '.'
end subroutine sayhello

! program main
!   use mpi
!   implicit none
!   integer ierr
!   call MPI_Init(ierr)
!   call sayhello(MPI_COMM_WORLD)
!   call MPI_Finalize(ierr)
! end program main

要将其编译成扩展模块 helloworld.so 以供 mpi4py 程序调用,我们可以使用类似于以下的命令一步到位:

$ f2py --f90exec=mpif90 -m helloworld -c helloworld.f90

其中 -m 指定模块名,-c 指明编译和重建扩展模块。

但是 f2py 文档中推荐使用以下两步走的方案:

  • 首先通过以下命令产生一个 signature 文件 helloworld.pyf:

    $ f2py -m helloworld -h helloworld.pyf helloworld.f90
    

生成的 signature 文件如下:

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module helloworld ! in 
    interface  ! in :helloworld
        subroutine sayhello(comm) ! in :helloworld:helloworld.f90
            use mpi
            integer :: comm
        end subroutine sayhello
    end interface 
end python module helloworld

! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/

可以根据需要手动编辑所产生的 signature 文件(一般情况下不用作任何编辑也能正常工作)。 作为一个简单的例子,我们在 integer :: comm 这一句中加上 intent(in),使 helloworld.pyf 变成如下:

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module helloworld ! in 
    interface  ! in :helloworld
        subroutine sayhello(comm) ! in :helloworld:helloworld.f90
            use mpi
            integer intent(in) :: comm
        end subroutine sayhello
    end interface 
end python module helloworld

! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/
  • 然后使用以下命令生成扩展模块:

    $ f2py --f90exec=mpif90 -c helloworld.pyf helloworld.f90 
    

编译成功后会生成扩展模块 helloworld.so,然后就可以在我们的 mpi4py 程序中像使用其它 Python 模块一样导入该模块并调用该模块中定义的 sayhello 函数,可以向此函数传递一个 mpi4py 中定义的通信子,如 MPI.COMM_WORLD 或者其它通信子对象。例如使用以下的 test 例程:

# test.py

from mpi4py import MPI
import helloworld as hw

null = MPI.COMM_NULL
fnull = null.py2f()
hw.sayhello(fnull)

comm = MPI.COMM_WORLD
fcomm = comm.py2f()
hw.sayhello(fcomm)

try:
    hw.sayhello(list())
except:
    pass
else:
    assert 0, "exception not raised"

执行结果如下:

$ mpiexec -n 4 python test.py
You passed MPI_COMM_NULL !!!
Hello, World! I am process            0  of            4  on node2.
You passed MPI_COMM_NULL !!!
Hello, World! I am process            1  of            4  on node2.
You passed MPI_COMM_NULL !!!
Hello, World! I am process            2  of            4  on node2.
You passed MPI_COMM_NULL !!!
Hello, World! I am process            3  of            4  on node2.

以上的过程直接编译出了扩展模块,如果想得到或看看扩展模块的源文件,可以使用如下命令:

$ f2py -m helloworld helloworld.f90

该命令会生成扩展模块源码文件 helloworldmodule.c,感兴趣的读者可以打开看看。当然如果你愿意的话也可以用一个 C 编译器手动地将此源文件编译成一个扩展模块。

为了方便,我们也可以编写如下 Makefile 以简化上述操作(注意其中使用了在上一篇中介绍的 python-config 文件):

# Makefile

.PHONY: default
default: build test clean

PYTHON  = python
PYTHON_CONFIG = ${PYTHON} ./python-config


MPIF90 = mpif90
F2PY = f2py
SO = ${shell ${PYTHON_CONFIG} --extension-suffix}
.PHONY: build
build: helloworld${SO}
helloworld${SO}: helloworld.f90
    ${F2PY} --f90exec=${MPIF90} -m helloworld -c $<


MPIEXEC = mpiexec
NP_FLAG = -n
NP = 5
.PHONY: test
test: build
    ${MPIEXEC} ${NP_FLAG} ${NP} ${PYTHON} test.py


.PHONY: clean
clean:
    ${RM} helloworld${SO}

编译扩展库,执行程序及清理可以分别使用如下命令:

$ make build
$ make test
$ make clean

以上我们介绍了用 f2py 包装 Fortran 语言 MPI 程序以供 mpi4py 调用的方法,可以看到包装 C, C++,Fortran 等其它计算机语言的 MPI 程序供 mpi4py 调用是比较容易的,其实反过来将 mpi4py 程序嵌入其它计算机语言中也不难,在下一篇中我们将介绍在 C 语言程序中嵌入 mpi4py 程序的方法。

你可能感兴趣的:(使用 f2py 包装 Fortran MPI 程序)