在使用MPI_Type_indexed进行文件输入输出的多态子程序时出现的未知问题

编程语言 2026-07-09

我有一个子程序,使用MPI_Type_indexed作为类型,将文件视图设定为输出来自CFD程序的数据。

该子程序有一个输入参数 local_data_array
它识别 local_data_array 的类型,
写入一个整型头部,包含数组的大小,
创建一个带全局数组位移的索引类型(其中包含一些索引处理,用于把矩阵分离出来)
在这些索引处写入数组。

代码是用 gfortran -fdec-format-defaults -ffree-line-length-none -g3 -fcheck=all -fbacktrace 编译的

并使用mpich-5.0.1,未进行任何特殊配置。

subroutine interleaved_datawrite(local_data_array)

        use utils, only: quicksort
        implicit none

        class(*),dimension(:,:),intent(in)        :: local_data_array
        integer :: i, j, k, bytsize, indexarray(npoin), arraysize, number_of_leaves
        TYPE(mpi_datatype) :: mpitype, MPIindexedtype
        integer(KIND=INT_ADDRESSING) :: blocksize

        select type (local_data_array)
            type is (integer(KIND=INT_FLAG))
                bytsize = 2
                mpitype = MPI_INTEGER2
            type is (integer(KIND=INT_INDEX))
                bytsize = 4
                mpitype = MPI_INTEGER4
            type is (integer(KIND=INT_ADDRESSING))
                bytsize = 8
                mpitype = MPI_INTEGER8
            type is (real(KIND=REAL_LOW_PREC))
                bytsize = 4
                mpitype = MPI_REAL4
            type is (real(KIND=REAL_HIGH_PREC))
                bytsize = 8
                mpitype = MPI_REAL8
            class default
                print*,"MISSING TYPING IN [datawrite] subroutine in output module"
                STOP
        end select

        number_of_leaves = size(local_data_array,2)

        arraysize = nptot * number_of_leaves

        blocksize = (arraysize * bytsize) + 8

        call MPI_BARRIER(MPI_COMM_WORLD, ierr)

        call MPI_File_set_view(file, offset, MPI_INTEGER8, MPI_INTEGER8, "native", MPI_INFO_NULL, ierr)
        call MPI_File_write_all(file, blocksize, 1 , MPI_INTEGER8, MPI_STATUS_IGNORE, ierr)

        offset = offset + 8

        indexarray = GblDomainDisps * number_of_leaves

        call quicksort(indexarray, 1, npoin, local_data_array)
! the quicksort subroutine here is sorting indexarray and then rearranging the local_data_array. such that indexarray is not all jumbled up as that causes other errors with mpi_indexed


        call MPI_BARRIER(MPI_COMM_WORLD, ierr)

        do i=1,number_of_leaves

            CALL MPI_Type_create_indexed_block(npoin, 1, indexarray, mpitype, MPIindexedtype, ierr)

            CALL MPI_Type_commit(MPIindexedtype, ierr)

            call MPI_File_set_view(file, offset, mpitype, MPIindexedtype, "native", MPI_INFO_NULL, ierr)

            call MPI_File_write_all(file, local_data_array(:,i), npoin , mpitype, MPI_STATUS_IGNORE, ierr)

            call MPI_Type_free(MPIindexedtype, ierr)

            indexarray = indexarray + 1 

        enddo

        offset = offset + arraysize * bytsize

    end subroutine

子程序在任何矩阵或数组下都会出错,例如,对于矩阵 Outarr,来自 ISO_FORTRAN_ENV 的一个 REAL32 类型数组。

        real(KIND=REAL32), dimension(npoin,3) :: Outarr

        call interleaved_datawrite(Outarr)

并产生如下错误:

Code: /..../mpich-5.0.1/src/binding/fortran/use_mpi_f08/wrappers_c/cdesc.c:33: cdesc_create_datatype: Assertion `cdesc->elem_len == size' failed.

据我理解,这似乎是 local_data_array 的大小可能与 mpitype*npoin 不一致的问题,但我手动检查过,情况并非如此。我也不确定这是不是某种更隐蔽的问题,或者可能是我打错了某些东西。

编辑:已移除编译选项,问题仍然存在。

解决方案

问题在于将一个 class(*) 变量传递给MPI,如果不在select-type块中显式执行写操作,MPI无法正确确定缓冲区中变量的长度。

下面这段代码有点偷懒,但能正常工作。

    subroutine interleaved_datawrite(local_data_array)

        use utils, only: quicksort
        implicit none

        class(*),intent(in)        :: local_data_array(:,:)
        integer :: i, j, k, bytsize, indexarray(npoin), arraysize, number_of_leaves
        TYPE(mpi_datatype) :: mpitype, MPIindexedtype
        integer(KIND=INT_ADDRESSING) :: blocksize

        select type (local_data_array)
            type is (integer(KIND=INT_FLAG))
                bytsize = 2
                mpitype = MPI_INTEGER2
            type is (integer(KIND=INT_INDEX))
                bytsize = 4
                mpitype = MPI_INTEGER4
            type is (integer(KIND=INT_ADDRESSING))
                bytsize = 8
                mpitype = MPI_INTEGER8
            type is (real(KIND=REAL_LOW_PREC))
                bytsize = 4
                mpitype = MPI_REAL4
            type is (real(KIND=REAL_HIGH_PREC))
                bytsize = 8
                mpitype = MPI_REAL8
            class default
                print*,"MISSING TYPING IN [datawrite] subroutine in output module"
                STOP
        end select

        number_of_leaves = size(local_data_array,2)

        arraysize = nptot * number_of_leaves

        blocksize = (arraysize * bytsize) + 8

        call MPI_BARRIER(MPI_COMM_WORLD, ierr)

        call MPI_File_set_view(file, offset, MPI_INTEGER8, MPI_INTEGER8, "native", MPI_INFO_NULL, ierr)
        call MPI_File_write_all(file, blocksize, 1 , MPI_INTEGER8, MPI_STATUS_IGNORE, ierr)

        offset = offset + 8

        indexarray = GblDomainDisps * number_of_leaves

        call quicksort(indexarray, 1, npoin, local_data_array)

        if (isize .eq. 1) then
            print*, 'Serial execution detected, serial write script enabled'
            call MPI_File_set_view(file, offset, mpitype, mpitype, "native", MPI_INFO_NULL, ierr)
            call MPI_File_write_all(file, local_data_array, npoin , mpitype, MPI_STATUS_IGNORE, ierr)
            offset = offset + arraysize * bytsize
            return
        endif

        call MPI_BARRIER(MPI_COMM_WORLD, ierr)

        do i=1,number_of_leaves

            CALL MPI_Type_create_indexed_block(npoin, 1, indexarray, mpitype, MPIindexedtype)

            CALL MPI_Type_commit(MPIindexedtype, ierr)

            call MPI_File_set_view(file, offset, mpitype, MPIindexedtype, "native", MPI_INFO_NULL, ierr)

            select type (local_data_array)
                type is (integer(KIND=INT_FLAG))
                    call MPI_File_write_all(file, local_data_array(:,i) , npoin , MPI_INTEGER2, MPI_STATUS_IGNORE, ierr)
                type is (integer(KIND=INT_INDEX))
                    call MPI_File_write_all(file, local_data_array(:,i) , npoin , MPI_INTEGER4, MPI_STATUS_IGNORE, ierr)
                type is (integer(KIND=INT_ADDRESSING))
                    call MPI_File_write_all(file, local_data_array(:,i) , npoin , MPI_INTEGER8, MPI_STATUS_IGNORE, ierr)
                type is (real(KIND=REAL_LOW_PREC))
                    call MPI_File_write_all(file, local_data_array(:,i) , npoin , MPI_REAL4, MPI_STATUS_IGNORE, ierr)
                type is (real(KIND=REAL_HIGH_PREC))
                    call MPI_File_write_all(file, local_data_array(:,i) , npoin , MPI_REAL8, MPI_STATUS_IGNORE, ierr)
                class default
                    print*,"MISSING TYPING IN [datawrite] subroutine in output module"
                    STOP
            end select

            call MPI_Type_free(MPIindexedtype, ierr)

            indexarray = indexarray + 1 

        enddo

        offset = offset + arraysize * bytsize

    end subroutine

最佳做法可能是把子程序重构为只保留一个select-type块,但为了让答案更易读,我就维持这样。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章