如何在Fortran中更快地读取某些条件下的数据文件?

时间:2018-11-13 04:47:42

标签: file-io fortran

我正在尝试为我的代码编写一个Fortran子例程,以便从文件(本身是一个巨大的数据集)中读取数据。该数据文件包含位置(nx0,ny0,nz0)和与该位置相关的字段(Bx,By,Bz)。 (例如:假设nx0,ny0和nz0的范围是[-15,15]。 因此行数将为31 * 31 * 31 = 29791)

-15.00000       -15.00000       -15.00000      700.00000     -590.00000      100.00000
-15.00000       -15.00000       -14.00000     -110.00000     -570.00000      100.00000
-15.00000       -15.00000       -13.00000     -550.00000     -200.00000      100.00000
-15.00000       -15.00000       -12.00000     -540.00000     -230.00000      100.00000
-15.00000       -15.00000       -11.00000     -140.00000     -50.00000      100.00000
 .              .               .           .              .             .
 .              .               .           .              .             .
 .              .               .           .              .             .
 15.00000       15.00000         15.00000     140.00000       50.00000       100.000

我要做的是在文件中查找特定位置(xi,yi和zi),并读取与该位置相关的字段,然后将其用于进一步分析。不仅是与目标位置本身相关的场,而且还与该位置的周围场(如围绕目标点的正方形的另外三个边)。

   subroutine read_data(xi,yi,zi,Bxij,Byij)
   real*8,intent(in) :: xi,yi,zi !,time   
   real*8,intent(out) :: Bxij(4),Byij(4) !,Bzij(4)
   integer,parameter :: step = 1 ,cols = 6, rows = 29791  !!!15,000,000
   real,dimension(rows) :: nx0,ny0,nz0,Bx,By,Bz
   character*15 filein
   character*35 path_file

    path_file = '/home/mehdi/Desktop/'
    filein= 'test-0001' 
    open(7,file=trim(path_file)//filein, status='old',action='read')

xi_1 = xi +step
yi_1 = yi +step

do i = 1,rows
read(7,*) nx0(i),ny0(i),nz0(i),Bx(i),By(i),Bz(i) 
c
    if ( xi == nx0(i) .and. yi == ny0(i) .and.
 &  zi == nz0(i)) then
      Bxij(1) = Bx(i) 
      Byij(1) = By(i)
      cycle
    endif
c
    if ( xi == nx0(i) .and. yi_1 == ny0(i) .and.
 &  zi == nz0(i)) then
      Bxij(2) = Bx(i)
      Byij(2) = By(i)
      cycle
    endif
c        
    if ( xi_1 == nx0(i) .and. yi == ny0(i) .and.
 &  zi == nz0(i)) then
      Bxij(3) = Bx(i)
      Byij(3) = By(i)
      cycle
    endif
c
    if ( xi_1 == nx0(i) .and. yi_1 == ny0(i) .and.
 &  zi == nz0(i)) then
     Bxij(4) = Bx(i)
     Byij(4) = By(i)
     exit
    endif
c
    close(7) 
    enddo
end

我已经这样做了,但是太慢了。对我来说,最重要的事情之一是速度(即使对于很小的数据集,这也确实很耗时)。

我知道这种慢速模式是为了每次需要读取整个数据集以寻找目标点。该子例程在代码中被称为几次,对于其他步骤,代码将一遍又一遍地执行相同的操作,因此非常耗时。

如何使此代码更有效地工作?

1 个答案:

答案 0 :(得分:1)

在我开始回答之前,让我重申我在对您的问题的评论中所说的话:

不要低估单个数组中可以放入的数据量。读取一次然后将所有内容存储在内存中仍然是最快的方法。

但是我们假设数据确实太大了。

您的主要问题似乎是,您必须从头开始重新读取所有数据,直到找到所需的值。那需要时间。

如果您可以计算出您感兴趣的值是数据文件的哪一行,则可能有助于将文件转换为无格式的直接访问文件。

这是转换的示例代码。它使用的是Fortran 2008功能,因此,如果编译器无法执行此操作,则必须对其进行修改:

program convert

    use iso_fortran_env, only: real64
    implicit none

    integer, parameter :: reclength = 6*8 ! Six 8-byte values

    integer :: ii, ios
    integer :: u_in, u_out
    real(kind=real64) :: pos(3), B(3)

    open(newunit=u_in, file='data.txt', form='formatted', &
        status='old', action='read', access='sequential')
    open(newunit=u_out, file='data.bin', form='unformatted', &
        status='new', action='write', access='direct', recl=reclength)
    ii = 0

    do
        ii = ii + 1
        read(u_in, *, iostat=ios) pos, B
        if (ios /= 0) exit
        write(u_out, rec=ii) pos, B
    end do

    close(u_out)
    close(u_in)

end program convert

转换数据后,只要可以计算出哪一条记录,就只能读取所需的记录。我假设就像您的示例一样,z坐标变化最快,而x坐标变化最快。

program read_txt
    use iso_fortran_env, only: real64
    implicit none

    integer, parameter :: nx=601, ny=181, nz=61
    real(kind=real64), parameter :: x_min=real(-nx/2, kind=real64)
    real(kind=real64), parameter :: y_min=real(-ny/2, kind=real64)
    real(kind=real64), parameter :: z_min=real(-nz/2, kind=real64)
    real(kind=real64), parameter :: x_step = 1.0_real64
    real(kind=real64), parameter :: y_step = 1.0_real64
    real(kind=real64), parameter :: z_step = 1.0_real64

    real(kind=real64) :: request(3), pos(3), B(3)
    integer :: ios, u_in
    integer :: ii, jj, kk, record
    integer, parameter :: reclength = 6 * 8 ! Six 8-byte values

    open(newunit=u_in, file='data.bin', access='direct', form='unformatted', &
        status='old', action='read', recl=reclength)
    mainloop : do
        read(*, *, iostat=ios) request
        if (ios /= 0) exit mainloop
        write(*, '(A, 3F7.2)') 'searching for ', request
        ! Calculate record
        ii = nint((request(1)-x_min)/x_step)
        jj = nint((request(2)-y_min)/y_step)
        kk = nint((request(3)-z_min)/z_step)
        record = kk + jj * nz + ii * nz * ny + 1
        read(u_in, rec=record, iostat=ios) pos, B
        if (ios /= 0) then
            print *, 'failure to read'
            cycle mainloop
        end if
        write(*, '(2(A, 3F7.2))') "found pos: ", pos, " Bx, By, Bz: ", B
    end do mainloop
    close(u_in)
end program read_txt

请注意,未格式化的不是独立于编译器和系统的。在一台计算机或由一个编译器编译的程序上创建的文件可能无法在另一程序或另一台计算机上读取。

但是,如果您对此有控制权,那么它可能是加快处理速度的有用方法。

PS:我将x,y和z坐标留在了文件中,以便您可以检查值是否确实是您想要的。总是很容易验证这些事情。