LCOV - code coverage report
Current view: top level - src/pw - realspace_grid_cube.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 83.7 % 453 379
Test Date: 2026-09-03 07:32:15 Functions: 100.0 % 6 6

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

Generated by: LCOV version 2.0-1