Как быстрее прочитать файл данных с некоторым условием в Фортране? - PullRequest
0 голосов
/ 13 ноября 2018

Я пытаюсь записать подпрограмму на Фортране для моего кода, чтобы прочитать данные из файла (который является огромным набором данных сам по себе). Файл данных содержит Местоположение (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 голосов
/ 15 ноября 2018

Прежде чем я начну этот ответ, позвольте мне повторить то, что я сказал в комментариях к вашему вопросу:

Не стоит недооценивать, сколько данных вы можете поместить в один массив.Чтение один раз, а затем хранение всего в памяти по-прежнему является самым быстрым из возможных способов.

Но давайте предположим, что данные действительно становятся слишком большими.

Похоже, ваша главная проблема заключается в-читайте все данные с самого начала, пока не найдете нужное значение.Это занимает время.

Если вы можете рассчитать, какая строка файла данных представляет интересующее вас значение, это может помочь преобразовать файл в неотформатированный файл прямого доступа.

Здесьэто пример кода для преобразования.Он использует функции 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 в файле, чтобы вы могли проверить, действительно ли значения соответствуют вашим ожиданиям.Всегда хорошо проверять эти вещи.

...