Types 使用MPI和Fortran 90发送自定义类型

Types 使用MPI和Fortran 90发送自定义类型,types,fortran,mpi,fortran90,Types,Fortran,Mpi,Fortran90,在我的程序中,我让每个处理器计算一个自定义类型“点”数组,该数组由4个双精度值和3个双复数值组成。现在,我希望每个处理器将其“点”类型的数组发送到主处理器 我很难理解如何使用MPI_type命令发送自定义类型。特别是在“点”类型中具有双复精度。据我所知,MPI没有MPI\u DOUBLE\u复杂类型 PROGRAM hello use mpi IMPLICIT NONE INTEGER, PARAMETER :: dp = REAL(KIND(0.0D0)) type point

在我的程序中,我让每个处理器计算一个自定义类型“点”数组,该数组由4个双精度值和3个双复数值组成。现在,我希望每个处理器将其“点”类型的数组发送到主处理器

我很难理解如何使用MPI_type命令发送自定义类型。特别是在“点”类型中具有双复精度。据我所知,MPI没有MPI\u DOUBLE\u复杂类型

PROGRAM hello
  use mpi
  IMPLICIT NONE
  INTEGER, PARAMETER :: dp = REAL(KIND(0.0D0))
  type point
    sequence
    real(kind=dp) :: x, y, z, w
    double complex :: ex, ey, ez
  end type point
  type(point), dimension(:), allocatable :: my_points
  ...

  if(rank .ne. master) then
    call MPI_SEND(my_points, count, [point?], master, 7, MPI_COMM_WORLD, ierror)
  end if

  ....
END
MPI标准规定(MPI 2.2规范中的§3.2.2):

MPI需要支持这些数据类型,这些数据类型与Fortran和ISO C的基本数据类型相匹配。如果主机语言具有其他数据类型,则应提供其他MPI数据类型:MPI_DOUBLE_COMPLEX for DOUBLE precision COMPLEX for Fortran声明为类型
DOUBLE COMPLEX

至于数据类型的构造,它是这样工作的。首先,声明一个或(仅使用已分配的)类型的伪两元素数组,并结合
MPI\u GET\u ADDRESS
来获取每个字段的地址:

type(point) :: dummy(2)
integer(kind=MPI_ADDRESS_KIND) :: offsets(7)
integer :: ierr

call MPI_GET_ADDRESS(dummy(1)%x,  offsets(1), ierr)
call MPI_GET_ADDRESS(dummy(1)%y,  offsets(2), ierr)
call MPI_GET_ADDRESS(dummy(1)%z,  offsets(3), ierr)
call MPI_GET_ADDRESS(dummy(1)%w,  offsets(4), ierr)
call MPI_GET_ADDRESS(dummy(1)%ex, offsets(5), ierr)
call MPI_GET_ADDRESS(dummy(1)%ey, offsets(6), ierr)
call MPI_GET_ADDRESS(dummy(1)%ez, offsets(7), ierr)
偏移量
现在包含绝对地址,因此将它们转换为相对于
虚拟%x的偏移量

integer :: i

do i = 2,7
  offsets(i) = offsets(i) - offsets(1)
end do
! dummy%x serves as base address, therefore set the offset to 0
offsets(1) = 0
下一步是构造MPI数据类型本身:

integer :: oldtypes(7), lengths(7)
integer :: point_type0

! dummy%x
oldtypes(1) = MPI_DOUBLE_PRECISION
lengths(1) = 1
! dummy%y
oldtypes(2) = MPI_DOUBLE_PRECISION
lengths(2) = 1
! dummy%z
oldtypes(3) = MPI_DOUBLE_PRECISION
lengths(3) = 1
! dummy%w
oldtypes(4) = MPI_DOUBLE_PRECISION
lengths(4) = 1
! dummy%ex
oldtypes(5) = MPI_DOUBLE_COMPLEX
lengths(5) = 1
! dummy%ey
oldtypes(6) = MPI_DOUBLE_COMPLEX
lengths(6) = 1
! dummy%ez
oldtypes(7) = MPI_DOUBLE_COMPLEX
lengths(7) = 1

call MPI_TYPE_CREATE_STRUCT(7, lengths, offsets, oldtypes, point_type0, ierr)
(注意:此代码假定
real(kind=kind(0.0D0))
实际上是
双精度

在这一点上,你几乎准备好了
point_type0
可以提交,然后用于发送自定义类型的单个元素,但它可能不适用于数组。原因是编译器可能会在类型的末尾或开头添加填充。MPI允许显式设置数据类型的范围。为此,首先确定实际范围应为:

integer(kind=MPI_ADDRESS_KIND) :: extent

! Reuse the offsets array
call MPI_GET_ADDRESS(dummy(1)%x, offsets(1), ierr)
call MPI_GET_ADDRESS(dummy(2)%x, offsets(2), ierr)

extent = offsets(2) - offsets(1)
(这应该清楚地说明为什么需要两个元素的虚拟数组)

现在将点数据类型“调整大小”到真实范围,并提交生成的数据类型:

integer :: point_type

call MPI_TYPE_CREATE_RESIZED(point_type0, 0_MPI_ADDRESS_KIND, extent, &
                             point_type, ierr)
call MPI_TYPE_COMMIT(point_type, ierr)
现在,您可以使用新注册的类型:

if(rank .ne. master) then
  call MPI_SEND(my_points(1)%x, count, point_type, &
                master, 7, MPI_COMM_WORLD, ierror)
end if
请注意,第一个数组元素的
x
字段是显式给出的。之所以这样做,是因为在计算偏移量时使用了
虚拟%x


上面的一些步骤在您的情况下可能不是严格必需的,因为您有一个序列类型,这可能会阻止编译器对齐字段。尽管如此,始终按照所示的方式进行操作仍然是一个好主意,因为它同时适用于压缩类型和非压缩类型。如果类型定义中缺少
sequence
语句,则允许编译器以其认为合适的任何顺序存储组件。这不会改变什么,因为即使
dummy%x
不是第一个组件,在它之前的组件也会有负偏移量。

Nice。
real(kind=kind(0.0D0))
不是被标准保证为
DOUBLE-PRECISION
吗?它似乎也能够使用
call-MPI\u-SEND(my\u-points,count,point\u-type,master,7,MPI\u-COMM\u-WORLD,ierror)
call-MPI\u-RECV(my\u-points,count,point\u-type,from,7,MPI\u-COMM-WORLD,status,ierror)
它之所以有效,是因为
x
是第一个字段,并且是序列类型。但是在一般情况下,如果只给出数组的名称,也就是说,如果字段由编译器重新排列,它将不起作用。@VladimirF,老实说,我对Fortran标准的了解非常有限。MPI标准规定两者应等效。MPI标准中有一些条款支持可移植类型的构造,例如
MPI\u TYPE\u CREATE\u F90\u REAL
,但这在实践中似乎没有得到很好的支持;我们应该指出与此类似的问题,作为fortran中mpi_type_create_struct的标准答案。值得注意的是,
DOUBLE COMPLEX
是非标准供应商扩展,而不是标准fortran。我建议改为
complex(dp)