Python 将数组从.npy文件读入Fortran 90

Python 将数组从.npy文件读入Fortran 90,python,arrays,fortran,Python,Arrays,Fortran,我正在使用Python生成一些初始数据,以2D数组的形式,例如“X”,然后使用Fortran对它们进行一些计算。最初,当数组大小约为10000 x 10000时,np.savetxt在速度方面运行良好。但一旦我开始增加数组的维数,savetxt的速度就会显著降低。因此,我尝试了np.save,这导致了更快的保存速度,但文件是以.npy格式保存的。我如何在Fortran中读取这样一个文件来重建原始数组?据我所知,二进制通常会导致最低的空间消耗和最快的速度 在Fortran 90中 open(10,

我正在使用Python生成一些初始数据,以2D数组的形式,例如“X”,然后使用Fortran对它们进行一些计算。最初,当数组大小约为10000 x 10000时,np.savetxt在速度方面运行良好。但一旦我开始增加数组的维数,savetxt的速度就会显著降低。因此,我尝试了np.save,这导致了更快的保存速度,但文件是以.npy格式保存的。我如何在Fortran中读取这样一个文件来重建原始数组?据我所知,二进制通常会导致最低的空间消耗和最快的速度

在Fortran 90中

open(10,file='/home/X.npy')

read(10,*)X
我得到以下错误:Fortran运行时错误:列表输入的第1项中存在错误的实数

编辑: 在python中,我这样做

import numpy as np 
x = np.linspace(-50,50,10)
y = np.linspace(-50,50,10) 
X,Y = np.meshgrid(x,y) 
np.save('X',X) 
program read_unformatted_binary_from_python
    use iso_c_binding
    implicit none

    integer, parameter :: DPR = selected_real_kind(p=15)
    character(len=*), parameter :: filename = 'X.npy'

    integer, parameter :: N = 10
    real(DPR), allocatable, dimension(:, :) :: X

    allocate(X(N, N))


    open(40, file=filename, status='old', access='stream', form='unformatted')
    read(40) X
    close(40)

    write(*,*) X

end program read_unformatted_binary_from_python
然后用Fortran语言我做这个

import numpy as np 
x = np.linspace(-50,50,10)
y = np.linspace(-50,50,10) 
X,Y = np.meshgrid(x,y) 
np.save('X',X) 
program read_unformatted_binary_from_python
    use iso_c_binding
    implicit none

    integer, parameter :: DPR = selected_real_kind(p=15)
    character(len=*), parameter :: filename = 'X.npy'

    integer, parameter :: N = 10
    real(DPR), allocatable, dimension(:, :) :: X

    allocate(X(N, N))


    open(40, file=filename, status='old', access='stream', form='unformatted')
    read(40) X
    close(40)

    write(*,*) X

end program read_unformatted_binary_from_python
输出从1.8758506894003703E-309 1.1711999948023422E+171 5.2274167985502976E-037 8.4474009688929314E+2522.6514123210345660E+180 9.9215260506210430473E+247 2.162099676999460E+2337.5805790251297605E-096 3.4756671925988047E-152 6.554909140876423E-260-50.000000000000000-38.888886开始-27.779等等


使用.bin格式时不会发生此错误,仅当使用导致npy的np.save时才会发生此错误。

Vladimir F正确地指出,您希望对Fortran中的“原始二进制”文件进行“流式”访问。这里有一个MWE:

蟒蛇

import numpy as np
A = np.random.rand(10000, 10000)
print(A.sum())
A.tofile('data.bin')
Fortran

program read_unformatted_binary_from_python
    use iso_c_binding
    implicit none

    integer, parameter :: DPR = selected_real_kind(p=15)
    character(len=*), parameter :: filename = 'data.bin'

    integer, parameter :: N = 10000
    real(DPR), allocatable, dimension(:, :) :: dat

    allocate(dat(N, N))

    open(40, file=filename, status='old', access='stream', form='unformatted')
    read(40) dat
    close(40)

    write(*,*) sum(dat)

end program read_unformatted_binary_from_python
我的Fortran示例可能比需要的要长一点,因为我使用许多不同的系统和编译器套件,也讨厌大型静态数组(毕竟我是Fortran用户)

我在MacBook Pro上用自制GCC 6.3.01中的Python 2.7.x、Numpy 13.x和gfortran快速编写了这段代码,但这应该适用于所有系统

更新: 这里需要特别注意阵列的形状和大小。如果
dat
被分配为大于文件中的值,则流
读取
应尝试填充整个数组,点击
EOF
符号,并发出错误。在Python中,
np.fromfile()
方法将最多读取
EOF
,然后返回具有适当长度的1D数组,即使
a
最初是多维的。这是因为原始二进制文件没有元数据,只是RAM中连续的字节字符串

因此,Python中的以下行生成相同的文件:

A = np.random.rand(10000, 10000)
A.tofile('file.whatever')
A.ravel().tofile('file.whatever')
A.reshape((100, 1000, 1000)).tofile('file.whatever')
该文件可以读入并重新格式化为:

B = np.fromfile('file.whatever').reshape(A.shape)
B = np.fromfile('file.whatever').reshape((100, 1000, 100, 10))
# or something like
B = np.fromfile('file.whatever') # just a 1D array
B.resize(A.shape)  # resized in-place
在Fortran中,使用流访问很容易读取整个原始文件,而无需提前知道其大小,不过显然需要某种用户输入来重塑数据:

program read_unformatted_binary_from_python
    use iso_c_binding
    implicit none

    integer, parameter :: DPR = selected_real_kind(p=15)
    character(len=*), parameter :: filename = 'data.bin'
    integer :: N = 10000, bytes, reals, M
    real(DPR), allocatable :: A(:,:), D(:, :), zeros(:)
    real(DPR), allocatable, target :: B(:)
    real(DPR), pointer :: C(:, :)

    allocate(A(N, N))

    open(40, file=filename, status='old', access='stream', form='unformatted')

    read(40) A
    write(*,*) 'sum of A', sum(A)

    inquire(unit=40, size=bytes)
    reals = bytes/8
    allocate(B(reals))

    read(40, pos=1) B
    write(*,*) 'sum of B', sum(B)

    ! now reshape B in-place assuming the user wants the first dimension 
    ! (which would be the outer dimension in Python) to be length 100
    N = 100
    if(mod(reals, N) == 0) then
         M = reals/N
         call C_F_POINTER (C_LOC(B), C, [N, M])
         write(*, *) 'sum of C', sum(C)
         write(*, *) 'shape of C', shape(C)
    else
         write(*,*) 'file size is not divisible by N!, did not point C to B'
    end if

    ! now reshape B out-of-place with first dimension length 9900, and
    ! pad the result so that there is no size mismatch error  
    N = 9900
    M = reals/N
    if(mod(reals, N) > 0) M=M+1

    allocate(D(N, M))
    allocate(zeros(N), source=real(0.0, DPR))
    D = reshape(B, [N, M], pad=zeros)

    write(*,*) 'sum of D', sum(D)
    write(*,*) 'shape of D', shape(D)

    ! obviously you can also work with shape(A) in fortran the same way you
    ! would use A.shape() in Python, if you already knew that A was the
    ! correct shape of the data
    deallocate(D)
    allocate(D, mold=A)
    D = reshape(B, shape(A))
    write(*,*) 'sum of D', sum(D)
    write(*,*) 'shape of D', shape(D)

    ! or, just directly read it in, skipping the whole reshape B part
    read(40, pos=1) D
    write(*,*) 'sum of D', sum(D)

    close(40)

end program read_unformatted_binary_from_python

Fortran无法自动读取和正确解释任意二进制文件。您必须找到或编写特定于该格式的例程。这是一个相对完善的文档,你最喜欢的搜索引擎将帮助你找到它的描述。在此过程中,错误消息表明文件的前4(或可能8)个字节不能解释为Fortran
real
。更糟糕的是,它已在代码段中打开以进行格式化读取,因此它试图将数字作为文本读取。那不行。您谈论的是二进制文件,但您没有打开二进制(未格式化)I/O文件,并且始终使用标记,以便更多的人可以看到您的问题。Fortran 90只是一个过时的旧版本。Fortran 90不足以完成此任务,您需要Fortran 2003中的流I/O。对,没有注意到……我还读到HDF5非常适合处理HPC环境中出现的大数据量……您认为这是二进制的好替代方案吗?Fortran中显然还有一个numpy库可用于读取.npy文件。请参阅本烹饪书的底部,其中包含libnpy的下载链接:很棒的东西!我还没有在我的电脑上尝试,但这是向后兼容的吗?与中一样,“流”在F 90中工作还是仅在2003年及更高版本中工作?libnpy似乎是一个很好的工具,它将检查outStream是否为fortran2003。我已经说过了,所以它在Fortran 90中不起作用。Fortran 90已经过时,没有人使用它。有些人使用Fortran 95,是的,但通常他们还必须使用许多非标准扩展。Fortran 90已经死了。除了一些非常旧的版本外,没有Fortran 90,所以不用担心。我明白了……另一方面,当我用一个10 x 10的数组运行上述代码时,其中的数字范围为-50到50,当我写出数组时,它不仅显示-50到50,而且在标题部分显示非常大的数字。我如何摆脱这些,使它不会影响进一步的计算?见我的更新上面,但TL;DR:原始二进制文件没有元数据,因此
fromfile()
只是将数据读取到一个连续的1D数组中,但您始终可以
restrape()
resize()
数组