我正在尝试为我的代码编写一个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
我已经这样做了,但是太慢了。对我来说,最重要的事情之一是速度(即使对于很小的数据集,这也确实很耗时)。
我知道这种慢速模式是为了每次需要读取整个数据集以寻找目标点。该子例程在代码中被称为几次,对于其他步骤,代码将一遍又一遍地执行相同的操作,因此非常耗时。
如何使此代码更有效地工作?
答案 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坐标留在了文件中,以便您可以检查值是否确实是您想要的。总是很容易验证这些事情。