使用并行NetCDF保存分布式3D复杂阵列 [英] Using parallel NetCDF to save a distributed 3D complex array

查看:96
本文介绍了使用并行NetCDF保存分布式3D复杂阵列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用Fortran编写的基于MPI的程序,该程序在每个节点(2D时间序列的各个部分)上生成复杂数据的3D数组.我想使用并行I/O将这些数组写入单个文件,可以在python中相对容易地打开该文件以进行进一步的分析/可视化.理想情况下,我希望该解决方案能够提高内存效率(即避免创建中间临时数组).

I have an MPI-based program written in Fortran which produces a 3D array of complex data at each node (sections of a 2D time-series). I would like to use parallel I/O to write these arrays to a single file which can be relatively easily opened in python for further analysis/visualization. Ideally I would like the solution to be memory efficient (i.e. avoid the creation of intermediate temporary arrays).

使用NetCDF,我设法调整了子例程可以实现3D实数数组.但是,对于复杂数组,我遇到了绊脚石.

Using NetCDF, I have managed to adapt a subroutine which achieves this for a 3D array of real numbers. However, I've hit a stumbling block when it comes to complex arrays.

在下面的代码中,我试图通过创建由两个实数组成的复合数据类型,并假设Fortran复杂数据类型的实部和虚部部分连续存储在第一个维度上,来将子例程从实数扩展为复数.3D阵列.

In the following code I have attempted to extend the subroutine from reals to complex numbers by creating a compound datatype consisting of two reals and assuming that the real and imaginary components of the Fortran complex datatype are stored contiguously in the 1st dimension of the 3D array.

 module IO

    use NetCDF
    use, intrinsic :: iso_fortran_env, only: dp => real64

    implicit none

    contains

        subroutine output_3D(dataname, starts, ends, global_data_dims, &
            local_data, MPI_communicator)

        character(len=*), intent(in) :: dataname
        integer, dimension(3), intent(in) :: starts
        integer, dimension(3), intent(in) :: ends
        integer, dimension(3), intent(in) :: global_data_dims

        complex(dp), intent(in) :: local_data(   1:(ends(1) - starts(1)+ 1), &
                                                1:(ends(2) - starts(2) + 1), &
                                                1:(ends(3) - starts(3) + 1))

        integer, dimension(3) :: expanded_starts

        integer, intent(in) :: MPI_communicator

        integer :: ncid, varid, dimid(3)
        integer :: counts(3)

        integer :: typeid

        expanded_starts(1) = (starts(1))* 2 + 1
        expanded_starts = starts(2) 
        expanded_starts(3) = starts(3)

        call check(nf90_create( trim(dataname)//'.cdf', &
                                IOR(NF90_NETCDF4, NF90_MPIIO), &
                                ncid, &
                                comm = MPI_communicator, &
                                info = MPI_INFO_NULL))

        call check(nf90_def_dim(ncid, "x", global_data_dims(1), dimid(1)))
        call check(nf90_def_dim(ncid, "y", global_data_dims(2) * 2, dimid(2)))
        call check(nf90_def_dim(ncid, "z", global_data_dims(3), dimid(3)))

        ! define a complex data type consisting of two real(8)
        call check(nf90_def_compound(ncid, 16, "COMPLEX", typeid))
        call check(nf90_insert_compound(ncid, typeid, "REAL", 0, NF90_DOUBLE))
        call check(nf90_insert_compound(ncid, typeid, "IMAG", 8, NF90_DOUBLE))

        ! define a 3D variable of type "complex"
        call check(nf90_def_var(ncid, dataname, typeid, dimid, varid))

        ! exit define mode
        call check(nf90_enddef(ncid))

        ! Now in NETCDF data mode

        ! set to use MPI/PnetCDF collective I/O
        call check(nf90_var_par_access(ncid, varid, NF90_COLLECTIVE))

        counts(1) = (ends(1) - starts(1) + 1) * 2
        counts(2) = (ends(2) - starts(2) + 1)
        counts(3) = (ends(3) - starts(3) + 1)

        call check(nf90_put_var(ncid, &
                                varid, &
                                local_data, &
                                start = expanded_starts,&
                                count = counts))

        ! close the file
        call check(nf90_close(ncid))

        return

    end subroutine output_3D

    subroutine check(status)

        integer, intent ( in) :: status

        if(status /= nf90_noerr) then
            print *, trim(nf90_strerror(status))
            stop 2
        end if

    end subroutine check

end module IO

program test_write

    use IO
    use MPI

    complex(dp) :: data(2,2,3)

    integer :: flock
    integer :: rank
    integer :: ierr

    integer :: i, j, k

    call MPI_init(ierr)
    call MPI_comm_size(MPI_comm_world, flock, ierr)
    call MPI_comm_rank(MPI_comm_world, rank, ierr)

    do k = 1, 3
        do j = 1, 2
            do i = 1, 2
                data(i,j,k) = cmplx(i, j, 8)
            enddo
        enddo
    enddo

    if (rank == 0) then

        call output_3D_hdf5('out', [1,1,1], [2,2,3], [2,2,6], &
                data, MPI_comm_world)

    else

        call output_3D_hdf5('out', [1,1,4], [2,2,6], [2,2,6],  &
                data, MPI_comm_world)

    endif

    call MPI_finalize(ierr)

end program test_write

以上代码导致编译时出现"nf90_put_var没有特定功能"错误.这表明该函数对输入数组的数据类型不满意,因此很明显我在使用复合数据类型方面缺少一些东西.

The above code results in an "There is no specific function for nf90_put_var" error on compilation. This indicates that the function is not happy with the data type of the input array, so clearly there is something I'm missing regarding the usage of compound data types.

一个简单的解决方法是将复杂数组分配给真实指针,如

One simple workaround is to assign the complex array to a real pointer as described in this post. The array can then be reshaped/recast using numpy to arrive at the complex array in python. It's a bit clunky, and somewhat dissatisfying - but is probably good enough for my purposes for now.

推荐答案

出于以下原因,这仅是部分答案-注释太长.希望我能够找到丢失的信息并升级",但这是我到目前为止的信息.

This is only a partial answer for reasons you will see below - but it is too long for a comment. Hopefully I will be able to find the missing info and "upgrade" it, but this is what I have so far.

如果您在

要以复合类型写入数据,请首先使用 nf90_def_compound 创建类型,多次调用 nf90_insert_compound 添加到复合类型,然后使用适当的 nf90_put_var1 nf90_put_vara nf90_put_vars nf90_put_varm调用.

To write data in a compound type, first use nf90_def_compound to create the type, multiple calls to nf90_insert_compound to add to the compound type, and then write data with the appropriate nf90_put_var1, nf90_put_vara, nf90_put_vars, or nf90_put_varm call.

请注意,它根本没有提到 nf90_put_var ,而是4种不同的功能.这在某种意义上说, nf90_put_var 可能很好地重载了NetCDF支持的所有内在类型(并且完全废弃,它不支持复杂的),因此对于非内在类型,大概有一些类似于C的接口,带有 void * 之类的东西,我猜这是上述四个函数实现的.

Note it doesn't mention nf90_put_var at all, but 4 different functions. This makes some kind of sense, nf90_put_var is presumably nicely overloaded to deal with all intrinsic types NetCDF support (and it is utterly crap it doesn't support complex), so for non-intrinsic type presumably there is some C like interface with something like a void *, and I'm guessing it is this that the four functions mentioned above implement.

到目前为止,您应该调用 nf90_put_var1 nf90_put_vara nf90_put_vars nf90_put_varm 之一比 nf90_put_var .现在是个坏消息-我找不到这4个功能的任何文档.等效的C函数位于 https://www.unidata.ucar.edu/software/netcdf/docs/group__variables.html ,因此您也许可以从那里算出所需的内容,但这并不是很好-我至少会向Unidata提交错误报告,但这对我来说,缺乏对复杂性的内在支持足以让我在其他地方寻找我的I/O解决方案...

So far so good, you should call one of nf90_put_var1, nf90_put_vara, nf90_put_vars, or nf90_put_varm rather than nf90_put_var. Now the bad news - I can't find any documentation for these 4 functions. The equivalent C functions are at https://www.unidata.ucar.edu/software/netcdf/docs/group__variables.html so you might be able to work out what is required from there, but it's not very good - I'd at the very least put in a bug report to Unidata, but that said for me lack of intrinsic support for complex is enough to make me look elsewhere for my I/O solution ...

尽管我在这里确实不应该为变量的种类使用显式数字,但我可以向您展示 complex(8)无法编译的编译器.取而代之的是使用 Selected_real_kind 或类似的代码,或者使用固有模块iso_fortran_env中的常数,或者可能使用iso_c_binding中的常数,并且复数的种类与构成实数的种类相同的事实它.

While I am here you really shouldn't use explicit numbers for kinds for variables, I can show you compilers where complex(8) will fail to compile. Instead use Selected_real_kind or similar, or use the constant in the intrinsic module iso_fortran_env, or possibly those in iso_c_binding, and the fact that the kind of a complex number is the same as the kind of the reals that compose it.

这篇关于使用并行NetCDF保存分布式3D复杂阵列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆