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