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

时间:2014-11-24 01:21:46

标签: types fortran mpi fortran90

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

我无法理解如何使用MPI_type命令发送自定义类型。特别是在"点"中具有双重复杂精度。类型。据我所知,MPI没有MPI_DOUBLE_COMPLEX类型。

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

1 个答案:

答案 0 :(得分:4)

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

  

MPI需要支持这些数据类型,这些数据类型与Fortran和ISO C的基本数据类型相匹配。如果主机语言具有其他数据类型,则应提供其他MPI数据类型:Fortran中的双精度复杂的MPI_DOUBLE_COMPLEX声明为类型{{ 1}}; ...

至于数据类型的构造,它的工作原理如下。首先,声明一个类型的虚拟双元素数组或(简单地使用已经分配的数组)与DOUBLE COMPLEX组合以获取每个字段的地址:

MPI_GET_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) 现在包含绝对地址,因此将它们转换为相对于offsets的偏移量:

dummy%x

下一步是构建MPI数据类型本身:

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

(注意:此代码假定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))

那时你已经准备好了。 DOUBLE PRECISION可以提交,然后用于发送自定义类型的单个元素,但它可能不适用于数组。原因是编译器可能在类型的结尾或开头添加填充。 MPI允许显式设置数据类型的范围。要做到这一点,首先要确定应该有的实际范围:

point_type0

(这应该清楚为什么你需要一个双元素的虚拟数组)

现在将点数据类型“调整”为真实范围并提交结果数据类型:

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被用作基础。

由于您的序列类型可能会阻止编译器对齐字段,因此上述某些步骤在您的情况下可能不是绝对必要的。尽管如此,总是以显示的方式执行它是一个好主意,因为它既适用于打包类型也适用于打包类型。如果缺少类型定义中的dummy%x语句,则允许编译器以其认为合适的任何顺序存储组件。这没有任何改变,因为即使sequence不是第一个组件,它之前的组件也只会有负偏移。