LCOV - code coverage report
Current view: top level - src/pw - realspace_grid_cube.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:34ef472) Lines: 370 436 84.9 %
Date: 2024-04-26 08:30:29 Functions: 5 5 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief Generate Gaussian cube files
      10             : ! **************************************************************************************************
      11             : MODULE realspace_grid_cube
      12             :    USE cp_files,                        ONLY: close_file,&
      13             :                                               open_file
      14             :    USE cp_log_handling,                 ONLY: cp_logger_get_default_io_unit
      15             :    USE kinds,                           ONLY: dp
      16             :    USE message_passing,                 ONLY: &
      17             :         file_amode_rdonly, file_offset, mp_comm_type, mp_file_descriptor_type, mp_file_type, &
      18             :         mp_file_type_free, mp_file_type_hindexed_make_chv, mp_file_type_set_view_chv, &
      19             :         mpi_character_size
      20             :    USE pw_grid_types,                   ONLY: PW_MODE_LOCAL
      21             :    USE pw_types,                        ONLY: pw_r3d_rs_type
      22             : #include "../base/base_uses.f90"
      23             : 
      24             :    IMPLICIT NONE
      25             : 
      26             :    PRIVATE
      27             : 
      28             :    PUBLIC :: pw_to_cube, cube_to_pw, pw_to_simple_volumetric
      29             : 
      30             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'realspace_grid_cube'
      31             :    LOGICAL, PARAMETER, PRIVATE          :: debug_this_module = .FALSE.
      32             :    LOGICAL, PRIVATE                     :: parses_linebreaks = .FALSE., &
      33             :                                            parse_test = .TRUE.
      34             : 
      35             : CONTAINS
      36             : 
      37             : ! **************************************************************************************************
      38             : !> \brief ...
      39             : !> \param pw ...
      40             : !> \param unit_nr ...
      41             : !> \param title ...
      42             : !> \param particles_r ...
      43             : !> \param particles_z ...
      44             : !> \param stride ...
      45             : !> \param zero_tails ...
      46             : !> \param silent ...
      47             : !> \param mpi_io ...
      48             : ! **************************************************************************************************
      49        1890 :    SUBROUTINE pw_to_cube(pw, unit_nr, title, particles_r, particles_z, stride, zero_tails, &
      50             :                          silent, mpi_io)
      51             :       TYPE(pw_r3d_rs_type), INTENT(IN)                   :: pw
      52             :       INTEGER, INTENT(IN)                                :: unit_nr
      53             :       CHARACTER(*), INTENT(IN), OPTIONAL                 :: title
      54             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
      55             :          OPTIONAL                                        :: particles_r
      56             :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: particles_z
      57             :       INTEGER, DIMENSION(:), OPTIONAL, POINTER           :: stride
      58             :       LOGICAL, INTENT(IN), OPTIONAL                      :: zero_tails, silent, mpi_io
      59             : 
      60             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pw_to_cube'
      61             :       INTEGER, PARAMETER                                 :: entry_len = 13, num_entries_line = 6
      62             : 
      63             :       INTEGER :: checksum, dest, handle, i, I1, I2, I3, iat, ip, L1, L2, L3, msglen, my_rank, &
      64             :          my_stride(3), np, num_linebreak, num_pe, rank(2), size_of_z, source, tag, U1, U2, U3
      65             :       LOGICAL                                            :: be_silent, my_zero_tails, parallel_write
      66        1890 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: buf
      67             :       TYPE(mp_comm_type)                                 :: gid
      68             :       TYPE(mp_file_type)                                 :: mp_unit
      69             : 
      70        1890 :       CALL timeset(routineN, handle)
      71             : 
      72        1890 :       my_zero_tails = .FALSE.
      73        1890 :       be_silent = .FALSE.
      74        1890 :       parallel_write = .FALSE.
      75        1890 :       IF (PRESENT(zero_tails)) my_zero_tails = zero_tails
      76        1890 :       IF (PRESENT(silent)) be_silent = silent
      77        1890 :       IF (PRESENT(mpi_io)) parallel_write = mpi_io
      78        7560 :       my_stride = 1
      79        1890 :       IF (PRESENT(stride)) THEN
      80        1842 :          IF (SIZE(stride) /= 1 .AND. SIZE(stride) /= 3) &
      81             :             CALL cp_abort(__LOCATION__, "STRIDE keyword can accept only 1 "// &
      82        1842 :                           "(the same for X,Y,Z) or 3 values. Correct your input file.")
      83        1842 :          IF (SIZE(stride) == 1) THEN
      84          88 :             DO i = 1, 3
      85          88 :                my_stride(i) = stride(1)
      86             :             END DO
      87             :          ELSE
      88        7280 :             my_stride = stride(1:3)
      89             :          END IF
      90        1842 :          CPASSERT(my_stride(1) > 0)
      91        1842 :          CPASSERT(my_stride(2) > 0)
      92        1842 :          CPASSERT(my_stride(3) > 0)
      93             :       END IF
      94             : 
      95        1890 :       IF (.NOT. parallel_write) THEN
      96          70 :          IF (unit_nr > 0) THEN
      97             :             ! this format seems to work for e.g. molekel and gOpenmol
      98             :             ! latest version of VMD can read non orthorhombic cells
      99          35 :             WRITE (unit_nr, '(a11)') "-Quickstep-"
     100          35 :             IF (PRESENT(title)) THEN
     101          35 :                WRITE (unit_nr, *) TRIM(title)
     102             :             ELSE
     103           0 :                WRITE (unit_nr, *) "No Title"
     104             :             END IF
     105             : 
     106          35 :             CPASSERT(PRESENT(particles_z) .EQV. PRESENT(particles_r))
     107          35 :             np = 0
     108          35 :             IF (PRESENT(particles_z)) THEN
     109          35 :                CPASSERT(SIZE(particles_z) == SIZE(particles_r, dim=2))
     110             :                ! cube files can only be written for 99999 particles due to a format limitation (I5)
     111             :                ! so we limit the number of particles written.
     112          35 :                np = MIN(99999, SIZE(particles_z))
     113             :             END IF
     114             : 
     115          35 :             WRITE (unit_nr, '(I5,3f12.6)') np, 0.0_dp, 0._dp, 0._dp !start of cube
     116             : 
     117          35 :             WRITE (unit_nr, '(I5,3f12.6)') (pw%pw_grid%npts(1) + my_stride(1) - 1)/my_stride(1), &
     118          35 :                pw%pw_grid%dh(1, 1)*REAL(my_stride(1), dp), pw%pw_grid%dh(2, 1)*REAL(my_stride(1), dp), &
     119          70 :                pw%pw_grid%dh(3, 1)*REAL(my_stride(1), dp)
     120          35 :             WRITE (unit_nr, '(I5,3f12.6)') (pw%pw_grid%npts(2) + my_stride(2) - 1)/my_stride(2), &
     121          35 :                pw%pw_grid%dh(1, 2)*REAL(my_stride(2), dp), pw%pw_grid%dh(2, 2)*REAL(my_stride(2), dp), &
     122          70 :                pw%pw_grid%dh(3, 2)*REAL(my_stride(2), dp)
     123          35 :             WRITE (unit_nr, '(I5,3f12.6)') (pw%pw_grid%npts(3) + my_stride(3) - 1)/my_stride(3), &
     124          35 :                pw%pw_grid%dh(1, 3)*REAL(my_stride(3), dp), pw%pw_grid%dh(2, 3)*REAL(my_stride(3), dp), &
     125          70 :                pw%pw_grid%dh(3, 3)*REAL(my_stride(3), dp)
     126             : 
     127          35 :             IF (PRESENT(particles_z)) THEN
     128         165 :                DO iat = 1, np
     129         165 :                   WRITE (unit_nr, '(I5,4f12.6)') particles_z(iat), 0._dp, particles_r(:, iat)
     130             :                END DO
     131             :             END IF
     132             :          END IF
     133             : 
     134             :          ! shortcut
     135          70 :          L1 = pw%pw_grid%bounds(1, 1)
     136          70 :          L2 = pw%pw_grid%bounds(1, 2)
     137          70 :          L3 = pw%pw_grid%bounds(1, 3)
     138          70 :          U1 = pw%pw_grid%bounds(2, 1)
     139          70 :          U2 = pw%pw_grid%bounds(2, 2)
     140          70 :          U3 = pw%pw_grid%bounds(2, 3)
     141             : 
     142         210 :          ALLOCATE (buf(L3:U3))
     143             : 
     144          70 :          my_rank = pw%pw_grid%para%my_pos
     145          70 :          gid = pw%pw_grid%para%group
     146          70 :          num_pe = pw%pw_grid%para%group_size
     147          70 :          tag = 1
     148             : 
     149          70 :          rank (1) = unit_nr
     150          70 :          rank (2) = my_rank
     151          70 :          checksum = 0
     152          70 :          IF (unit_nr > 0) checksum = 1
     153             : 
     154          70 :          CALL gid%sum(checksum)
     155          70 :          CPASSERT(checksum == 1)
     156             : 
     157          70 :          CALL gid%maxloc(rank)
     158          70 :          CPASSERT(rank(1) > 0)
     159             : 
     160          70 :          dest = rank(2)
     161        2110 :          DO I1 = L1, U1, my_stride(1)
     162       66146 :             DO I2 = L2, U2, my_stride(2)
     163             : 
     164             :                ! cycling through the CPUs, check if the current ray (I1,I2) is local to that CPU
     165       64036 :                IF (pw%pw_grid%para%mode .NE. PW_MODE_LOCAL) THEN
     166      192108 :                   DO ip = 0, num_pe - 1
     167             :                      IF (pw%pw_grid%para%bo(1, 1, ip, 1) <= I1 - L1 + 1 .AND. pw%pw_grid%para%bo(2, 1, ip, 1) >= I1 - L1 + 1 .AND. &
     168      192108 :                          pw%pw_grid%para%bo(1, 2, ip, 1) <= I2 - L2 + 1 .AND. pw%pw_grid%para%bo(2, 2, ip, 1) >= I2 - L2 + 1) THEN
     169       64036 :                         source = ip
     170             :                      END IF
     171             :                   END DO
     172             :                ELSE
     173           0 :                   source = dest
     174             :                END IF
     175             : 
     176       64036 :                IF (source == dest) THEN
     177       32216 :                   IF (my_rank == source) THEN
     178      717159 :                      buf(:) = pw%array(I1, I2, :)
     179             :                   END IF
     180             :                ELSE
     181       31820 :                   IF (my_rank == source) THEN
     182      703236 :                      buf(:) = pw%array(I1, I2, :)
     183       15910 :                      CALL gid%send(buf, dest, tag)
     184             :                   END IF
     185       31820 :                   IF (my_rank == dest) THEN
     186       15910 :                      CALL gid%recv(buf, source, tag)
     187             :                   END IF
     188             :                END IF
     189             : 
     190       64036 :                IF (unit_nr > 0) THEN
     191       32018 :                   IF (my_zero_tails) THEN
     192           0 :                      DO I3 = L3, U3
     193           0 :                         IF (buf(I3) < 1.E-7_dp) buf(I3) = 0.0_dp
     194             :                      END DO
     195             :                   END IF
     196       32018 :                   WRITE (unit_nr, '(6E13.5)') (buf(I3), I3=L3, U3, my_stride(3))
     197             :                END IF
     198             : 
     199             :                ! this double loop generates so many messages that it can overload
     200             :                ! the message passing system, e.g. on XT3
     201             :                ! we therefore put a barrier here that limits the amount of message
     202             :                ! that flies around at any given time.
     203             :                ! if ever this routine becomes a bottleneck, we should go for a
     204             :                ! more complicated rewrite
     205       66076 :                CALL gid%sync()
     206             : 
     207             :             END DO
     208             :          END DO
     209             : 
     210          70 :          DEALLOCATE (buf)
     211             :       ELSE
     212        1820 :          size_of_z = CEILING(REAL(pw%pw_grid%bounds(2, 3) - pw%pw_grid%bounds(1, 3) + 1, dp)/REAL(my_stride(3), dp))
     213        1820 :          num_linebreak = size_of_z/num_entries_line
     214        1820 :          IF (MODULO(size_of_z, num_entries_line) /= 0) &
     215        1512 :             num_linebreak = num_linebreak + 1
     216        1820 :          msglen = (size_of_z*entry_len + num_linebreak)*mpi_character_size
     217        1820 :          CALL mp_unit%set_handle(unit_nr)
     218        1820 :          CALL pw_to_cube_parallel(pw, mp_unit, title, particles_r, particles_z, my_stride, my_zero_tails, msglen)
     219             :       END IF
     220             : 
     221        1890 :       CALL timestop(handle)
     222             : 
     223        3780 :    END SUBROUTINE pw_to_cube
     224             : 
     225             : ! **************************************************************************************************
     226             : !> \brief  Computes the external density on the grid
     227             : !>         hacked from external_read_density
     228             : !> \param grid     pw to read from cube file
     229             : !> \param filename name of cube file
     230             : !> \param scaling  scale values before storing
     231             : !> \param parallel_read ...
     232             : !> \param silent ...
     233             : !> \par History
     234             : !>      Created [M.Watkins] (01.2014)
     235             : !>      Use blocking, collective MPI read for parallel simulations [Nico Holmberg] (05.2017)
     236             : ! **************************************************************************************************
     237          38 :    SUBROUTINE cube_to_pw(grid, filename, scaling, parallel_read, silent)
     238             : 
     239             :       TYPE(pw_r3d_rs_type), INTENT(IN)                   :: grid
     240             :       CHARACTER(len=*), INTENT(in)                       :: filename
     241             :       REAL(kind=dp), INTENT(in)                          :: scaling
     242             :       LOGICAL, INTENT(in)                                :: parallel_read
     243             :       LOGICAL, INTENT(in), OPTIONAL                      :: silent
     244             : 
     245             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cube_to_pw'
     246             :       INTEGER, PARAMETER                                 :: entry_len = 13, num_entries_line = 6
     247             : 
     248             :       INTEGER                                            :: extunit, handle, i, j, k, msglen, &
     249             :                                                             my_rank, nat, ndum, num_linebreak, &
     250             :                                                             num_pe, output_unit, size_of_z, tag
     251             :       INTEGER, DIMENSION(3)                              :: lbounds, lbounds_local, npoints, &
     252             :                                                             npoints_local, ubounds, ubounds_local
     253             :       LOGICAL                                            :: be_silent
     254          38 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: buffer
     255             :       REAL(kind=dp), DIMENSION(3)                        :: dr, rdum
     256             :       TYPE(mp_comm_type)                                 :: gid
     257             : 
     258          76 :       output_unit = cp_logger_get_default_io_unit()
     259             : 
     260          38 :       CALL timeset(routineN, handle)
     261             : 
     262          38 :       be_silent = .FALSE.
     263          38 :       IF (PRESENT(silent)) THEN
     264           0 :          be_silent = silent
     265             :       END IF
     266             :       !get rs grids and parallel environment
     267          38 :       gid = grid%pw_grid%para%group
     268          38 :       my_rank = grid%pw_grid%para%my_pos
     269          38 :       num_pe = grid%pw_grid%para%group_size
     270          38 :       tag = 1
     271             : 
     272         152 :       lbounds_local = grid%pw_grid%bounds_local(1, :)
     273         152 :       ubounds_local = grid%pw_grid%bounds_local(2, :)
     274          38 :       size_of_z = ubounds_local(3) - lbounds_local(3) + 1
     275             : 
     276          38 :       IF (.NOT. parallel_read) THEN
     277           0 :          npoints = grid%pw_grid%npts
     278           0 :          lbounds = grid%pw_grid%bounds(1, :)
     279           0 :          ubounds = grid%pw_grid%bounds(2, :)
     280             : 
     281           0 :          DO i = 1, 3
     282           0 :             dr(i) = grid%pw_grid%dh(i, i)
     283             :          END DO
     284             : 
     285           0 :          npoints_local = grid%pw_grid%npts_local
     286             :          !pw grids at most pencils - all processors have a full set of z data for x,y
     287           0 :          ALLOCATE (buffer(lbounds(3):ubounds(3)))
     288             : 
     289           0 :          IF (my_rank == 0) THEN
     290           0 :             IF (output_unit > 0 .AND. .NOT. be_silent) THEN
     291           0 :                WRITE (output_unit, FMT="(/,T2,A,/,/,T2,A,/)") "Reading the cube file:     ", TRIM(filename)
     292             :             END IF
     293             : 
     294             :             CALL open_file(file_name=filename, &
     295             :                            file_status="OLD", &
     296             :                            file_form="FORMATTED", &
     297             :                            file_action="READ", &
     298           0 :                            unit_number=extunit)
     299             : 
     300             :             !skip header comments
     301           0 :             DO i = 1, 2
     302           0 :                READ (extunit, *)
     303             :             END DO
     304           0 :             READ (extunit, *) nat, rdum
     305           0 :             DO i = 1, 3
     306           0 :                READ (extunit, *) ndum, rdum
     307           0 :                IF ((ndum /= npoints(i) .OR. (ABS(rdum(i) - dr(i)) > 1e-4)) .AND. &
     308           0 :                    output_unit > 0) THEN
     309           0 :                   WRITE (output_unit, *) "Restart from density | ERROR! | CUBE FILE NOT COINCIDENT WITH INTERNAL GRID ", i
     310           0 :                   WRITE (output_unit, *) "Restart from density | ", ndum, " DIFFERS FROM ", npoints(i)
     311           0 :                   WRITE (output_unit, *) "Restart from density | ", rdum, " DIFFERS FROM ", dr(i)
     312             :                END IF
     313             :             END DO
     314             :             !ignore atomic position data - read from coord or topology instead
     315           0 :             DO i = 1, nat
     316           0 :                READ (extunit, *)
     317             :             END DO
     318             :          END IF
     319             : 
     320             :          !master sends all data to everyone
     321           0 :          DO i = lbounds(1), ubounds(1)
     322           0 :             DO j = lbounds(2), ubounds(2)
     323           0 :                IF (my_rank .EQ. 0) THEN
     324           0 :                   READ (extunit, *) (buffer(k), k=lbounds(3), ubounds(3))
     325             :                END IF
     326           0 :                CALL gid%bcast(buffer(lbounds(3):ubounds(3)), 0)
     327             : 
     328             :                !only use data that is local to me - i.e. in slice of pencil I own
     329             :                IF ((lbounds_local(1) .LE. i) .AND. (i .LE. ubounds_local(1)) .AND. (lbounds_local(2) .LE. j) &
     330           0 :                    .AND. (j .LE. ubounds_local(2))) THEN
     331             :                   !allow scaling of external potential values by factor 'scaling' (SCALING_FACTOR in input file)
     332           0 :                   grid%array(i, j, lbounds(3):ubounds(3)) = buffer(lbounds(3):ubounds(3))*scaling
     333             :                END IF
     334             : 
     335             :             END DO
     336             :          END DO
     337             : 
     338           0 :          IF (my_rank == 0) CALL close_file(unit_number=extunit)
     339             : 
     340           0 :          CALL gid%sync()
     341             :       ELSE
     342             :          ! Parallel routine needs as input the byte size of each grid z-slice
     343             :          ! This is a hack to prevent compilation errors with gcc -Wall (up to versions 6.3)
     344             :          ! related to allocatable-length string declaration CHARACTER(LEN=:), ALLOCATABLE, DIMENSION(:) :: string
     345             :          ! Each data line of a Gaussian cube contains max 6 entries with last line potentially containing less if nz % 6 /= 0
     346             :          ! Thus, this size is simply the number of entries multiplied by the entry size + the number of line breaks
     347          38 :          num_linebreak = size_of_z/num_entries_line
     348          38 :          IF (MODULO(size_of_z, num_entries_line) /= 0) &
     349          36 :             num_linebreak = num_linebreak + 1
     350          38 :          msglen = (size_of_z*entry_len + num_linebreak)*mpi_character_size
     351          38 :          CALL cube_to_pw_parallel(grid, filename, scaling, msglen, silent=silent)
     352             :       END IF
     353             : 
     354          38 :       CALL timestop(handle)
     355             : 
     356          76 :    END SUBROUTINE cube_to_pw
     357             : 
     358             : ! **************************************************************************************************
     359             : !> \brief Reads a realspace potential/density from a cube file using collective MPI I/O and
     360             : !>        stores it in grid.
     361             : !> \param grid     pw to read from cube file
     362             : !> \param filename name of cube file
     363             : !> \param scaling  scale values before storing
     364             : !> \param msglen   the size of each grid slice along z-axis in bytes
     365             : !> \param silent ...
     366             : !> \par History
     367             : !>      Created [Nico Holmberg] (05.2017)
     368             : ! **************************************************************************************************
     369          38 :    SUBROUTINE cube_to_pw_parallel(grid, filename, scaling, msglen, silent)
     370             : 
     371             :       TYPE(pw_r3d_rs_type), INTENT(IN)                   :: grid
     372             :       CHARACTER(len=*), INTENT(in)                       :: filename
     373             :       REAL(kind=dp), INTENT(in)                          :: scaling
     374             :       INTEGER, INTENT(in)                                :: msglen
     375             :       LOGICAL, INTENT(in), OPTIONAL                      :: silent
     376             : 
     377             :       INTEGER, PARAMETER                                 :: entry_len = 13, num_entries_line = 6
     378             : 
     379             :       INTEGER, DIMENSION(3)                              :: lbounds, lbounds_local, npoints, &
     380             :                                                             npoints_local, ubounds, ubounds_local
     381          38 :       INTEGER, ALLOCATABLE, DIMENSION(:), TARGET         :: blocklengths
     382             :       INTEGER(kind=file_offset), ALLOCATABLE, &
     383          38 :          DIMENSION(:), TARGET                            :: displacements
     384             :       INTEGER(kind=file_offset)                          :: BOF
     385             :       INTEGER :: extunit_handle, i, ientry, islice, j, k, my_rank, nat, ndum, nslices, num_pe, &
     386             :          offset_global, output_unit, pos, readstat, size_of_z, tag
     387          38 :       CHARACTER(LEN=msglen), ALLOCATABLE, DIMENSION(:)   :: readbuffer
     388          38 :       CHARACTER(LEN=msglen)                              :: tmp
     389             :       LOGICAL                                            :: be_silent, should_read(2)
     390          38 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: buffer
     391             :       REAL(kind=dp), DIMENSION(3)                        :: dr, rdum
     392             :       TYPE(mp_comm_type)                                 :: gid
     393             :       TYPE(mp_file_descriptor_type)                      :: mp_file_desc
     394             :       TYPE(mp_file_type)                                 :: extunit
     395             : 
     396          76 :       output_unit = cp_logger_get_default_io_unit()
     397             : 
     398          38 :       be_silent = .FALSE.
     399          38 :       IF (PRESENT(silent)) THEN
     400           0 :          be_silent = silent
     401             :       END IF
     402             : 
     403             :       !get rs grids and parallel envnment
     404          38 :       gid = grid%pw_grid%para%group
     405          38 :       my_rank = grid%pw_grid%para%my_pos
     406          38 :       num_pe = grid%pw_grid%para%group_size
     407          38 :       tag = 1
     408             : 
     409         152 :       DO i = 1, 3
     410         152 :          dr(i) = grid%pw_grid%dh(i, i)
     411             :       END DO
     412             : 
     413         152 :       npoints = grid%pw_grid%npts
     414         152 :       lbounds = grid%pw_grid%bounds(1, :)
     415         152 :       ubounds = grid%pw_grid%bounds(2, :)
     416             : 
     417         152 :       npoints_local = grid%pw_grid%npts_local
     418         152 :       lbounds_local = grid%pw_grid%bounds_local(1, :)
     419         152 :       ubounds_local = grid%pw_grid%bounds_local(2, :)
     420          38 :       size_of_z = ubounds_local(3) - lbounds_local(3) + 1
     421          38 :       nslices = (ubounds_local(1) - lbounds_local(1) + 1)*(ubounds_local(2) - lbounds_local(2) + 1)
     422          38 :       islice = 1
     423             : 
     424             :       ! Read header information and determine byte offset of cube data on master process
     425          38 :       IF (my_rank == 0) THEN
     426          19 :          IF (output_unit > 0 .AND. .NOT. be_silent) THEN
     427          19 :             WRITE (output_unit, FMT="(/,T2,A,/,/,T2,A,/)") "Reading the cube file:     ", TRIM(filename)
     428             :          END IF
     429             : 
     430             :          CALL open_file(file_name=filename, &
     431             :                         file_status="OLD", &
     432             :                         file_form="FORMATTED", &
     433             :                         file_action="READ", &
     434             :                         file_access="STREAM", &
     435          19 :                         unit_number=extunit_handle)
     436             : 
     437             :          !skip header comments
     438          57 :          DO i = 1, 2
     439          57 :             READ (extunit_handle, *)
     440             :          END DO
     441          19 :          READ (extunit_handle, *) nat, rdum
     442          76 :          DO i = 1, 3
     443          57 :             READ (extunit_handle, *) ndum, rdum
     444          57 :             IF ((ndum /= npoints(i) .OR. (ABS(rdum(i) - dr(i)) > 1e-4)) .AND. &
     445          19 :                 output_unit > 0) THEN
     446           0 :                WRITE (output_unit, *) "Restart from density | ERROR! | CUBE FILE NOT COINCIDENT WITH INTERNAL GRID ", i
     447           0 :                WRITE (output_unit, *) "Restart from density | ", ndum, " DIFFERS FROM ", npoints(i)
     448           0 :                WRITE (output_unit, *) "Restart from density | ", rdum, " DIFFERS FROM ", dr(i)
     449             :             END IF
     450             :          END DO
     451             :          !ignore atomic position data - read from coord or topology instead
     452          44 :          DO i = 1, nat
     453          44 :             READ (extunit_handle, *)
     454             :          END DO
     455             :          ! Get byte offset
     456          19 :          INQUIRE (extunit_handle, POS=offset_global)
     457          19 :          CALL close_file(unit_number=extunit_handle)
     458             :       END IF
     459             :       ! Sync offset and start parallel read
     460          38 :       CALL gid%bcast(offset_global, grid%pw_grid%para%group_head_id)
     461          38 :       BOF = offset_global
     462          38 :       CALL extunit%open(groupid=gid, filepath=filename, amode_status=file_amode_rdonly)
     463             :       ! Determine byte offsets for each grid z-slice which are local to a process
     464         114 :       ALLOCATE (displacements(nslices))
     465       29450 :       displacements = 0
     466        1151 :       DO i = lbounds(1), ubounds(1)
     467        3396 :          should_read(:) = .TRUE.
     468        1132 :          IF (i .LT. lbounds_local(1)) THEN
     469         371 :             should_read(1) = .FALSE.
     470         761 :          ELSE IF (i .GT. ubounds_local(1)) THEN
     471             :             EXIT
     472             :          END IF
     473       45269 :          DO j = lbounds(2), ubounds(2)
     474       44118 :             should_read(2) = .TRUE.
     475       44118 :             IF (j .LT. lbounds_local(2) .OR. j .GT. ubounds_local(2)) THEN
     476           0 :                should_read(2) = .FALSE.
     477             :             END IF
     478      102942 :             IF (ALL(should_read .EQV. .TRUE.)) THEN
     479       29412 :                IF (islice > nslices) CPABORT("Index out of bounds.")
     480       29412 :                displacements(islice) = BOF
     481       29412 :                islice = islice + 1
     482             :             END IF
     483             :             ! Update global byte offset
     484       45231 :             BOF = BOF + msglen
     485             :          END DO
     486             :       END DO
     487             :       ! Size of each z-slice is msglen
     488         114 :       ALLOCATE (blocklengths(nslices))
     489       29450 :       blocklengths(:) = msglen
     490             :       ! Create indexed MPI type using calculated byte offsets as displacements and use it as a file view
     491          38 :       mp_file_desc = mp_file_type_hindexed_make_chv(nslices, blocklengths, displacements)
     492          38 :       BOF = 0
     493          38 :       CALL mp_file_type_set_view_chv(extunit, BOF, mp_file_desc)
     494             :       ! Collective read of cube
     495         114 :       ALLOCATE (readbuffer(nslices))
     496       29450 :       readbuffer(:) = ''
     497          38 :       CALL extunit%read_all(msglen, nslices, readbuffer, mp_file_desc)
     498          38 :       CALL mp_file_type_free(mp_file_desc)
     499          38 :       CALL extunit%close()
     500             :       ! Convert cube values string -> real
     501          38 :       i = lbounds_local(1)
     502          38 :       j = lbounds_local(2)
     503         114 :       ALLOCATE (buffer(lbounds(3):ubounds(3)))
     504        1522 :       buffer = 0.0_dp
     505             :       ! Test if the compiler supports parsing lines with line breaks in them
     506          38 :       IF (parse_test) THEN
     507          16 :          READ (readbuffer(1), *, IOSTAT=readstat) (buffer(k), k=lbounds(3), ubounds(3))
     508          16 :          IF (readstat == 0) THEN
     509          16 :             parses_linebreaks = .TRUE.
     510             :          ELSE
     511           0 :             parses_linebreaks = .FALSE.
     512             :          END IF
     513          16 :          parse_test = .FALSE.
     514         652 :          buffer = 0.0_dp
     515             :       END IF
     516       29450 :       DO islice = 1, nslices
     517       29412 :          IF (parses_linebreaks) THEN
     518             :             ! Use list-directed conversion if supported
     519             :             ! Produces faster code, but maybe the latter method could be optimized
     520       29412 :             READ (readbuffer(islice), *) (buffer(k), k=lbounds(3), ubounds(3))
     521             :          ELSE
     522             :             ! Convert values by going through the z-slice one value at a time
     523           0 :             tmp = readbuffer(islice)
     524             :             pos = 1
     525             :             ientry = 1
     526           0 :             DO k = lbounds_local(3), ubounds_local(3)
     527           0 :                IF (MODULO(ientry, num_entries_line) == 0 .OR. k == ubounds_local(3)) THEN
     528             :                   ! Last value on line, dont read line break
     529           0 :                   READ (tmp(pos:pos + (entry_len - 2)), '(E12.5)') buffer(k)
     530           0 :                   pos = pos + (entry_len + 1)
     531             :                ELSE
     532           0 :                   READ (tmp(pos:pos + (entry_len - 1)), '(E13.5)') buffer(k)
     533           0 :                   pos = pos + entry_len
     534             :                END IF
     535           0 :                ientry = ientry + 1
     536             :             END DO
     537             :          END IF
     538             :          ! Optionally scale cube file values
     539     1213948 :          grid%array(i, j, lbounds(3):ubounds(3)) = scaling*buffer(lbounds(3):ubounds(3))
     540       29412 :          j = j + 1
     541       29450 :          IF (j > ubounds_local(2)) THEN
     542         742 :             j = lbounds_local(2)
     543         742 :             i = i + 1
     544             :          END IF
     545             :       END DO
     546          38 :       DEALLOCATE (readbuffer)
     547          38 :       DEALLOCATE (blocklengths, displacements)
     548             :       IF (debug_this_module) THEN
     549             :          ! Check that cube was correctly read using intrinsic read on master who sends data to everyone
     550             :          buffer = 0.0_dp
     551             :          IF (my_rank == 0) THEN
     552             :             IF (output_unit > 0 .AND. .NOT. be_silent) THEN
     553             :                WRITE (output_unit, FMT="(/,T2,A,/,/,T2,A)") "Reading the cube file:     ", filename
     554             :             END IF
     555             : 
     556             :             CALL open_file(file_name=filename, &
     557             :                            file_status="OLD", &
     558             :                            file_form="FORMATTED", &
     559             :                            file_action="READ", &
     560             :                            unit_number=extunit_handle)
     561             : 
     562             :             !skip header comments
     563             :             DO i = 1, 2
     564             :                READ (extunit_handle, *)
     565             :             END DO
     566             :             READ (extunit_handle, *) nat, rdum
     567             :             DO i = 1, 3
     568             :                READ (extunit_handle, *) ndum, rdum
     569             :                IF ((ndum /= npoints(i) .OR. (ABS(rdum(i) - dr(i)) > 1e-4)) .AND. &
     570             :                    output_unit > 0) THEN
     571             :                   WRITE (output_unit, *) "Restart from density | ERROR! | CUBE FILE NOT COINCIDENT WITH INTERNAL GRID ", i
     572             :                   WRITE (output_unit, *) "Restart from density | ", ndum, " DIFFERS FROM ", npoints(i)
     573             :                   WRITE (output_unit, *) "Restart from density | ", rdum, " DIFFERS FROM ", dr(i)
     574             :                END IF
     575             :             END DO
     576             :             !ignore atomic position data - read from coord or topology instead
     577             :             DO i = 1, nat
     578             :                READ (extunit_handle, *)
     579             :             END DO
     580             :          END IF
     581             : 
     582             :          !master sends all data to everyone
     583             :          DO i = lbounds(1), ubounds(1)
     584             :             DO j = lbounds(2), ubounds(2)
     585             :                IF (my_rank .EQ. 0) THEN
     586             :                   READ (extunit_handle, *) (buffer(k), k=lbounds(3), ubounds(3))
     587             :                END IF
     588             :                CALL gid%bcast(buffer(lbounds(3):ubounds(3)), 0)
     589             : 
     590             :                !only use data that is local to me - i.e. in slice of pencil I own
     591             :                IF ((lbounds_local(1) .LE. i) .AND. (i .LE. ubounds_local(1)) .AND. (lbounds_local(2) .LE. j) &
     592             :                    .AND. (j .LE. ubounds_local(2))) THEN
     593             :                   !allow scaling of external potential values by factor 'scaling' (SCALING_FACTOR in input file)
     594             :                   IF (ANY(grid%array(i, j, lbounds(3):ubounds(3)) /= buffer(lbounds(3):ubounds(3))*scaling)) &
     595             :                      CALL cp_abort(__LOCATION__, &
     596             :                                    "Error in parallel read of input cube file.")
     597             :                END IF
     598             : 
     599             :             END DO
     600             :          END DO
     601             : 
     602             :          IF (my_rank == 0) CALL close_file(unit_number=extunit_handle)
     603             : 
     604             :          CALL gid%sync()
     605             :       END IF
     606          38 :       DEALLOCATE (buffer)
     607             : 
     608          38 :    END SUBROUTINE cube_to_pw_parallel
     609             : 
     610             : ! **************************************************************************************************
     611             : !> \brief Writes a realspace potential to a cube file using collective MPI I/O.
     612             : !> \param grid        the pw to output to the cube file
     613             : !> \param unit_nr     the handle associated with the cube file
     614             : !> \param title       title of the cube file
     615             : !> \param particles_r Cartersian coordinates of the system
     616             : !> \param particles_z atomic masses of atoms in the system
     617             : !> \param stride      every stride(i)th value of the potential is outputted (i=x,y,z)
     618             : !> \param zero_tails  flag that determines if small values of the potential should be zeroed
     619             : !> \param msglen      the size of each grid slice along z-axis in bytes
     620             : !> \par History
     621             : !>      Created [Nico Holmberg] (11.2017)
     622             : ! **************************************************************************************************
     623        1820 :    SUBROUTINE pw_to_cube_parallel(grid, unit_nr, title, particles_r, particles_z, stride, zero_tails, &
     624             :                                   msglen)
     625             : 
     626             :       TYPE(pw_r3d_rs_type), INTENT(IN)                   :: grid
     627             :       TYPE(mp_file_type), INTENT(IN)                     :: unit_nr
     628             :       CHARACTER(*), INTENT(IN), OPTIONAL                 :: title
     629             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
     630             :          OPTIONAL                                        :: particles_r
     631             :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: particles_z
     632             :       INTEGER, INTENT(IN)                                :: stride(3)
     633             :       LOGICAL, INTENT(IN)                                :: zero_tails
     634             :       INTEGER, INTENT(IN)                                :: msglen
     635             : 
     636             :       INTEGER, PARAMETER                                 :: entry_len = 13, header_len = 41, &
     637             :                                                             header_len_z = 53, num_entries_line = 6
     638             : 
     639             :       CHARACTER(LEN=entry_len)                           :: value
     640             :       CHARACTER(LEN=header_len)                          :: header
     641             :       CHARACTER(LEN=header_len_z)                        :: header_z
     642             :       INTEGER, DIMENSION(3)                              :: lbounds, lbounds_local, ubounds, &
     643             :                                                             ubounds_local
     644        1820 :       INTEGER, ALLOCATABLE, DIMENSION(:), TARGET         :: blocklengths
     645             :       INTEGER(kind=file_offset), ALLOCATABLE, &
     646        1820 :          DIMENSION(:), TARGET                            :: displacements
     647             :       INTEGER(kind=file_offset)                          :: BOF
     648             :       INTEGER                                            :: counter, i, islice, j, k, last_z, &
     649             :                                                             my_rank, np, nslices, size_of_z
     650        1820 :       CHARACTER(LEN=msglen), ALLOCATABLE, DIMENSION(:)   :: writebuffer
     651        1820 :       CHARACTER(LEN=msglen)                              :: tmp
     652             :       LOGICAL                                            :: should_write(2)
     653             :       TYPE(mp_comm_type)                                 :: gid
     654             :       TYPE(mp_file_descriptor_type)                      :: mp_desc
     655             : 
     656             :       !get rs grids and parallel envnment
     657        1820 :       gid = grid%pw_grid%para%group
     658        1820 :       my_rank = grid%pw_grid%para%my_pos
     659             : 
     660             :       ! Shortcut
     661        7280 :       lbounds = grid%pw_grid%bounds(1, :)
     662        7280 :       ubounds = grid%pw_grid%bounds(2, :)
     663        7280 :       lbounds_local = grid%pw_grid%bounds_local(1, :)
     664        7280 :       ubounds_local = grid%pw_grid%bounds_local(2, :)
     665             :       ! Determine the total number of z-slices and the number of values per slice
     666        1820 :       size_of_z = CEILING(REAL(ubounds_local(3) - lbounds_local(3) + 1, dp)/REAL(stride(3), dp))
     667        1820 :       islice = 1
     668       28092 :       DO i = lbounds(1), ubounds(1), stride(1)
     669       81546 :          should_write(:) = .TRUE.
     670       27182 :          IF (i .LT. lbounds_local(1)) THEN
     671        8868 :             should_write(1) = .FALSE.
     672       18314 :          ELSE IF (i .GT. ubounds_local(1)) THEN
     673             :             EXIT
     674             :          END IF
     675      592641 :          DO j = lbounds(2), ubounds(2), stride(2)
     676      564549 :             should_write(2) = .TRUE.
     677      564549 :             IF (j .LT. lbounds_local(2) .OR. j .GT. ubounds_local(2)) THEN
     678           0 :                should_write(2) = .FALSE.
     679             :             END IF
     680     1339665 :             IF (ALL(should_write .EQV. .TRUE.)) THEN
     681      374422 :                islice = islice + 1
     682             :             END IF
     683             :          END DO
     684             :       END DO
     685        1820 :       nslices = islice - 1
     686       37274 :       DO k = lbounds(3), ubounds(3), stride(3)
     687       37274 :          IF (k + stride(3) > ubounds(3)) last_z = k
     688             :       END DO
     689        1820 :       islice = 1
     690             :       ! Determine initial byte offset (0 or EOF if data is appended)
     691        1820 :       CALL unit_nr%get_position(BOF)
     692             :       ! Write header information on master process and update byte offset accordingly
     693        1820 :       IF (my_rank == 0) THEN
     694             :          ! this format seems to work for e.g. molekel and gOpenmol
     695             :          ! latest version of VMD can read non orthorhombic cells
     696         910 :          CALL unit_nr%write_at(BOF, "-Quickstep-"//NEW_LINE("C"))
     697         910 :          BOF = BOF + LEN("-Quickstep-"//NEW_LINE("C"))*mpi_character_size
     698         910 :          IF (PRESENT(title)) THEN
     699         910 :             CALL unit_nr%write_at(BOF, TRIM(title)//NEW_LINE("C"))
     700         910 :             BOF = BOF + LEN(TRIM(title)//NEW_LINE("C"))*mpi_character_size
     701             :          ELSE
     702           0 :             CALL unit_nr%write_at(BOF, "No Title"//NEW_LINE("C"))
     703           0 :             BOF = BOF + LEN("No Title"//NEW_LINE("C"))*mpi_character_size
     704             :          END IF
     705             : 
     706         910 :          CPASSERT(PRESENT(particles_z) .EQV. PRESENT(particles_r))
     707         910 :          np = 0
     708         910 :          IF (PRESENT(particles_z)) THEN
     709         910 :             CPASSERT(SIZE(particles_z) == SIZE(particles_r, dim=2))
     710             :             ! cube files can only be written for 99999 particles due to a format limitation (I5)
     711             :             ! so we limit the number of particles written.
     712         910 :             np = MIN(99999, SIZE(particles_z))
     713             :          END IF
     714             : 
     715         910 :          WRITE (header, '(I5,3f12.6)') np, 0.0_dp, 0._dp, 0._dp !start of cube
     716         910 :          CALL unit_nr%write_at(BOF, header//NEW_LINE("C"))
     717         910 :          BOF = BOF + LEN(header//NEW_LINE("C"))*mpi_character_size
     718             : 
     719         910 :          WRITE (header, '(I5,3f12.6)') (grid%pw_grid%npts(1) + stride(1) - 1)/stride(1), &
     720         910 :             grid%pw_grid%dh(1, 1)*REAL(stride(1), dp), grid%pw_grid%dh(2, 1)*REAL(stride(1), dp), &
     721        1820 :             grid%pw_grid%dh(3, 1)*REAL(stride(1), dp)
     722         910 :          CALL unit_nr%write_at(BOF, header//NEW_LINE("C"))
     723         910 :          BOF = BOF + LEN(header//NEW_LINE("C"))*mpi_character_size
     724             : 
     725         910 :          WRITE (header, '(I5,3f12.6)') (grid%pw_grid%npts(2) + stride(2) - 1)/stride(2), &
     726         910 :             grid%pw_grid%dh(1, 2)*REAL(stride(2), dp), grid%pw_grid%dh(2, 2)*REAL(stride(2), dp), &
     727        1820 :             grid%pw_grid%dh(3, 2)*REAL(stride(2), dp)
     728         910 :          CALL unit_nr%write_at(BOF, header//NEW_LINE("C"))
     729         910 :          BOF = BOF + LEN(header//NEW_LINE("C"))*mpi_character_size
     730             : 
     731         910 :          WRITE (header, '(I5,3f12.6)') (grid%pw_grid%npts(3) + stride(3) - 1)/stride(3), &
     732         910 :             grid%pw_grid%dh(1, 3)*REAL(stride(3), dp), grid%pw_grid%dh(2, 3)*REAL(stride(3), dp), &
     733        1820 :             grid%pw_grid%dh(3, 3)*REAL(stride(3), dp)
     734         910 :          CALL unit_nr%write_at(BOF, header//NEW_LINE("C"))
     735         910 :          BOF = BOF + LEN(header//NEW_LINE("C"))*mpi_character_size
     736             : 
     737         910 :          IF (PRESENT(particles_z)) THEN
     738        3963 :             DO i = 1, np
     739        3053 :                WRITE (header_z, '(I5,4f12.6)') particles_z(i), 0._dp, particles_r(:, i)
     740        3053 :                CALL unit_nr%write_at(BOF, header_z//NEW_LINE("C"))
     741        3963 :                BOF = BOF + LEN(header_z//NEW_LINE("C"))*mpi_character_size
     742             :             END DO
     743             :          END IF
     744             :       END IF
     745             :       ! Sync offset
     746        1820 :       CALL gid%bcast(BOF, grid%pw_grid%para%group_head_id)
     747             :       ! Determine byte offsets for each grid z-slice which are local to a process
     748             :       ! and convert z-slices to cube format compatible strings
     749        5460 :       ALLOCATE (displacements(nslices))
     750      376242 :       displacements = 0
     751        5460 :       ALLOCATE (writebuffer(nslices))
     752      376242 :       writebuffer(:) = ''
     753       28092 :       DO i = lbounds(1), ubounds(1), stride(1)
     754       81546 :          should_write(:) = .TRUE.
     755       27182 :          IF (i .LT. lbounds_local(1)) THEN
     756        8868 :             should_write(1) = .FALSE.
     757       18314 :          ELSE IF (i .GT. ubounds_local(1)) THEN
     758             :             EXIT
     759             :          END IF
     760       28092 :          DO j = lbounds(2), ubounds(2), stride(2)
     761      564549 :             should_write(2) = .TRUE.
     762      564549 :             IF (j .LT. lbounds_local(2) .OR. j .GT. ubounds_local(2)) THEN
     763           0 :                should_write(2) = .FALSE.
     764             :             END IF
     765     1313393 :             IF (ALL(should_write .EQV. .TRUE.)) THEN
     766      374422 :                IF (islice > nslices) CPABORT("Index out of bounds.")
     767      374422 :                displacements(islice) = BOF
     768      374422 :                tmp = ''
     769      374422 :                counter = 0
     770     9830257 :                DO k = lbounds(3), ubounds(3), stride(3)
     771     9455835 :                   IF (zero_tails .AND. grid%array(i, j, k) < 1.E-7_dp) THEN
     772       54882 :                      WRITE (value, '(E13.5)') 0.0_dp
     773             :                   ELSE
     774     9400953 :                      WRITE (value, '(E13.5)') grid%array(i, j, k)
     775             :                   END IF
     776     9455835 :                   tmp = TRIM(tmp)//TRIM(value)
     777     9455835 :                   counter = counter + 1
     778     9455835 :                   IF (MODULO(counter, num_entries_line) == 0 .OR. k == last_z) &
     779     2090403 :                      tmp = TRIM(tmp)//NEW_LINE('C')
     780             :                END DO
     781      374422 :                writebuffer(islice) = tmp
     782      374422 :                islice = islice + 1
     783             :             END IF
     784             :             ! Update global byte offset
     785      564549 :             BOF = BOF + msglen
     786             :          END DO
     787             :       END DO
     788             :       ! Create indexed MPI type using calculated byte offsets as displacements
     789             :       ! Size of each z-slice is msglen
     790        5460 :       ALLOCATE (blocklengths(nslices))
     791      376242 :       blocklengths(:) = msglen
     792        1820 :       mp_desc = mp_file_type_hindexed_make_chv(nslices, blocklengths, displacements)
     793             :       ! Use the created type as a file view
     794             :       ! NB. The vector 'displacements' contains the absolute offsets of each z-slice i.e.
     795             :       ! they are given relative to the beginning of the file. The global offset to
     796             :       ! set_view must therefore be set to 0
     797        1820 :       BOF = 0
     798        1820 :       CALL mp_file_type_set_view_chv(unit_nr, BOF, mp_desc)
     799             :       ! Collective write of cube
     800        1820 :       CALL unit_nr%write_all(msglen, nslices, writebuffer, mp_desc)
     801             :       ! Clean up
     802        1820 :       CALL mp_file_type_free(mp_desc)
     803        1820 :       DEALLOCATE (writebuffer)
     804        1820 :       DEALLOCATE (blocklengths, displacements)
     805             : 
     806        1820 :    END SUBROUTINE pw_to_cube_parallel
     807             : 
     808             : ! **************************************************************************************************
     809             : !> \brief Prints a simple grid file: X Y Z value
     810             : !> \param pw ...
     811             : !> \param unit_nr ...
     812             : !> \param stride ...
     813             : !> \param pw2 ...
     814             : !> \par History
     815             : !>      Created [Vladimir Rybkin] (08.2018)
     816             : !> \author Vladimir Rybkin
     817             : ! **************************************************************************************************
     818          16 :    SUBROUTINE pw_to_simple_volumetric(pw, unit_nr, stride, pw2)
     819             :       TYPE(pw_r3d_rs_type), INTENT(IN)                   :: pw
     820             :       INTEGER, INTENT(IN)                                :: unit_nr
     821             :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: stride
     822             :       TYPE(pw_r3d_rs_type), INTENT(IN), OPTIONAL         :: pw2
     823             : 
     824             :       CHARACTER(len=*), PARAMETER :: routineN = 'pw_to_simple_volumetric'
     825             : 
     826             :       INTEGER                                            :: checksum, dest, handle, i, I1, I2, I3, &
     827             :                                                             ip, L1, L2, L3, my_rank, my_stride(3), &
     828             :                                                             ngrids, npoints, num_pe, rank(2), &
     829             :                                                             source, tag, U1, U2, U3
     830             :       LOGICAL                                            :: DOUBLE
     831             :       REAL(KIND=dp)                                      :: x, y, z
     832          16 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: buf, buf2
     833             :       TYPE(mp_comm_type)                                 :: gid
     834             : 
     835          16 :       CALL timeset(routineN, handle)
     836             : 
     837             :       ! Check if we write two grids
     838          16 :       DOUBLE = .FALSE.
     839          16 :       IF (PRESENT(pw2)) DOUBLE = .TRUE.
     840             : 
     841          64 :       my_stride = 1
     842          16 :       IF (PRESENT(stride)) THEN
     843          16 :          IF (SIZE(stride) /= 1 .AND. SIZE(stride) /= 3) &
     844             :             CALL cp_abort(__LOCATION__, "STRIDE keyword can accept only 1 "// &
     845          16 :                           "(the same for X,Y,Z) or 3 values. Correct your input file.")
     846          16 :          IF (SIZE(stride) == 1) THEN
     847           0 :             DO i = 1, 3
     848           0 :                my_stride(i) = stride(1)
     849             :             END DO
     850             :          ELSE
     851          64 :             my_stride = stride(1:3)
     852             :          END IF
     853          16 :          CPASSERT(my_stride(1) > 0)
     854          16 :          CPASSERT(my_stride(2) > 0)
     855          16 :          CPASSERT(my_stride(3) > 0)
     856             :       END IF
     857             : 
     858             :       ! shortcut
     859          16 :       L1 = pw%pw_grid%bounds(1, 1)
     860          16 :       L2 = pw%pw_grid%bounds(1, 2)
     861          16 :       L3 = pw%pw_grid%bounds(1, 3)
     862          16 :       U1 = pw%pw_grid%bounds(2, 1)
     863          16 :       U2 = pw%pw_grid%bounds(2, 2)
     864          16 :       U3 = pw%pw_grid%bounds(2, 3)
     865             : 
     866             :       ! Write the header: number of points and number of spins
     867          16 :       ngrids = 1
     868          16 :       IF (DOUBLE) ngrids = 2
     869             :       npoints = ((pw%pw_grid%npts(1) + my_stride(1) - 1)/my_stride(1))* &
     870             :                 ((pw%pw_grid%npts(2) + my_stride(2) - 1)/my_stride(1))* &
     871          16 :                 ((pw%pw_grid%npts(3) + my_stride(3) - 1)/my_stride(1))
     872          16 :       IF (unit_nr > 1) WRITE (unit_nr, '(I7,I5)') npoints, ngrids
     873             : 
     874          48 :       ALLOCATE (buf(L3:U3))
     875          16 :       IF (DOUBLE) ALLOCATE (buf2(L3:U3))
     876             : 
     877          16 :       my_rank = pw%pw_grid%para%my_pos
     878          16 :       gid = pw%pw_grid%para%group
     879          16 :       num_pe = pw%pw_grid%para%group_size
     880          16 :       tag = 1
     881             : 
     882          16 :       rank (1) = unit_nr
     883          16 :       rank (2) = my_rank
     884          16 :       checksum = 0
     885          16 :       IF (unit_nr > 0) checksum = 1
     886             : 
     887          16 :       CALL gid%sum(checksum)
     888          16 :       CPASSERT(checksum == 1)
     889             : 
     890          16 :       CALL gid%maxloc(rank)
     891          16 :       CPASSERT(rank(1) > 0)
     892             : 
     893          16 :       dest = rank(2)
     894         500 :       DO I1 = L1, U1, my_stride(1)
     895       15288 :          DO I2 = L2, U2, my_stride(2)
     896             : 
     897             :             ! cycling through the CPUs, check if the current ray (I1,I2) is local to that CPU
     898       14788 :             IF (pw%pw_grid%para%mode .NE. PW_MODE_LOCAL) THEN
     899       44364 :                DO ip = 0, num_pe - 1
     900             :                   IF (pw%pw_grid%para%bo(1, 1, ip, 1) <= I1 - L1 + 1 .AND. pw%pw_grid%para%bo(2, 1, ip, 1) >= I1 - L1 + 1 .AND. &
     901       44364 :                       pw%pw_grid%para%bo(1, 2, ip, 1) <= I2 - L2 + 1 .AND. pw%pw_grid%para%bo(2, 2, ip, 1) >= I2 - L2 + 1) THEN
     902       14788 :                      source = ip
     903             :                   END IF
     904             :                END DO
     905             :             ELSE
     906           0 :                source = dest
     907             :             END IF
     908             : 
     909       14788 :             IF (source == dest) THEN
     910        7444 :                IF (my_rank == source) THEN
     911      118276 :                   buf(:) = pw%array(I1, I2, :)
     912        3722 :                   IF (DOUBLE) buf2(:) = pw2%array(I1, I2, :)
     913             :                END IF
     914             :             ELSE
     915        7344 :                IF (my_rank == source) THEN
     916      116976 :                   buf(:) = pw%array(I1, I2, :)
     917        3672 :                   CALL gid%send(buf, dest, tag)
     918        3672 :                   IF (DOUBLE) THEN
     919           0 :                      buf2(:) = pw2%array(I1, I2, :)
     920           0 :                      CALL gid%send(buf2, dest, tag)
     921             :                   END IF
     922             :                END IF
     923        7344 :                IF (my_rank == dest) THEN
     924        3672 :                   CALL gid%recv(buf, source, tag)
     925        3672 :                   IF (DOUBLE) CALL gid%recv(buf2, source, tag)
     926             :                END IF
     927             :             END IF
     928             : 
     929       14788 :             IF (.NOT. DOUBLE) THEN
     930      470504 :                DO I3 = L3, U3, my_stride(3)
     931             :                   x = pw%pw_grid%dh(1, 1)*I1 + &
     932             :                       pw%pw_grid%dh(2, 1)*I2 + &
     933      455716 :                       pw%pw_grid%dh(3, 1)*I3
     934             : 
     935             :                   y = pw%pw_grid%dh(1, 2)*I1 + &
     936             :                       pw%pw_grid%dh(2, 2)*I2 + &
     937      455716 :                       pw%pw_grid%dh(3, 2)*I3
     938             : 
     939             :                   z = pw%pw_grid%dh(1, 3)*I1 + &
     940             :                       pw%pw_grid%dh(2, 3)*I2 + &
     941      455716 :                       pw%pw_grid%dh(3, 3)*I3
     942             : 
     943      470504 :                   IF (unit_nr > 0) THEN
     944      227858 :                      WRITE (unit_nr, '(6E13.5, 6E13.5, 6E13.5, 6E13.5)') x, y, z, buf(I3)
     945             :                   END IF
     946             :                END DO
     947             : 
     948             :             ELSE
     949             : 
     950           0 :                DO I3 = L3, U3, my_stride(3)
     951             :                   x = pw%pw_grid%dh(1, 1)*I1 + &
     952             :                       pw%pw_grid%dh(2, 1)*I2 + &
     953           0 :                       pw%pw_grid%dh(3, 1)*I3
     954             : 
     955             :                   y = pw%pw_grid%dh(1, 2)*I1 + &
     956             :                       pw%pw_grid%dh(2, 2)*I2 + &
     957           0 :                       pw%pw_grid%dh(3, 2)*I3
     958             : 
     959             :                   z = pw%pw_grid%dh(1, 3)*I1 + &
     960             :                       pw%pw_grid%dh(2, 3)*I2 + &
     961           0 :                       pw%pw_grid%dh(3, 3)*I3
     962             : 
     963           0 :                   IF (unit_nr > 0) THEN
     964           0 :                      WRITE (unit_nr, '(6E13.5, 6E13.5, 6E13.5, 6E13.5)') x, y, z, buf(I3), buf2(I3)
     965             :                   END IF
     966             :                END DO
     967             : 
     968             :             END IF ! Double
     969             : 
     970             :             ! this double loop generates so many messages that it can overload
     971             :             ! the message passing system, e.g. on XT3
     972             :             ! we therefore put a barrier here that limits the amount of message
     973             :             ! that flies around at any given time.
     974             :             ! if ever this routine becomes a bottleneck, we should go for a
     975             :             ! more complicated rewrite
     976       15272 :             CALL gid%sync()
     977             : 
     978             :          END DO
     979             :       END DO
     980             : 
     981          16 :       DEALLOCATE (buf)
     982          16 :       IF (DOUBLE) DEALLOCATE (buf2)
     983             : 
     984          16 :       CALL timestop(handle)
     985             : 
     986          32 :    END SUBROUTINE pw_to_simple_volumetric
     987             : 
     988             : END MODULE realspace_grid_cube

Generated by: LCOV version 1.15