我正在研究一种模拟波浪能转换器的工具,我需要将两个软件包相互耦合 . 一个程序用Fortran编写,另一个用C语言编写 . 我需要在每个时间步骤将Fortran程序中的信息发送到C程序 . 但是,在将数据发送到C程序之前,首先需要在Python中处理数据 . 我收到了一条使用MPI在程序之间传输数据的提示 .
我现在正在尝试从Fortran代码向Python发送一个简单的字符串,但Python代码卡在receive命令中 .
我的Fortran代码如下所示:
USE GlobalVariables
USE MPI
IMPLICIT NONE
CHARACTER(LEN=10):: astring
INTEGER :: comm, rank, size, mpierr
! Initialize MPI on first timestep
IF(tstep .LT. 2) THEN
call MPI_INIT(mpierr)
ENDIF
! make string to send to python
astring = "TEST"
! MPI Test
call MPI_Comm_size(MPI_COMM_WORLD, size, mpierr)
call MPI_Comm_rank(MPI_COMM_WORLD, rank, mpierr)
! Send message to python
CALL MPI_SEND(astring, len(astring), MPI_CHARACTER, 0, 22, MPI_COMM_WORLD, mpierr)
print *, 'MPI MESSAGE SENT ', mpierr
! Initialize MPI on first timestep
IF(tstep .EQ. Nsteps-1) THEN
call MPI_FINALIZE(mpierr)
print *, 'MPI FINALIZED!'
ENDIF
我的Python代码如下:
from mpi4py import MPI
import numpy as np
import subprocess as sp
import os
# Start OW3D_SPH in the background and send MPI message
os.chdir('OW3D_run')
args = ['OceanWave3D_SPH','OW3D.inp']
pid = sp.Popen(args,shell=False)
os.chdir('..')
# Check if MPI is initialized
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
# Receive message from fortran
test = comm.recv(source=0, tag=22)
# Let the program end
output = pid.communicate()
with open('test.txt','w') as f:
f.write(test)
Python代码永远不会超过MPI接收命令而无法完成 . Fortran代码完成并正确打印“MPI FINALIZED”消息 .
我没有看到我做错了什么,消息从进程0发送到进程0并带有标记22并在两个代码中使用 MPI_COMM_WORLD
.
4 回答
An MPI process can spawn processes by using the function MPI_Comm_spawn(). 在python程序中,此函数是通信器的一种方法:
comm.Spawn()
. 有关示例,请参阅the mpi4py tutorial . The spawned process is ran according to an executable which could be another python program, a c/c++/fortran program or whatever you want. 然后,一个内部通信器可以merged在主进程和生成进程之间定义一个内部通信器,如mpi4py: Communicating between spawned processes中所执行的那样 the master process and the spawned processes can freely communicate without any restriction.我们来介绍一个Python / c示例 . Python代码生成进程并接收一个字符:
由
mpicc slave.c -o slavec -Wall
编译的C代码使用合并的通信器发送字符:让我们从C代码接收一个字符并将一个整数发送到fortran程序:
由
mpiCC slave.cpp -o slavecpp -Wall
编译的C程序非常接近C 1:最后,由
mpif90 slave.f90 -o slavef90 -Wall
编译的Fortran程序接收整数:通过对通信器的更多工作,“C进程”可以直接向“fortran进程”发送消息,甚至不涉及通信中的主进程 .
最后,以这种方式混合语言似乎很容易,但从长远来看,它可能不是一个好的解决方案 . 实际上,您可能会面临与演出相关的问题或维护系统可能会变得困难(三种语言......) . 对于C部分,Cython和F2PY可能是一个有 Value 的替代方案 . 毕竟,Python is a little bit like a glue ......
如果要在同一个MPI作业中启动Fortran程序和Python程序,则必须使用以下内容:
Fortran程序将成为MPI等级0,Python程序将成为MPI等级1.您还可以启动每个可执行文件中的多个,例如:
排名0和1将来自Fortran程序,排名2到5 - 来自Python的一个 .
另请注意,
comm.recv()
和mpi4py中以小写字母开头的其他通信方法(comm.send()
,comm.irecv()
等)使用了引擎盖下的Pickle,并且实际上使用了序列化的Python对象 . 这与Fortran代码发送的字符数组不兼容 . 您必须使用以大写字母(comm.Send()
,comm.Recv()
等)开头的通信方法,这些方法在NumPy阵列上运行并接收显式类型信息 . 不幸的是,我的Python fu很弱,我现在无法提供完整的工作示例,但MPI部分应该是这样的(未经验证的代码):在Fortran代码中,您必须指定目标等级1(在运行一个Fortran可执行文件和一个Python可执行文件的情况下) .
当两者都是不同的程序时,你当然不能同时拥有源0和目标0 . 你说"from process 0 to process 0"但你显然有两个不同的过程!其中一个有一些不同的排名,但你没有显示你的实际
mpirun
命令,所以很难说哪一个是哪个 .澄清一下:MPI_COM_WORLD是在你的mpirun或同等程序中执行的所有进程的通信器 . 你必须留下简单的思维图片,第一个Python进程是0级,第一个Fortran进程是0级,第一个C是0级...
如果你这样做
然后在MPI_COMM_WORLD中,Python程序将排名为0,Fortran进程将排名为1,C将排名为2.您可以创建仅限于Python子集或Fortran子集或C语言的本地通信器,您将获得排名每个都为0,但这将在不同的通信器中编号,而不是在MPI_COMM_WORLD中 .
我不会为此目的使用MPI(除非并行明确要求执行代码) . 如果您的目标是连接用Fortran,C和Python编写的例程,那么我建议在Python中编写(主要)连接部分,同时为Fortran和C例程创建适配器,以便在Python中导入它们 . 然后,您可以管理主Python程序中的所有函数调用,并根据需要发送数据 .
查看以下链接:
在Python中运行Fortran代码
f2py
现在附带numpy
,允许您将Fortran源代码编译为Python字节代码 .在Python中运行C代码
Extending Python with C or C++
Boost.Python