Как ПРОЧИТАТЬ данные, начиная с сопоставленной по шаблону строки с помощью Фортрана? - PullRequest
0 голосов
/ 13 июля 2020

У меня есть файл pos.xyz со следующим форматом, где i = 6, и c представляют индексы кадров. (Здесь первый кадр имеет i = 6. Как правило, индекс первого кадра может быть i = 0, i = 1 или i = 2, ...) Я хочу реализовать функцию: для любых двух заданных целых чисел a и b, (a<b, например, 7 и 9) считывать данные из индекса кадра с 7 по 9 в массив. Не могли бы вы посоветовать, как реализовать эту идею?

    4
i =    6, time =      3.000, E =     -205.1846561900
O         2.6028572470        4.1666579520       12.7865910725
O         6.5415232423        8.8963227363       17.7533721708
O        15.6020396800       11.9022808314       15.2930838049
O        11.2843786793       13.2653367176       13.8186352548
    4
i =    7, time =    3.500, E =     -205.1845561905
O         5.1072569275       11.9945026418        4.1254340934
O         2.5299942732       11.4124710424        9.5495912455
O        14.8837181647       12.6571252157        7.8905997802
O        15.1684493877       10.7315923081        2.6631494700
       4
i =    8, time =    4.000, E =     -205.1846261900
O         2.6028572470        4.1666579520       12.7865910725
O         6.5415232423        8.8963227363       17.7533721708
O        15.6020396800       11.9922808314       15.2930838049
O        11.2843786793       13.2653367176       13.8186352548
    4
i =    9, time =    4.500, E =     -205.1846561805
O         5.1072569375       11.9945026418        4.1258340934
O         2.5299942732       11.4124710424        9.5495912455
O        14.8837181647       12.6570252157        7.8905997802
O        15.1684493877       10.7310923081        2.6630494700
    4
i =   10, time =    5.000, E =     -205.1846551805
O         5.1072569275       11.9945026418        4.1254340934
O         2.5299932732       11.4129710424        9.5495912455
O        14.8837181647       12.6571252157        7.8905997802
O        15.1684473877       10.7313923081        2.6631494700

что я сделал: для особого случая с i = 0 в качестве первого кадра. Например, если я хочу прочитать из 3-го кадра, я могу сначала пропустить (m+2)*(3-1) строк, а затем ПРОЧИТАТЬ данные, m=4. Функция выглядит следующим образом.

  SUBROUTINE skip_lines(indx, i_input)
    ! Purpose: 
    ! To skip lines when read data from the input
    IMPLICIT NONE
    INTEGER :: i
    INTEGER,INTENT(IN) :: i_input,indx
    do i=1,i_input
       read(indx,*) !Neglect (nat+2)*(ns-1) lines
    enddo    
 END SUBROUTINE skip_lines

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

Ответы [ 2 ]

0 голосов
/ 27 июля 2020
gfortran -Wall -fcheck=all parameter_shared.f95 atom_module.f95 traj.f95 sample.f95 test.f95 -o test.x
! test.f95
PROGRAM test 
  ! Purpose: To read data starting from any block.
  USE atom_module
  IMPLICIT NONE
  !==========
  !parameters
  !==========
  INTEGER :: ns  ! Get one sample from the trajectory every ns step.
  INTEGER :: nmo_start
  INTEGER :: nmo_end
  INTEGER :: nat  ! number of atoms
  REAL(kind=4) :: delta_t0   ! For reading data
  character(LEN=200) :: pos_filename
  !===============
  ! Initialization
  delta_t0 = 0.0005; ns = 2
  nmo_start = 7; nmo_end = 10 
  nat = 4; pos_filename="pos.xyz"
  !========================
  ! Sampling the trajectory
  CALL sample(pos_filename,nmo_start,nmo_end,nat,ns)
END PROGRAM test
! sample.f95
SUBROUTINE sample(pos_filename,nmo_start,nmo_end,nat,ns)
  USE parameter_shared
  USE atom_module, ONLY: atom_info
  USE traj
  IMPLICIT NONE
  !==========
  !Parameters
  !==========
  character(LEN=*), INTENT(IN) :: pos_filename
  INTEGER, INTENT(IN) :: nmo_start  
  INTEGER, INTENT(IN) :: nmo_end  
  INTEGER, INTENT(IN) :: nat ! number of atoms
  INTEGER, INTENT(IN) :: ns  ! Get one sample from the trajectory every ns step.
  !Local varables
  INTEGER :: n_samples  !n_samples = INT(nmo/ns)
  INTEGER :: iatom,imovie,i
  !Initialization
  iatom = 0; imovie =0; i =0
  ! Obatin n_samples
  n_samples = sampling_number(nmo_start,nmo_end,ns)
  allocate(sampled_movie(n_samples))
  allocate(sampled_time(n_samples))
  allocate(sampled_energy(n_samples))
  !=======================
  !read in trajectory file 
  !=======================
  open(10,file=trim(pos_filename))
  CALL read_traj(10,nmo_start,nmo_end,ns,nat,n_samples) 
  close(10)
  write(6,*) 'End of trajectory reading.'
  !=============
  !write in file
  !=============
  sampled_pos_filename = 'pos_sampled.xyz'
  open(10,file=sampled_pos_filename)
  do i =1,n_samples
    write (10,'(I8)') nat
    WRITE(10,100) 'i =',i-1,', time =',sampled_time(i),', E =',sampled_energy(i)
    100 FORMAT (1X,A3,I10,A8,F10.3,A5,F20.10)
    DO iatom = 1, nat
       WRITE(10,*) TRIM(atom_info(iatom, i)%atom_name), &
        atom_info(iatom,i)%coord(1), &
        atom_info(iatom,i)%coord(2), &
        atom_info(iatom,i)%coord(3)
    ENDDO
  enddo
  write(6,*)'Sampled trajectory is written in: ', sampled_pos_filename
  close(10)
  deallocate(sampled_movie, sampled_time,sampled_energy)
END SUBROUTINE sample
MODULE traj
IMPLICIT NONE
CONTAINS
  INTEGER FUNCTION sampling_number(nmo_start,nmo_end,ns)
    !To calculate the total numbers of samples one want to include 
    INTEGER,INTENT(IN) :: ns  ! Get one sample from the trajectory every ns step.
    INTEGER,INTENT(IN) :: nmo_start, nmo_end  

    write(*,*) 'In function sampling_number: nmo_end = ', nmo_end
    positive: IF (nmo_end <0 .OR. nmo_start < 0 .OR. ns <0) THEN
      write(*,*) 'Please enter non-negative values for the ns, starting step and ending step.'
    ELSE IF (nmo_end < nmo_start) THEN
      write(*,*) 'Please note that starting step shoud not larger than  ending step.'
    ELSE IF (ns ==0) THEN
      sampling_number = nmo_end-(nmo_start-1)
    ELSE IF (nmo_end-(nmo_start-1) <= ns) THEN
      sampling_number = INT((nmo_end-(nmo_start-1))/ns + 1)
    ELSE IF (nmo_end-(nmo_start-1) > ns) THEN
      sampling_number = INT((nmo_end-(nmo_start-1))/ns)
    END IF positive
  END FUNCTION sampling_number

  SUBROUTINE read_traj(indx,nmo_start,nmo_end,ns,nat,n_samples)
    ! Purpose: to READ data starting from a pattern-matched line.
    USE atom_module, ONLY: atom_info
    USE parameter_shared, ONLY: sampled_movie, sampled_time, sampled_energy

    INTEGER :: iatom,i_sample
    INTEGER, INTENT(IN) :: nat
    INTEGER, INTENT(IN) :: n_samples  !n_samples = INT(nmo/ns)
    INTEGER, INTENT(IN) :: indx
    INTEGER, INTENT(IN) :: ns  ! Get one sample from the trajectory every ns step.
    INTEGER, INTENT(IN) :: nmo_start, nmo_end  ! To get the total number of moves
    CHARACTER(LEN=4) :: head_char
    INTEGER :: y

    allocate(atom_info(nat,n_samples))
    i_sample = 1
    write(*,*) "read_traj(): New total time steps (n_samples):", n_samples
    DO WHILE (i_sample < n_samples+1) ! +1 means i_sample can take the value of n_samples 
        read(indx, '(A4)') head_char
        PRE_CHECK:IF (head_char=="i = ") THEN
            BACKSPACE(UNIT=indx) ! Because I am not able to read other lines with the format '(A4,I8)', and have not find any good way, so I try to read it in '(A4)' first 
            read(indx, '(A4,I8)') head_char, y
            CHECK_HEAD:IF (head_char=="i = " .AND. (y>nmo_start-1 .and. y<nmo_end+1) .AND. MOD(y-(nmo_start-1),ns) == 1) THEN
                WRITE(*,*)"read_traj():", head_char, y
                BACKSPACE(UNIT=indx) ! Because we have to read the whole line with ' i = ' line.
                read(indx,130) sampled_movie(i_sample), sampled_time(i_sample), sampled_energy(i_sample)
                130 FORMAT (4X,I8,9X,F12.3,6X,F20.10)
                131 FORMAT (A4,3F20.10)
                inner: do iatom= 1,nat
                  read (indx,131) atom_info(iatom, i_sample)%atom_name, atom_info(iatom,i_sample)%coord(1), & 
                    atom_info(iatom,i_sample)%coord(2), atom_info(iatom,i_sample)%coord(3)
                enddo inner
                i_sample = i_sample + 1 
            ENDIF CHECK_HEAD
        ENDIF PRE_CHECK
    END DO
  END SUBROUTINE read_traj
END MODULE traj
MODULE atom_module
! To define the derived data type for atom
IMPLICIT NONE
TYPE :: atom
  CHARACTER(LEN=2) :: atom_name
  INTEGER :: atom_id 
  INTEGER :: host_id  ! For O atom in water, host_id = atom_id
  REAL :: mass
  REAL, DIMENSION(3) :: coord 
END TYPE atom

! The array atom_info can be shared by subroutines  
TYPE(atom), ALLOCATABLE, DIMENSION(:,:) :: atom_info
END MODULE atom_module
MODULE parameter_shared
!
! Purpose:
!   To declare data to share between routines.
IMPLICIT NONE
!SAVE 
character(LEN=200) :: sampled_pos_filename
INTEGER, ALLOCATABLE, DIMENSION(:) :: sampled_movie
REAL, ALLOCATABLE, DIMENSION(:) :: sampled_time, sampled_energy
END MODULE parameter_shared
0 голосов
/ 23 июля 2020

Благодаря предложениям @francescalus и @High Performance Mark. Я использую DO WHILE l oop и реализовал свою идею. Я поместил здесь одну упрощенную версию моей подпрограммы. Он включает некоторые типы, определенные в модулях, которые здесь не важны. Теперь он может (1) читать файл траектории от любого шага a до любого шага b, где a и b задаются пользователем; (2) Считывать данные каждые ns шагов.

    SUBROUTINE read_traj(indx,nmo_start,nmo_end,ns,nat,n_samples)
    ! goal:
    ! read info from the trajectory file (format: ***.xyz)
    ! read data from frame a to frame b
    USE atom_module
    USE parameter_shared
    INTEGER :: iatom, i_sample
    INTEGER, PARAMETER:: nat = 4
    INTEGER :: n_samples  !n_samples = INT((a-b)/ns)
    INTEGER, PARAMETER :: indx = 10
    INTEGER, PARAMETER :: ns = 2  ! read one sample from the trajectory every ns step.
    INTEGER, PARAMETER :: a =7 
    INTEGER, PARAMETER :: b=10  
    CHARACTER(LEN=4) :: x
    INTEGER :: y

    allocate(atom_info(nat,n_samples))
    i_sample = 1
    DO WHILE (i_sample < n_samples) 
        read(indx, '(A3,I5)') x, y
        CHECK: IF (head_char=="i = " .AND. (y>a-1 .and. y<b+1) .AND. MOD(y-(a-1),ns) == 1) THEN
          WRITE(*,*)"head_char and y:", x, y
          BACKSPACE(UNIT=indx) ! we have to read the whole line with ' i = ' line.
          read(indx,120) sampled_movie(i_sample), sampled_time(i_sample), sampled_energy(i_sample)
          120 FORMAT (3X,I5,8X,F9.3,5X,F20.10)
          inner: do iatom= 1,nat
            read (indx,*) atom_info(iatom, i_sample)%atom_name, atom_info(iatom,i_sample)%coord(1), & 
              atom_info(iatom,i_sample)%coord(2), atom_info(iatom,i_sample)%coord(3)
          enddo inner
          i_sample = i_sample + 1 
        ENDIF CHECK
    END DO
  END SUBROUTINE read_traj
...