(git:98357aa)
Loading...
Searching...
No Matches
smeagol_matrix_utils.F
Go to the documentation of this file.
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 Routines to convert sparse matrices between DBCSR (distributed-blocks compressed sparse rows)
10!> and SIESTA (distributed compressed sparse columns) formats.
11!> \author Sergey Chulkov
12!> \author Christian Ahart
13!> \author Clotilde Cucinotta
14! **************************************************************************************************
16 USE cell_types, ONLY: cell_type, &
23 USE kinds, ONLY: dp, &
24 dp_size, &
25 int_8
30#if defined(__SMEAGOL)
31 USE parallel, ONLY: getnodeorbs, &
32 globaltolocalorb, &
33 localtoglobalorb, &
34 whichnodeorb
35#endif
44 USE qs_subsys_types, ONLY: qs_subsys_get, &
46 USE util, ONLY: sort
47#include "./base/base_uses.f90"
48
49 IMPLICIT NONE
50 PRIVATE
51
52 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'smeagol_matrix_utils'
53 LOGICAL, PARAMETER, PRIVATE :: debug_this_module = .false.
54
55 INTEGER, PARAMETER, PRIVATE :: neighbor_list_iatom_index = 1
56 INTEGER, PARAMETER, PRIVATE :: neighbor_list_jatom_index = 2
57 INTEGER, PARAMETER, PRIVATE :: neighbor_list_dbcsr_image_index = 3
58 INTEGER, PARAMETER, PRIVATE :: neighbor_list_siesta_image_index = 4
59 INTEGER, PARAMETER, PRIVATE :: neighbor_list_siesta_transp_image_index = 5
60 INTEGER, PARAMETER, PRIVATE :: neighbor_list_dim1 = neighbor_list_siesta_transp_image_index
61
65
66 PRIVATE :: get_negf_cell_ijk, index_in_canonical_enumeration, number_from_canonical_enumeration, pbc_0_1
67 PRIVATE :: get_number_of_mpi_sendrecv_requests, assign_nonzero_elements_to_requests
68
69 !> number of DBCSR matrix elements to receive from a given rank
70 INTEGER, PARAMETER, PRIVATE :: nelements_dbcsr_recv = 1
71 !> number of DBCSR matrix elements to send to a given rank
72 INTEGER, PARAMETER, PRIVATE :: nelements_dbcsr_send = 2
73 INTEGER, PARAMETER, PRIVATE :: nelements_dbcsr_dim2 = nelements_dbcsr_send
74
75 ! 128 MiB (to limit memory usage for matrix redistribution)
76 INTEGER(kind=int_8), PARAMETER, PRIVATE :: max_mpi_packet_size_bytes = 134217728
77 INTEGER(kind=int_8), PARAMETER, PRIVATE :: max_mpi_packet_size_dp = max_mpi_packet_size_bytes/int(dp_size, kind=int_8)
78
79 ! a portable way to determine the upper bound for tag value is to call
80 ! MPI_COMM_GET_ATTR(comm, MPI_TAG_UB, max_mpi_rank, flag, ierror).
81 ! The MPI specification guarantees a value of 32767
82 INTEGER, PARAMETER, PRIVATE :: max_mpi_rank = 32767
83
84! **************************************************************************************************
85!> \brief Sparsity pattern of replicated SIESTA compressed sparse column (CSC) matrices
86! **************************************************************************************************
88 !> gather all non-zero matrix elements on the given MPI process.
89 !> Distribute the elements across MPI processes if gather_root < 0.
90 !> The matrix elements should be located on I/O process in case of bulk transport,
91 !> and should be distributed in case of SMEAGOL calculation.
92 INTEGER :: gather_root = 0
93 !> Based of full (.FALSE.) or upper-triangular (.TRUE.) DBCSR matrix.
94 !> It is used in CPASSERTs to allow access to lower-triangular matrix elements of non-symmetric DBCSR matrices
95 !> In case there is no bugs in this module, these CPASSERTs should newer trigger.
96 !> Therefore the 'symmetric' variable alongside with relevant CPASSERT calls are excessive and can be removed.
97 LOGICAL :: symmetric = .true.
98 !> number of neighbour list nodes for each MPI process (0:num_pe-1).
99 !> If do_merge == .TRUE., nodes for different cell images along k cell vector are merged into one node
100 INTEGER, ALLOCATABLE, DIMENSION(:) :: nnodes_per_proc
101
102 !> replicated neighbour list (1:neighbor_list_dim1, 1:SUM(nnodes_per_proc)).
103 !> Neighbour list nodes are ordered according to their MPI ranks.
104 !> Thus, the first nnodes_per_proc(0) nodes are stored on MPI rank 0,
105 !> the next nnodes_per_proc(1) nodes reside on MPI rank 1, etc
106 !> Nodes for cell images along transport direction are merged into one node.
107 !> The number of non-zero DBCSR matrix blocks and their DBCSR cell image indices
108 !> are stored into 'n_dbcsr_cell_images_to_merge' and 'dbcsr_cell_image_to_merge' arrays
109 INTEGER, ALLOCATABLE, DIMENSION(:, :) :: nl_repl
110
111 !> number of DBCSR images for each local merged neighbour list node (1:nnodes_per_proc(para_env%mepos))
112 INTEGER, ALLOCATABLE, DIMENSION(:) :: n_dbcsr_cell_images_to_merge
113 !> list of DBCSR image indices to merge; (1:SUM(n_dbcsr_cell_images_to_merge))
114 INTEGER, ALLOCATABLE, DIMENSION(:) :: dbcsr_cell_image_to_merge
115
116 !> number of DBCSR non-zero matrix elements that should be received/sent from each MPI rank
117 !> (0:num_pe-1, 1:nelements_dbcsr_dim2)
118 INTEGER(kind=int_8), ALLOCATABLE, DIMENSION(:, :) :: nelements_per_proc
119
120 !> number of non-zero matrix elements local to this MPI rank.
121 INTEGER(kind=int_8) :: n_nonzero_elements = 0_int_8
122 INTEGER :: nrows = 0, ncols = 0
123 !> Number of non-zero matrix elements (columns) on each row
124 !> n_nonzero_cols(1:nrows); same as 'numh' in SMEAGOL code.
125 INTEGER, ALLOCATABLE, DIMENSION(:) :: n_nonzero_cols
126 !> offset of the first non-zero matrix elements on each row.
127 !> column_offset(1:nrows); same as 'listhptr' in SMEAGOL code.
128 !> It should be declared as INTEGER(kind=int_8), but SMEAGOL expects it to be INTEGER
129 INTEGER, ALLOCATABLE, DIMENSION(:) :: row_offset
130 !> column index of each non-zero matrix element.
131 !> col_index(1:n_nonzero_elements); same as 'listh' in SMEAGOL code
132 !> col index of the first non-zero matrix elements of irow row is col_index(row_offset(irow)+1)
133 INTEGER, ALLOCATABLE, DIMENSION(:) :: col_index
134 !> index of the non-zero matrix element in a communication buffer for each rank
135 INTEGER, ALLOCATABLE, DIMENSION(:) :: packed_index
136 !> R_atom_row - R_atom_col
137 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: xij
138 !> equivalent atomic orbitals
139 INTEGER, ALLOCATABLE, DIMENSION(:) :: indxuo
140 !> atomic index on which the orbital is centred
141 INTEGER, ALLOCATABLE, DIMENSION(:) :: iaorb
142 !> coordinates of all atoms in the supercell
143 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: xa
145
146CONTAINS
147
148! **************************************************************************************************
149!> \brief Map non-zero matrix blocks between sparse matrices in DBCSR and SIESTA formats.
150!> \param siesta_struct structure that stores metadata (sparsity pattern) of sparse SIESTA matrices
151!> \param matrix_dbcsr_kp DBCSR matrices for each cell image
152!> \param subsys QuickStep molecular system
153!> \param cell_to_index array to convert 3-D cell indices to 1-D DBCSR image indices
154!> \param sab_nl pair-wise neighbour list
155!> \param para_env MPI parallel environment
156!> \param max_ij_cell_image largest index of cell images along i and j cell vectors (e.g. (2,0) in
157!> case of 5 cell images (0,0), (1,0), (-1,0), (2,0), and (-2,0))
158!> \param do_merge merge DBCSR images along transport direction (k cell vector)
159!> \param gather_root distribute non-zero matrix elements of SIESTA matrices across all
160!> parallel processes (-1), or gather them on the given MPI rank (>= 0).
161! **************************************************************************************************
162 SUBROUTINE siesta_struct_create(siesta_struct, matrix_dbcsr_kp, subsys, cell_to_index, &
163 sab_nl, para_env, max_ij_cell_image, do_merge, gather_root)
165 INTENT(inout) :: siesta_struct
166 TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in) :: matrix_dbcsr_kp
167 TYPE(qs_subsys_type), POINTER :: subsys
168 INTEGER, DIMENSION(:, :, :), POINTER :: cell_to_index
169 TYPE(neighbor_list_set_p_type), DIMENSION(:), &
170 POINTER :: sab_nl
171 TYPE(mp_para_env_type), POINTER :: para_env
172 INTEGER, DIMENSION(2), INTENT(inout) :: max_ij_cell_image
173 LOGICAL, INTENT(in) :: do_merge
174 INTEGER, INTENT(in) :: gather_root
175
176 CHARACTER(len=*), PARAMETER :: routinen = 'siesta_struct_create'
177
178 CHARACTER(len=20) :: str_nelem, str_nelem_max
179 INTEGER :: handle, iatom, icol, icol_blk, icol_local, image, image_j, image_k, irow, &
180 irow_local, natoms, ncells_siesta_total, ncols_blk, ncols_total, nrows_local, &
181 nrows_total, offset
182 INTEGER(kind=int_8) :: n_nonzero_elements_local
183 INTEGER, DIMENSION(3) :: max_ijk_cell_image, ncells_siesta
184 INTEGER, DIMENSION(:), POINTER :: col_blk_offset, col_blk_size
185 LOGICAL :: do_distribute, is_root_rank
186 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: particle_coords
187 REAL(kind=dp), DIMENSION(3) :: real_cell_shift, scaled_cell_shift
188 TYPE(cell_type), POINTER :: cell
189 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
190
191 CALL timeset(routinen, handle)
192 do_distribute = gather_root < 0
193 is_root_rank = gather_root == para_env%mepos
194
195 ! here row_blk_offset / col_blk_offset are global indices of the first row / column of a given non-zero block.
196 ! They are not offsets (index-1) but the actual indices.
197 CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
198 nfullrows_total=nrows_total, nfullcols_total=ncols_total, &
199 nblkcols_total=ncols_blk, col_blk_size=col_blk_size, col_blk_offset=col_blk_offset)
200 IF (debug_this_module) THEN
201 cpassert(nrows_total == ncols_total)
202 cpassert(gather_root < para_env%num_pe)
203 END IF
204
205 siesta_struct%gather_root = gather_root
206 CALL get_neighbor_list_set_p(neighbor_list_sets=sab_nl, symmetric=siesta_struct%symmetric)
207
208 ! apply periodic boundary conditions to atomic coordinates
209 CALL qs_subsys_get(subsys, cell=cell, particle_set=particle_set, nparticle=natoms)
210 ALLOCATE (particle_coords(3, natoms))
211 DO iatom = 1, natoms
212 CALL pbc_0_1(particle_coords(1:3, iatom), particle_set(iatom)%r(1:3), cell)
213 END DO
214
215 ! Note: in case we would like to limit the number of cell images along transport direction (k cell vector)
216 ! by enabling 'BulkTransvCellSizeZ' keyword, we need to pass max_ijk_cell_image(1:3) vector
217 ! to the subroutine instead of the reduced vector max_ij_cell_image(1:2)
218 max_ijk_cell_image(1:2) = max_ij_cell_image(1:2)
219
220 ! determine the actual number of cell images along k cell vector. Unless the third element is also passed
221 ! via subroutine arguments, an extra MPI_Allreduce operation is needed each time we call
222 ! replicate_neighbour_list() / get_nnodes_local().
223 max_ijk_cell_image(3) = -1
224 ! bulk-transport calculation expects exactly 3 cell images along transport direction
225 IF (.NOT. do_merge) max_ijk_cell_image(3) = 1
226
227 ! replicate pair-wise neighbour list. Identical non-zero matrix blocks from cell image along transport direction
228 ! are grouped together if do_merge == .TRUE.
229 ALLOCATE (siesta_struct%nnodes_per_proc(0:para_env%num_pe - 1))
230 CALL replicate_neighbour_list(siesta_struct%nl_repl, &
231 siesta_struct%n_dbcsr_cell_images_to_merge, &
232 siesta_struct%dbcsr_cell_image_to_merge, &
233 siesta_struct%nnodes_per_proc, &
234 max_ijk_cell_image, sab_nl, para_env, particle_coords, cell, cell_to_index, do_merge)
235 max_ij_cell_image(1:2) = max_ijk_cell_image(1:2)
236
237 ! count number of non-zero matrix elements that need to be send to and received from other parallel processes
238 ALLOCATE (siesta_struct%nelements_per_proc(0:para_env%num_pe - 1, nelements_dbcsr_dim2))
239 CALL count_remote_dbcsr_elements(siesta_struct%nelements_per_proc, siesta_struct%nnodes_per_proc, &
240 siesta_struct%nl_repl, matrix_dbcsr_kp, siesta_struct%symmetric, para_env, gather_root)
241
242 ! number of SIESTA non-zero matrix elements that are going to be stored on this parallel process
243 n_nonzero_elements_local = sum(siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv))
244 siesta_struct%n_nonzero_elements = n_nonzero_elements_local
245
246 ! as SMEAGOL uses 32-bits integers, the number of non-zero matrix elements is limited by 2^31 per MPI rank.
247 ! Abort CP2K if we are about to exceed this limit.
248 IF (n_nonzero_elements_local > int(huge(0), kind=int_8)) THEN
249 WRITE (str_nelem, '(I0)') n_nonzero_elements_local
250 WRITE (str_nelem_max, '(I0)') huge(0)
251 CALL cp_abort(__location__, &
252 "The number of non-zero matrix elements per MPI process "//trim(str_nelem)// &
253 " cannot exceed "//trim(str_nelem_max)// &
254 ". Please increase the number of MPI processes to satisfy this SMEAGOL limitation.")
255 END IF
256
257 ! in case there is no nonzero matrix element stored on this process, allocate arrays with one element to avoid SEGFAULT
258 IF (n_nonzero_elements_local == 0) n_nonzero_elements_local = 1
259
260 ! number of SIESTA-matrix rows local to the given parallel process
261 IF (do_distribute) THEN
262#if defined(__SMEAGOL)
263 CALL getnodeorbs(nrows_total, para_env%mepos, para_env%num_pe, nrows_local)
264#else
265 CALL cp_abort(__location__, &
266 "CP2K was compiled with no SMEAGOL support.")
267#endif
268 ELSE
269 IF (is_root_rank) THEN
270 nrows_local = nrows_total
271 ELSE
272 nrows_local = 0
273 END IF
274 END IF
275
276 ! number of cell images along each cell vector. It is 2*m+1, as SIESTA images are ordered as 0, 1, -1, ..., m, -m
277 ncells_siesta(1:3) = 2*max_ijk_cell_image(1:3) + 1
278 ! in case of merged cell images along the transport direction, there will be just 1 'merged' cell image along it
279 IF (do_merge) ncells_siesta(3) = 1
280
281 ncells_siesta_total = ncells_siesta(1)*ncells_siesta(2)*ncells_siesta(3)
282
283 ! number of rows local to the given parallel process. Rows are distributed in a block-cyclic manner
284 siesta_struct%nrows = nrows_local
285
286 ! number of columns of the matrix in its dense form. SIESTA uses 1-D (rows) block-cyclic distribution.
287 ! All non-zero matrix elements on a given row are stored on the same parallel process
288 siesta_struct%ncols = nrows_total*ncells_siesta_total
289
290 ! allocate at least one array element to avoid SIGFAULT when passing unallocated arrays to subroutines
291 IF (nrows_local == 0) nrows_local = 1
292
293 ALLOCATE (siesta_struct%n_nonzero_cols(nrows_local))
294 ALLOCATE (siesta_struct%row_offset(nrows_local))
295 ALLOCATE (siesta_struct%col_index(n_nonzero_elements_local))
296 ALLOCATE (siesta_struct%packed_index(n_nonzero_elements_local))
297
298 ! restore the actual number of local rows
299 nrows_local = siesta_struct%nrows
300
301 ! get number of non-zero matrix element on each local row (n_nonzero_cols),
302 ! offset of the first non-zero matrix element for each local row (row_offset),
303 ! global column indices of all local non-zero matrix elements (col_index), and
304 ! the indices of all local non-zero matrix elements in the communication buffer (packed_index)
305 CALL get_nonzero_element_indices(siesta_struct%n_nonzero_cols, siesta_struct%row_offset, &
306 siesta_struct%col_index, siesta_struct%packed_index, &
307 siesta_struct%nl_repl, matrix_dbcsr_kp, &
308 siesta_struct%symmetric, para_env, gather_root)
309
310 ! indices of equivalent atomic orbitals
311 ALLOCATE (siesta_struct%indxuo(siesta_struct%ncols))
312 DO icol = 1, ncols_total
313 siesta_struct%indxuo(icol) = icol
314 END DO
315 DO image = 2, ncells_siesta_total
316 siesta_struct%indxuo((image - 1)*ncols_total + 1:image*ncols_total) = siesta_struct%indxuo(1:ncols_total)
317 END DO
318
319 ! particle index on which the orbital is centred
320 ALLOCATE (siesta_struct%iaorb(siesta_struct%ncols))
321 DO icol_blk = 1, ncols_blk
322 ! col_blk_offset() is not an offset but the index of the first atomic orbital in the column block
323 siesta_struct%iaorb(col_blk_offset(icol_blk):col_blk_offset(icol_blk) + col_blk_size(icol_blk) - 1) = icol_blk
324 END DO
325 DO image = 2, ncells_siesta_total
326 siesta_struct%iaorb((image - 1)*ncols_total + 1:image*ncols_total) = siesta_struct%iaorb(1:ncols_total) + (image - 1)*natoms
327 END DO
328
329 ! coordinates of all particles in each cell images
330 ALLOCATE (siesta_struct%xa(3, natoms*ncells_siesta_total))
331 DO image_k = 1, ncells_siesta(3)
332 !icell_siesta(3) = image_k
333 scaled_cell_shift(3) = real(number_from_canonical_enumeration(image_k), kind=dp) ! SIESTA -> actual cell index
334 DO image_j = 1, ncells_siesta(2)
335 scaled_cell_shift(2) = real(number_from_canonical_enumeration(image_j), kind=dp)
336 DO image = 1, ncells_siesta(1)
337 scaled_cell_shift(1) = real(number_from_canonical_enumeration(image), kind=dp)
338 CALL scaled_to_real(real_cell_shift, scaled_cell_shift, cell)
339 offset = (((image_k - 1)*ncells_siesta(2) + image_j - 1)*ncells_siesta(1) + image - 1)*natoms
340 DO iatom = 1, natoms
341 siesta_struct%xa(1:3, offset + iatom) = particle_set(iatom)%r(1:3) + real_cell_shift(1:3)
342 END DO
343 END DO
344 END DO
345 END DO
346
347 ! inter-atomic distance
348 ALLOCATE (siesta_struct%xij(3, n_nonzero_elements_local))
349 DO irow_local = 1, nrows_local
350 IF (do_distribute) THEN
351#if defined(__SMEAGOL)
352 CALL localtoglobalorb(irow_local, para_env%mepos, para_env%num_pe, irow)
353#else
354 CALL cp_abort(__location__, &
355 "CP2K was compiled with no SMEAGOL support.")
356#endif
357 ELSE
358 irow = irow_local
359 IF (debug_this_module) THEN
360 cpassert(is_root_rank)
361 END IF
362 END IF
363 offset = siesta_struct%row_offset(irow_local)
364 DO icol_local = offset + 1, offset + siesta_struct%n_nonzero_cols(irow_local)
365 icol = siesta_struct%col_index(icol_local)
366 siesta_struct%xij(1:3, icol_local) = siesta_struct%xa(1:3, siesta_struct%iaorb(icol)) - &
367 siesta_struct%xa(1:3, siesta_struct%iaorb(irow))
368 END DO
369 END DO
370
371 DEALLOCATE (particle_coords)
372
373 CALL timestop(handle)
374 END SUBROUTINE siesta_struct_create
375
376! **************************************************************************************************
377!> \brief Release a SIESTA matrix structure
378!> \param siesta_struct structure to release
379! **************************************************************************************************
380 SUBROUTINE siesta_struct_release(siesta_struct)
382 INTENT(inout) :: siesta_struct
383
384 CHARACTER(len=*), PARAMETER :: routinen = 'siesta_struct_release'
385
386 INTEGER :: handle
387
388 CALL timeset(routinen, handle)
389
390 siesta_struct%gather_root = -1
391
392 IF (ALLOCATED(siesta_struct%nnodes_per_proc)) DEALLOCATE (siesta_struct%nnodes_per_proc)
393 IF (ALLOCATED(siesta_struct%nl_repl)) DEALLOCATE (siesta_struct%nl_repl)
394 IF (ALLOCATED(siesta_struct%n_dbcsr_cell_images_to_merge)) DEALLOCATE (siesta_struct%n_dbcsr_cell_images_to_merge)
395 IF (ALLOCATED(siesta_struct%dbcsr_cell_image_to_merge)) DEALLOCATE (siesta_struct%dbcsr_cell_image_to_merge)
396 IF (ALLOCATED(siesta_struct%nelements_per_proc)) DEALLOCATE (siesta_struct%nelements_per_proc)
397
398 siesta_struct%n_nonzero_elements = 0
399 siesta_struct%nrows = 0
400 siesta_struct%ncols = 0
401
402 IF (ALLOCATED(siesta_struct%n_nonzero_cols)) DEALLOCATE (siesta_struct%n_nonzero_cols)
403 IF (ALLOCATED(siesta_struct%row_offset)) DEALLOCATE (siesta_struct%row_offset)
404 IF (ALLOCATED(siesta_struct%col_index)) DEALLOCATE (siesta_struct%col_index)
405 IF (ALLOCATED(siesta_struct%packed_index)) DEALLOCATE (siesta_struct%packed_index)
406
407 IF (ALLOCATED(siesta_struct%xij)) DEALLOCATE (siesta_struct%xij)
408 IF (ALLOCATED(siesta_struct%indxuo)) DEALLOCATE (siesta_struct%indxuo)
409 IF (ALLOCATED(siesta_struct%iaorb)) DEALLOCATE (siesta_struct%iaorb)
410 IF (ALLOCATED(siesta_struct%xa)) DEALLOCATE (siesta_struct%xa)
411
412 CALL timestop(handle)
413 END SUBROUTINE siesta_struct_release
414
415! **************************************************************************************************
416!> \brief Convert matrix from DBCSR to sparse SIESTA format.
417!> \param matrix_siesta matrix in SIESTA format [out]
418!> \param matrix_dbcsr_kp DBCSR matrix [in]
419!> \param siesta_struct structure to map matrix blocks between formats
420!> \param para_env MPI parallel environment
421! **************************************************************************************************
422 SUBROUTINE convert_dbcsr_to_distributed_siesta(matrix_siesta, matrix_dbcsr_kp, siesta_struct, para_env)
423 REAL(kind=dp), DIMENSION(:), INTENT(out) :: matrix_siesta
424 TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in) :: matrix_dbcsr_kp
425 TYPE(siesta_distrib_csc_struct_type), INTENT(in) :: siesta_struct
426 TYPE(mp_para_env_type), INTENT(in), POINTER :: para_env
427
428 CHARACTER(len=*), PARAMETER :: routinen = 'convert_dbcsr_to_distributed_siesta'
429
430 INTEGER :: first_col_minus_one, first_row_minus_one, handle, icol_blk, icol_local, &
431 image_dbcsr, image_ind, image_ind_offset, image_siesta, image_siesta_transp, inode, &
432 inode_proc, iproc, irequest, irow_blk, irow_local, irow_proc, mepos, n_image_ind, &
433 ncols_blk, ncols_local, nnodes_proc, node_offset, nprocs, nrequests_recv, &
434 nrequests_total, nrows_blk, nrows_local
435 INTEGER(kind=int_8) :: n_nonzero_elements_dbcsr, &
436 n_nonzero_elements_siesta, &
437 offset_recv_mepos, offset_send_mepos
438 INTEGER(kind=int_8), ALLOCATABLE, DIMENSION(:) :: n_packed_elements_per_proc, &
439 nelements_per_request, &
440 offset_per_proc, offset_per_request
441 INTEGER, ALLOCATABLE, DIMENSION(:) :: next_nonzero_element_offset, peer_rank, &
442 request_tag
443 INTEGER, DIMENSION(:), POINTER :: col_blk_offset, col_blk_size, &
444 row_blk_offset, row_blk_size
445 LOGICAL :: do_distribute, found, is_root_rank, &
446 symmetric
447 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: recv_buffer, reorder_recv_buffer, &
448 send_buffer
449 REAL(kind=dp), DIMENSION(:, :), POINTER :: sm_block, sm_block_merged
450 TYPE(mp_request_type), ALLOCATABLE, DIMENSION(:) :: requests
451
452 CALL timeset(routinen, handle)
453 matrix_siesta(:) = 0.0_dp
454
455 mepos = para_env%mepos
456 nprocs = para_env%num_pe
457 do_distribute = siesta_struct%gather_root < 0
458 is_root_rank = siesta_struct%gather_root == mepos
459
460 CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
461 nblkrows_total=nrows_blk, nblkcols_total=ncols_blk, &
462 row_blk_size=row_blk_size, col_blk_size=col_blk_size, &
463 row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
464 symmetric = siesta_struct%symmetric
465
466 ! number of locally stored SIESTA non-zero matrix elements
467 n_nonzero_elements_siesta = sum(siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv))
468 ! number of locally stored DBCSR non-zero matrix elements
469 n_nonzero_elements_dbcsr = sum(siesta_struct%nelements_per_proc(:, nelements_dbcsr_send))
470
471 ! number of concurrent MPI isend / irecv operations
472 nrequests_recv = get_number_of_mpi_sendrecv_requests(mepos, siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv), &
473 max_mpi_packet_size_dp)
474 nrequests_total = get_number_of_mpi_sendrecv_requests(mepos, siesta_struct%nelements_per_proc(:, nelements_dbcsr_send), &
475 max_mpi_packet_size_dp) + nrequests_recv
476
477 IF (nrequests_total > 0) THEN
478 ! allocate MPI-related arrays. request_tag is not actually needed, as MPI standard guarantees the order of
479 ! peer-to-peer messages with the same tag between same processes
480 ALLOCATE (requests(nrequests_total))
481 ALLOCATE (peer_rank(nrequests_total), request_tag(nrequests_total))
482 ALLOCATE (offset_per_request(nrequests_total), nelements_per_request(nrequests_total))
483 !requests(:) = mp_request_null
484
485 ! split large messages into a number of smaller messages. It is not really needed
486 ! unless we are going to send > 2^31 matrix elements per MPI request
487 IF (nrequests_recv > 0) THEN
488 CALL assign_nonzero_elements_to_requests(offset_per_request(1:nrequests_recv), &
489 nelements_per_request(1:nrequests_recv), &
490 peer_rank(1:nrequests_recv), &
491 request_tag(1:nrequests_recv), &
492 mepos, &
493 siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv), &
494 max_mpi_packet_size_dp)
495 END IF
496 IF (nrequests_total > nrequests_recv) THEN
497 CALL assign_nonzero_elements_to_requests(offset_per_request(nrequests_recv + 1:nrequests_total), &
498 nelements_per_request(nrequests_recv + 1:nrequests_total), &
499 peer_rank(nrequests_recv + 1:nrequests_total), &
500 request_tag(nrequests_recv + 1:nrequests_total), &
501 mepos, &
502 siesta_struct%nelements_per_proc(:, nelements_dbcsr_send), &
503 max_mpi_packet_size_dp)
504 END IF
505 END IF
506
507 ! point-to-point recv/send can be replaced with alltoallv, if data to distribute per rank is < 2^31 elements
508 ! (should be OK due to SMEAGOL limitation)
509 ! in principle, it is possible to overcome this limit by using derived datatypes
510 ! (which will require additional wrapper functions, indeed).
511 !
512 ! pre-post non-blocking receive operations
513 IF (n_nonzero_elements_siesta > 0) THEN
514 ALLOCATE (recv_buffer(n_nonzero_elements_siesta))
515 END IF
516 DO irequest = 1, nrequests_recv
517 CALL para_env%irecv(recv_buffer(offset_per_request(irequest) + 1: &
518 offset_per_request(irequest) + nelements_per_request(irequest)), &
519 peer_rank(irequest), requests(irequest), request_tag(irequest))
520 END DO
521
522 ! pack local DBCSR non-zero matrix elements ordering by their target parallel process
523 ALLOCATE (offset_per_proc(0:nprocs - 1), n_packed_elements_per_proc(0:nprocs - 1))
524 offset_per_proc(0) = 0
525 DO iproc = 1, nprocs - 1
526 offset_per_proc(iproc) = offset_per_proc(iproc - 1) + siesta_struct%nelements_per_proc(iproc - 1, nelements_dbcsr_send)
527 END DO
528 n_packed_elements_per_proc(:) = 0
529
530 ! number of local neighbour-list nodes and offset of the first local neighbour-list node
531 nnodes_proc = siesta_struct%nnodes_per_proc(mepos)
532 !node_offset = SUM(siesta_struct%nnodes_per_proc(0:mepos)) - siesta_struct%nnodes_per_proc(mepos)
533 node_offset = sum(siesta_struct%nnodes_per_proc(0:mepos)) - nnodes_proc
534
535 ! if do_distribute == .FALSE., send all matrix elements to MPI process with rank gather_root
536 ! in case of do_distribute == .TRUE., iproc is determined by calling WhichNodeOrb()
537 iproc = siesta_struct%gather_root
538
539 IF (n_nonzero_elements_dbcsr > 0) THEN
540 ALLOCATE (send_buffer(n_nonzero_elements_dbcsr))
541 send_buffer(:) = 0.0_dp
542
543 ! iterate over locally-stored DBCSR matrix blocks.
544 ! inode_proc is the target parallel process (where data are going to be sent)
545 image_ind_offset = 0
546 DO inode_proc = 1, nnodes_proc
547 n_image_ind = siesta_struct%n_dbcsr_cell_images_to_merge(inode_proc)
548 IF (n_image_ind > 0) THEN
549 inode = node_offset + inode_proc
550
551 irow_blk = siesta_struct%nl_repl(neighbor_list_iatom_index, inode)
552 icol_blk = siesta_struct%nl_repl(neighbor_list_jatom_index, inode)
553 cpassert(irow_blk <= icol_blk .OR. (.NOT. symmetric))
554 image_siesta = siesta_struct%nl_repl(neighbor_list_siesta_image_index, inode)
555 image_siesta_transp = siesta_struct%nl_repl(neighbor_list_siesta_transp_image_index, inode)
556
557 nrows_local = row_blk_size(irow_blk)
558 ncols_local = col_blk_size(icol_blk)
559 first_row_minus_one = row_blk_offset(irow_blk) - 1
560 first_col_minus_one = col_blk_offset(icol_blk) - 1
561
562 ! merging cell images along transport direction
563 IF (n_image_ind == 1) THEN
564 ! the most common case. Nothing to merge, so there is no need to allocate memory for a merged block
565 image_dbcsr = siesta_struct%dbcsr_cell_image_to_merge(image_ind_offset + 1)
566 CALL dbcsr_get_block_p(matrix=matrix_dbcsr_kp(image_dbcsr)%matrix, &
567 row=irow_blk, col=icol_blk, block=sm_block_merged, found=found)
568 cpassert(found)
569 ELSE ! n_image_ind > 1
570 ALLOCATE (sm_block_merged(nrows_local, ncols_local))
571
572 DO image_ind = 1, n_image_ind
573 image_dbcsr = siesta_struct%dbcsr_cell_image_to_merge(image_ind + image_ind_offset)
574
575 CALL dbcsr_get_block_p(matrix=matrix_dbcsr_kp(image_dbcsr)%matrix, &
576 row=irow_blk, col=icol_blk, block=sm_block, found=found)
577 cpassert(found)
578 sm_block_merged(1:nrows_local, 1:ncols_local) = sm_block_merged(1:nrows_local, 1:ncols_local) + &
579 sm_block(1:nrows_local, 1:ncols_local)
580 END DO
581 END IF
582
583 ! pack matrix elements for the 'normal' SIESTA matrix block
584 IF (image_siesta > 0) THEN
585 DO irow_local = 1, nrows_local
586 IF (do_distribute) THEN
587#if defined(__SMEAGOL)
588 CALL whichnodeorb(irow_local + first_row_minus_one, nprocs, iproc)
589#else
590 CALL cp_abort(__location__, &
591 "CP2K was compiled with no SMEAGOL support.")
592#endif
593 END IF
594
595 ! CPASSERT
596 IF (debug_this_module) THEN
597 cpassert(iproc >= 0 .AND. iproc < nprocs)
598 IF (n_packed_elements_per_proc(iproc) + ncols_local > &
599 siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
600 CALL cp__a(__short_file__, __line__)
601 END IF
602 END IF
603
604 offset_send_mepos = offset_per_proc(iproc) + n_packed_elements_per_proc(iproc)
605 send_buffer(offset_send_mepos + 1:offset_send_mepos + ncols_local) = sm_block_merged(irow_local, 1:ncols_local)
606
607 n_packed_elements_per_proc(iproc) = n_packed_elements_per_proc(iproc) + ncols_local
608 END DO
609 END IF
610
611 ! pack matrix elements of the transposed SIESTA matrix block
612 IF (image_siesta_transp > 0) THEN
613 DO icol_local = 1, ncols_local
614 IF (do_distribute) THEN
615#if defined(__SMEAGOL)
616 CALL whichnodeorb(icol_local + first_col_minus_one, nprocs, iproc) ! iproc_orb
617#else
618 CALL cp_abort(__location__, &
619 "CP2K was compiled with no SMEAGOL support.")
620#endif
621 END IF
622
623 ! CPASSERT
624 IF (debug_this_module) THEN
625 cpassert(iproc >= 0 .AND. iproc < nprocs)
626 IF (n_packed_elements_per_proc(iproc) + nrows_local > &
627 siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
628 CALL cp__a(__short_file__, __line__)
629 END IF
630 END IF
631
632 offset_send_mepos = offset_per_proc(iproc) + n_packed_elements_per_proc(iproc)
633 send_buffer(offset_send_mepos + 1:offset_send_mepos + nrows_local) = sm_block_merged(1:nrows_local, icol_local)
634
635 n_packed_elements_per_proc(iproc) = n_packed_elements_per_proc(iproc) + nrows_local
636 END DO
637 END IF
638
639 IF (n_image_ind > 1) THEN
640 DEALLOCATE (sm_block_merged)
641 END IF
642 END IF
643
644 image_ind_offset = image_ind_offset + siesta_struct%n_dbcsr_cell_images_to_merge(inode_proc)
645 END DO
646
647 IF (debug_this_module) THEN
648 DO iproc = 0, nprocs - 1
649 IF (n_packed_elements_per_proc(iproc) /= siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
650 CALL cp__a(__short_file__, __line__)
651 END IF
652 END DO
653 END IF
654
655 ! send packed data to other parallel processes
656 DO irequest = nrequests_recv + 1, nrequests_total
657 CALL para_env%isend(send_buffer(offset_per_request(irequest) + 1: &
658 offset_per_request(irequest) + nelements_per_request(irequest)), &
659 peer_rank(irequest), requests(irequest), request_tag(irequest))
660 END DO
661
662 ! copy data locally that stay on the same process.
663 IF (mepos > 0) THEN
664 offset_recv_mepos = sum(siesta_struct%nelements_per_proc(0:mepos - 1, nelements_dbcsr_recv))
665 ELSE
666 offset_recv_mepos = 0
667 END IF
668 offset_send_mepos = offset_per_proc(mepos)
669
670 IF (debug_this_module) THEN
671 IF (n_packed_elements_per_proc(mepos) /= siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_recv)) THEN
672 CALL cp__a(__short_file__, __line__)
673 END IF
674 END IF
675
676 IF (n_packed_elements_per_proc(mepos) > 0) THEN
677 recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + n_packed_elements_per_proc(mepos)) = &
678 send_buffer(offset_send_mepos + 1:offset_send_mepos + n_packed_elements_per_proc(mepos))
679 END IF
680 END IF
681
682 IF (nrequests_total > 0) THEN
683 ! wait for pending isend/irecv requests
684 CALL mp_waitall(requests)
685 DEALLOCATE (nelements_per_request, offset_per_request, peer_rank, requests, request_tag)
686 END IF
687
688 ! release send buffers
689 IF (ALLOCATED(send_buffer)) DEALLOCATE (send_buffer)
690 DEALLOCATE (offset_per_proc, n_packed_elements_per_proc)
691
692 ! non-zero matrix elements in 'recv_buffer' array are grouped by their source MPI rank,
693 ! local row index, and column index (in this order).
694 ! Reorder the matrix elements ('reorder_recv_buffer') so they are grouped by their local row index,
695 ! source MPI rank, and column index.
696 ! (column indices are in the ascending order within each (row index, source MPI rank) block).
697 ! The array 'packed_index' allows mapping matrix element between these intermediate order and SIESTA order :
698 ! local row index, column index
699 IF (n_nonzero_elements_siesta > 0) THEN
700 ALLOCATE (reorder_recv_buffer(n_nonzero_elements_siesta))
701 ALLOCATE (next_nonzero_element_offset(siesta_struct%nrows))
702 next_nonzero_element_offset(:) = 0
703 offset_recv_mepos = 0
704
705 DO inode = 1, SIZE(siesta_struct%nl_repl, 2)
706 irow_blk = siesta_struct%nl_repl(neighbor_list_iatom_index, inode)
707 icol_blk = siesta_struct%nl_repl(neighbor_list_jatom_index, inode)
708 cpassert(irow_blk <= icol_blk .OR. (.NOT. symmetric))
709 image_siesta = siesta_struct%nl_repl(neighbor_list_siesta_image_index, inode)
710 image_siesta_transp = siesta_struct%nl_repl(neighbor_list_siesta_transp_image_index, inode)
711
712 nrows_local = row_blk_size(irow_blk)
713 ncols_local = col_blk_size(icol_blk)
714 first_row_minus_one = row_blk_offset(irow_blk) - 1
715 first_col_minus_one = col_blk_offset(icol_blk) - 1
716
717 ! normal block
718 IF (image_siesta > 0) THEN
719 DO irow_local = 1, nrows_local
720 IF (do_distribute) THEN
721#if defined(__SMEAGOL)
722 CALL globaltolocalorb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
723#else
724 CALL cp_abort(__location__, &
725 "CP2K was compiled with no SMEAGOL support.")
726#endif
727 ELSE
728 IF (is_root_rank) THEN
729 irow_proc = irow_local + first_row_minus_one
730 ELSE
731 irow_proc = 0
732 END IF
733 END IF
734 IF (irow_proc > 0) THEN
735 offset_send_mepos = siesta_struct%row_offset(irow_proc) + next_nonzero_element_offset(irow_proc)
736 reorder_recv_buffer(offset_send_mepos + 1:offset_send_mepos + ncols_local) = &
737 recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + ncols_local)
738 offset_recv_mepos = offset_recv_mepos + ncols_local
739 next_nonzero_element_offset(irow_proc) = next_nonzero_element_offset(irow_proc) + ncols_local
740 END IF
741 END DO
742 END IF
743
744 ! transposed block
745 IF (image_siesta_transp > 0) THEN
746 DO icol_local = 1, ncols_local
747 IF (do_distribute) THEN
748#if defined(__SMEAGOL)
749 CALL globaltolocalorb(icol_local + first_col_minus_one, mepos, nprocs, irow_proc)
750#else
751 CALL cp_abort(__location__, &
752 "CP2K was compiled with no SMEAGOL support.")
753#endif
754 ELSE
755 IF (is_root_rank) THEN
756 irow_proc = icol_local + first_col_minus_one
757 ELSE
758 irow_proc = 0
759 END IF
760 END IF
761 IF (irow_proc > 0) THEN
762 offset_send_mepos = siesta_struct%row_offset(irow_proc) + next_nonzero_element_offset(irow_proc)
763 reorder_recv_buffer(offset_send_mepos + 1:offset_send_mepos + nrows_local) = &
764 recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + nrows_local)
765 offset_recv_mepos = offset_recv_mepos + nrows_local
766 next_nonzero_element_offset(irow_proc) = next_nonzero_element_offset(irow_proc) + nrows_local
767 END IF
768 END DO
769 END IF
770 END DO
771
772 IF (debug_this_module) THEN
773 DO irow_local = 1, siesta_struct%nrows
774 IF (siesta_struct%n_nonzero_cols(irow_local) /= next_nonzero_element_offset(irow_local)) THEN
775 CALL cp__a(__short_file__, __line__)
776 END IF
777 END DO
778 END IF
779
780 DEALLOCATE (next_nonzero_element_offset)
781 DEALLOCATE (recv_buffer)
782
783 ! Map non-zero matrix element between the intermediate order and SIESTA order
784 DO irow_local = 1, siesta_struct%nrows
785 offset_recv_mepos = siesta_struct%row_offset(irow_local)
786 DO icol_local = 1, siesta_struct%n_nonzero_cols(irow_local)
787 matrix_siesta(offset_recv_mepos + icol_local) = &
788 reorder_recv_buffer(offset_recv_mepos + siesta_struct%packed_index(offset_recv_mepos + icol_local))
789 END DO
790 END DO
791 DEALLOCATE (reorder_recv_buffer)
792 END IF
793
794 CALL timestop(handle)
796
797! **************************************************************************************************
798!> \brief Convert matrix from DBCSR to sparse SIESTA format.
799!> \param matrix_dbcsr_kp DBCSR matrix [out]. The matrix is declared as INTENT(in) as pointers to
800!> dbcsr matrices remain intact. However we have intention to update matrix elements
801!> \param matrix_siesta matrix in SIESTA format [in]
802!> \param siesta_struct structure to map matrix blocks between formats
803!> \param para_env MPI parallel environment
804! **************************************************************************************************
805 SUBROUTINE convert_distributed_siesta_to_dbcsr(matrix_dbcsr_kp, matrix_siesta, siesta_struct, para_env)
806 TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in) :: matrix_dbcsr_kp
807 REAL(kind=dp), DIMENSION(:), INTENT(in) :: matrix_siesta
808 TYPE(siesta_distrib_csc_struct_type), INTENT(in) :: siesta_struct
809 TYPE(mp_para_env_type), INTENT(in), POINTER :: para_env
810
811 CHARACTER(len=*), PARAMETER :: routinen = 'convert_distributed_siesta_to_dbcsr'
812
813 INTEGER :: first_col_minus_one, first_row_minus_one, handle, icol_blk, icol_local, &
814 image_dbcsr, image_siesta, image_siesta_transp, inode, inode_proc, iproc, irequest, &
815 irow_blk, irow_local, irow_proc, mepos, n_image_ind, ncols_blk, ncols_local, nnodes_proc, &
816 node_offset, nprocs, nrequests_recv, nrequests_total, nrows_blk, nrows_local
817 INTEGER(kind=int_8) :: n_nonzero_elements_dbcsr, &
818 n_nonzero_elements_siesta, &
819 offset_recv_mepos, offset_send_mepos
820 INTEGER(kind=int_8), ALLOCATABLE, DIMENSION(:) :: n_packed_elements_per_proc, &
821 nelements_per_request, &
822 offset_per_proc, offset_per_request
823 INTEGER, ALLOCATABLE, DIMENSION(:) :: next_nonzero_element_offset, peer_rank, &
824 request_tag
825 INTEGER, DIMENSION(:), POINTER :: col_blk_offset, col_blk_size, &
826 row_blk_offset, row_blk_size
827 LOGICAL :: do_distribute, found, is_root_rank, &
828 symmetric
829 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: recv_buffer, reorder_send_buffer, &
830 send_buffer
831 REAL(kind=dp), DIMENSION(:, :), POINTER :: sm_block
832 TYPE(mp_request_type), ALLOCATABLE, DIMENSION(:) :: requests
833
834 CALL timeset(routinen, handle)
835 DO image_dbcsr = 1, SIZE(matrix_dbcsr_kp)
836 CALL dbcsr_set(matrix_dbcsr_kp(image_dbcsr)%matrix, 0.0_dp)
837 END DO
838
839 mepos = para_env%mepos
840 nprocs = para_env%num_pe
841 do_distribute = siesta_struct%gather_root < 0
842 is_root_rank = siesta_struct%gather_root == mepos
843
844 CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
845 nblkrows_total=nrows_blk, nblkcols_total=ncols_blk, &
846 row_blk_size=row_blk_size, col_blk_size=col_blk_size, &
847 row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
848 symmetric = siesta_struct%symmetric
849
850 n_nonzero_elements_siesta = sum(siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv))
851 n_nonzero_elements_dbcsr = sum(siesta_struct%nelements_per_proc(:, nelements_dbcsr_send))
852
853 nrequests_recv = get_number_of_mpi_sendrecv_requests(mepos, siesta_struct%nelements_per_proc(:, nelements_dbcsr_send), &
854 max_mpi_packet_size_dp)
855 nrequests_total = get_number_of_mpi_sendrecv_requests(mepos, siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv), &
856 max_mpi_packet_size_dp) + nrequests_recv
857 IF (nrequests_total > 0) THEN
858 ALLOCATE (requests(nrequests_total))
859 ALLOCATE (peer_rank(nrequests_total), request_tag(nrequests_total))
860 ALLOCATE (offset_per_request(nrequests_total), nelements_per_request(nrequests_total))
861 !requests(:) = mp_request_null
862 IF (nrequests_recv > 0) THEN
863 CALL assign_nonzero_elements_to_requests(offset_per_request(1:nrequests_recv), &
864 nelements_per_request(1:nrequests_recv), &
865 peer_rank(1:nrequests_recv), &
866 request_tag(1:nrequests_recv), &
867 mepos, &
868 siesta_struct%nelements_per_proc(:, nelements_dbcsr_send), &
869 max_mpi_packet_size_dp)
870 END IF
871 IF (nrequests_total > nrequests_recv) THEN
872 CALL assign_nonzero_elements_to_requests(offset_per_request(nrequests_recv + 1:nrequests_total), &
873 nelements_per_request(nrequests_recv + 1:nrequests_total), &
874 peer_rank(nrequests_recv + 1:nrequests_total), &
875 request_tag(nrequests_recv + 1:nrequests_total), &
876 mepos, &
877 siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv), &
878 max_mpi_packet_size_dp)
879 END IF
880 END IF
881
882 IF (n_nonzero_elements_dbcsr > 0) THEN
883 ALLOCATE (recv_buffer(n_nonzero_elements_dbcsr))
884 END IF
885 DO irequest = 1, nrequests_recv
886 CALL para_env%irecv(recv_buffer(offset_per_request(irequest) + 1: &
887 offset_per_request(irequest) + nelements_per_request(irequest)), &
888 peer_rank(irequest), requests(irequest), request_tag(irequest))
889 END DO
890
891 ALLOCATE (offset_per_proc(0:nprocs - 1), n_packed_elements_per_proc(0:nprocs - 1))
892 offset_per_proc(0) = 0
893 DO iproc = 1, nprocs - 1
894 offset_per_proc(iproc) = offset_per_proc(iproc - 1) + siesta_struct%nelements_per_proc(iproc - 1, nelements_dbcsr_send)
895 END DO
896 n_packed_elements_per_proc(:) = 0
897
898 IF (mepos > 0) THEN
899 node_offset = sum(siesta_struct%nnodes_per_proc(0:mepos - 1))
900 ELSE
901 node_offset = 0
902 END IF
903 nnodes_proc = siesta_struct%nnodes_per_proc(mepos)
904
905 IF (n_nonzero_elements_siesta > 0) THEN
906 ALLOCATE (send_buffer(n_nonzero_elements_siesta))
907
908 ALLOCATE (reorder_send_buffer(n_nonzero_elements_siesta))
909 DO irow_local = 1, siesta_struct%nrows
910 offset_send_mepos = siesta_struct%row_offset(irow_local)
911 DO icol_local = 1, siesta_struct%n_nonzero_cols(irow_local)
912 reorder_send_buffer(offset_send_mepos + siesta_struct%packed_index(offset_send_mepos + icol_local)) = &
913 matrix_siesta(offset_send_mepos + icol_local)
914 END DO
915 END DO
916
917 ALLOCATE (next_nonzero_element_offset(siesta_struct%nrows))
918 next_nonzero_element_offset(:) = 0
919 offset_send_mepos = 0
920
921 DO inode = 1, SIZE(siesta_struct%nl_repl, 2)
922 irow_blk = siesta_struct%nl_repl(neighbor_list_iatom_index, inode)
923 icol_blk = siesta_struct%nl_repl(neighbor_list_jatom_index, inode)
924 cpassert(irow_blk <= icol_blk .OR. (.NOT. symmetric))
925 image_siesta = siesta_struct%nl_repl(neighbor_list_siesta_image_index, inode)
926 image_siesta_transp = siesta_struct%nl_repl(neighbor_list_siesta_transp_image_index, inode)
927
928 nrows_local = row_blk_size(irow_blk)
929 ncols_local = col_blk_size(icol_blk)
930 first_row_minus_one = row_blk_offset(irow_blk) - 1
931 first_col_minus_one = col_blk_offset(icol_blk) - 1
932
933 IF (image_siesta > 0) THEN
934 DO irow_local = 1, nrows_local
935 IF (do_distribute) THEN
936#if defined(__SMEAGOL)
937 CALL globaltolocalorb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
938#else
939 CALL cp_abort(__location__, &
940 "CP2K was compiled with no SMEAGOL support.")
941#endif
942 ELSE
943 IF (is_root_rank) THEN
944 irow_proc = irow_local + first_row_minus_one
945 ELSE
946 irow_proc = 0
947 END IF
948 END IF
949 IF (irow_proc > 0) THEN
950 offset_recv_mepos = siesta_struct%row_offset(irow_proc) + next_nonzero_element_offset(irow_proc)
951 send_buffer(offset_send_mepos + 1:offset_send_mepos + ncols_local) = &
952 reorder_send_buffer(offset_recv_mepos + 1:offset_recv_mepos + ncols_local)
953 offset_send_mepos = offset_send_mepos + ncols_local
954 next_nonzero_element_offset(irow_proc) = next_nonzero_element_offset(irow_proc) + ncols_local
955 END IF
956 END DO
957 END IF
958
959 ! transposed block
960 IF (image_siesta_transp > 0) THEN
961 DO icol_local = 1, ncols_local
962 IF (do_distribute) THEN
963#if defined(__SMEAGOL)
964 CALL globaltolocalorb(icol_local + first_col_minus_one, mepos, nprocs, irow_proc)
965#else
966 CALL cp_abort(__location__, &
967 "CP2K was compiled with no SMEAGOL support.")
968#endif
969 ELSE
970 IF (is_root_rank) THEN
971 irow_proc = icol_local + first_col_minus_one
972 ELSE
973 irow_proc = 0
974 END IF
975 END IF
976 IF (irow_proc > 0) THEN
977 offset_recv_mepos = siesta_struct%row_offset(irow_proc) + next_nonzero_element_offset(irow_proc)
978 send_buffer(offset_send_mepos + 1:offset_send_mepos + nrows_local) = &
979 reorder_send_buffer(offset_recv_mepos + 1:offset_recv_mepos + nrows_local)
980 offset_send_mepos = offset_send_mepos + nrows_local
981 next_nonzero_element_offset(irow_proc) = next_nonzero_element_offset(irow_proc) + nrows_local
982 END IF
983 END DO
984 END IF
985 END DO
986
987 IF (debug_this_module) THEN
988 DO irow_local = 1, siesta_struct%nrows
989 IF (siesta_struct%n_nonzero_cols(irow_local) /= next_nonzero_element_offset(irow_local)) THEN
990 CALL cp__a(__short_file__, __line__)
991 END IF
992 END DO
993 END IF
994
995 DEALLOCATE (next_nonzero_element_offset)
996 DEALLOCATE (reorder_send_buffer)
997
998 DO irequest = nrequests_recv + 1, nrequests_total
999 CALL para_env%isend(send_buffer(offset_per_request(irequest) + 1: &
1000 offset_per_request(irequest) + nelements_per_request(irequest)), &
1001 peer_rank(irequest), requests(irequest), request_tag(irequest))
1002 END DO
1003
1004 ! copy data locally that stay on the same process.
1005 IF (mepos > 0) THEN
1006 offset_send_mepos = sum(siesta_struct%nelements_per_proc(0:mepos - 1, nelements_dbcsr_recv))
1007 ELSE
1008 offset_send_mepos = 0
1009 END IF
1010 offset_recv_mepos = offset_per_proc(mepos)
1011
1012 IF (debug_this_module) THEN
1013 IF (siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_recv) /= &
1014 siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_send)) THEN
1015 CALL cp__a(__short_file__, __line__)
1016 END IF
1017 END IF
1018
1019 IF (siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_send) > 0) THEN
1020 recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_send)) = &
1021 send_buffer(offset_send_mepos + 1:offset_send_mepos + siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_send))
1022 END IF
1023 END IF
1024
1025 IF (nrequests_total > 0) THEN
1026 ! wait for pending isend/irecv requests
1027 CALL mp_waitall(requests)
1028 DEALLOCATE (nelements_per_request, offset_per_request, peer_rank, requests, request_tag)
1029 END IF
1030
1031 IF (ALLOCATED(send_buffer)) DEALLOCATE (send_buffer)
1032
1033 ! if do_distribute == .FALSE., collect matrix elements from MPI process with rank gather_root
1034 iproc = siesta_struct%gather_root
1035 IF (n_nonzero_elements_dbcsr > 0) THEN
1036 DO inode_proc = 1, nnodes_proc
1037 n_image_ind = siesta_struct%n_dbcsr_cell_images_to_merge(inode_proc)
1038 IF (n_image_ind > 0) THEN
1039 inode = node_offset + inode_proc
1040
1041 irow_blk = siesta_struct%nl_repl(neighbor_list_iatom_index, inode)
1042 icol_blk = siesta_struct%nl_repl(neighbor_list_jatom_index, inode)
1043 image_dbcsr = siesta_struct%nl_repl(neighbor_list_dbcsr_image_index, inode)
1044 cpassert(irow_blk <= icol_blk .OR. (.NOT. symmetric))
1045 image_siesta = siesta_struct%nl_repl(neighbor_list_siesta_image_index, inode)
1046 image_siesta_transp = siesta_struct%nl_repl(neighbor_list_siesta_transp_image_index, inode)
1047
1048 nrows_local = row_blk_size(irow_blk)
1049 ncols_local = col_blk_size(icol_blk)
1050 first_row_minus_one = row_blk_offset(irow_blk) - 1
1051 first_col_minus_one = col_blk_offset(icol_blk) - 1
1052
1053 CALL dbcsr_get_block_p(matrix=matrix_dbcsr_kp(image_dbcsr)%matrix, &
1054 row=irow_blk, col=icol_blk, block=sm_block, found=found)
1055 cpassert(found)
1056
1057 IF (image_siesta > 0) THEN
1058 DO irow_local = 1, nrows_local
1059 IF (do_distribute) THEN
1060#if defined(__SMEAGOL)
1061 CALL whichnodeorb(irow_local + first_row_minus_one, nprocs, iproc) ! iproc_orb
1062#else
1063 CALL cp_abort(__location__, &
1064 "CP2K was compiled with no SMEAGOL support.")
1065#endif
1066 END IF
1067 ! CPASSERT
1068 IF (debug_this_module) THEN
1069 cpassert(iproc >= 0 .AND. iproc < nprocs)
1070 IF (n_packed_elements_per_proc(iproc) + ncols_local > &
1071 siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
1072 CALL cp__a(__short_file__, __line__)
1073 END IF
1074 END IF
1075
1076 offset_recv_mepos = offset_per_proc(iproc) + n_packed_elements_per_proc(iproc)
1077 sm_block(irow_local, 1:ncols_local) = recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + ncols_local)
1078
1079 n_packed_elements_per_proc(iproc) = n_packed_elements_per_proc(iproc) + ncols_local
1080 END DO
1081 END IF
1082
1083 ! transposed block
1084 IF (image_siesta_transp > 0) THEN
1085 DO icol_local = 1, ncols_local
1086 IF (do_distribute) THEN
1087#if defined(__SMEAGOL)
1088 CALL whichnodeorb(icol_local + first_col_minus_one, nprocs, iproc) ! iproc_orb
1089#else
1090 CALL cp_abort(__location__, &
1091 "CP2K was compiled with no SMEAGOL support.")
1092#endif
1093 END IF
1094 ! CPASSERT
1095 IF (debug_this_module) THEN
1096 cpassert(iproc >= 0 .AND. iproc < nprocs)
1097 IF (n_packed_elements_per_proc(iproc) + nrows_local > &
1098 siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
1099 CALL cp__a(__short_file__, __line__)
1100 END IF
1101 END IF
1102
1103 offset_recv_mepos = offset_per_proc(iproc) + n_packed_elements_per_proc(iproc)
1104 sm_block(1:nrows_local, icol_local) = recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + nrows_local)
1105
1106 n_packed_elements_per_proc(iproc) = n_packed_elements_per_proc(iproc) + nrows_local
1107 END DO
1108 END IF
1109 END IF
1110 END DO
1111
1112 IF (debug_this_module) THEN
1113 DO iproc = 0, nprocs - 1
1114 IF (n_packed_elements_per_proc(iproc) /= siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
1115 CALL cp__a(__short_file__, __line__)
1116 END IF
1117 END DO
1118 END IF
1119
1120 DEALLOCATE (recv_buffer)
1121 END IF
1122
1123 DEALLOCATE (offset_per_proc, n_packed_elements_per_proc)
1124
1125 CALL timestop(handle)
1127
1128 ! *** PRIVATE SUBROUTINES ***
1129
1130! **************************************************************************************************
1131!> \brief Computes number of neighbour-list nodes on the current parallel process.
1132!> \param nnodes_local number of nodes [out]
1133!> \param max_ijk_cell_image_local largest index of cell images along i, j and k cell vectors
1134!> on this parallel process [out]
1135!> \param max_ijk_cell_image largest index of cell images along i, j and k cell vectors [inout]
1136!> \param sab_nl pair-wise neighbour list [in]
1137!> \param para_env MPI parallel environment [in]
1138!> \param particle_coords list of atomic coordinates subject to periodic boundary conditions [in]
1139!> \param cell simulation unit cell [in]
1140! **************************************************************************************************
1141 SUBROUTINE get_nnodes_local(nnodes_local, max_ijk_cell_image_local, max_ijk_cell_image, sab_nl, para_env, particle_coords, cell)
1142 INTEGER, INTENT(out) :: nnodes_local
1143 INTEGER, DIMENSION(3), INTENT(out) :: max_ijk_cell_image_local
1144 INTEGER, DIMENSION(3), INTENT(inout) :: max_ijk_cell_image
1145 TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1146 INTENT(in), POINTER :: sab_nl
1147 TYPE(mp_para_env_type), INTENT(in), POINTER :: para_env
1148 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
1149 INTENT(in) :: particle_coords
1150 TYPE(cell_type), INTENT(in), POINTER :: cell
1151
1152 CHARACTER(len=*), PARAMETER :: routinen = 'get_nnodes_local'
1153
1154 INTEGER :: handle, iatom, icoord, jatom
1155 INTEGER, DIMENSION(3) :: cell_ijk, max_ijk_cell_image_tmp
1156 LOGICAL :: update_ncells
1157 REAL(kind=dp), DIMENSION(3) :: r_ij
1159 DIMENSION(:), POINTER :: nl_iterator
1160
1161 CALL timeset(routinen, handle)
1162
1163 update_ncells = .false.
1164 DO icoord = 1, 3 ! x, y, z
1165 IF (max_ijk_cell_image(icoord) >= 0) THEN
1166 max_ijk_cell_image_tmp(icoord) = max_ijk_cell_image(icoord)
1167 ELSE
1168 max_ijk_cell_image_tmp(icoord) = huge(max_ijk_cell_image_tmp(icoord))
1169 update_ncells = .true.
1170 END IF
1171 END DO
1172
1173 nnodes_local = 0
1174 max_ijk_cell_image_local(:) = 0
1175
1176 CALL neighbor_list_iterator_create(nl_iterator, sab_nl)
1177 DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
1178 CALL get_iterator_info(nl_iterator, iatom=iatom, jatom=jatom, r=r_ij)
1179 CALL get_negf_cell_ijk(cell_ijk, r_ij, r_i=particle_coords(1:3, iatom), r_j=particle_coords(1:3, jatom), cell=cell)
1180 cell_ijk(1:3) = abs(cell_ijk(1:3))
1181
1182 IF (cell_ijk(1) <= max_ijk_cell_image_tmp(1) .AND. cell_ijk(2) <= max_ijk_cell_image_tmp(2) .AND. &
1183 cell_ijk(3) <= max_ijk_cell_image_tmp(3)) THEN
1184 nnodes_local = nnodes_local + 1
1185 max_ijk_cell_image_local(1:3) = max(max_ijk_cell_image_local(1:3), cell_ijk(1:3))
1186 END IF
1187 END DO
1188 CALL neighbor_list_iterator_release(nl_iterator)
1189
1190 IF (update_ncells) THEN
1191 max_ijk_cell_image_tmp(1:3) = max_ijk_cell_image_local(1:3)
1192 CALL para_env%max(max_ijk_cell_image_tmp)
1193 DO icoord = 1, 3
1194 IF (max_ijk_cell_image(icoord) < 0) THEN
1195 max_ijk_cell_image(icoord) = max_ijk_cell_image_tmp(icoord)
1196 END IF
1197 END DO
1198 END IF
1199
1200 CALL timestop(handle)
1201 END SUBROUTINE get_nnodes_local
1202
1203! **************************************************************************************************
1204!> \brief Construct list of neighbour-list's nodes on the current parallel process.
1205!> \param nl_local non-merged local neighbour-list's nodes [out]
1206!> \param max_ijk_cell_image_local largest index of cell images along i, j and k cell vectors
1207!> on this parallel process [in]
1208!> \param max_ijk_cell_image largest index of cell images along i, j and k cell vectors [in]
1209!> \param sab_nl pair-wise neighbour list [in]
1210!> \param particle_coords list of atomic coordinates subject to periodic boundary conditions [in]
1211!> \param cell simulation unit cell [in]
1212!> \param cell_to_index array to convert 3-D cell indices to 1-D DBCSR image indices
1213!> \param do_merge merge cell images along transport direction [in]
1214!> \param node_merged_indices nodes-related indices. Nodes with identical indices will be merged [out]
1215!> \param k_cells list of cell image indices along transport direction. Nodes to
1216!> be merged have the same 'node_merged_indices' but different 'k_cells' indices [out]
1217! **************************************************************************************************
1218 SUBROUTINE get_nl_nodes_local(nl_local, max_ijk_cell_image_local, max_ijk_cell_image, sab_nl, particle_coords, &
1219 cell, cell_to_index, do_merge, node_merged_indices, k_cells)
1220 INTEGER, DIMENSION(:, :), INTENT(out) :: nl_local
1221 INTEGER, DIMENSION(3), INTENT(in) :: max_ijk_cell_image_local, &
1222 max_ijk_cell_image
1223 TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1224 INTENT(in), POINTER :: sab_nl
1225 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
1226 INTENT(in) :: particle_coords
1227 TYPE(cell_type), INTENT(in), POINTER :: cell
1228 INTEGER, DIMENSION(:, :, :), INTENT(in), POINTER :: cell_to_index
1229 LOGICAL, INTENT(in) :: do_merge
1230 INTEGER(kind=int_8), DIMENSION(:), INTENT(out) :: node_merged_indices
1231 INTEGER, DIMENSION(:), INTENT(out) :: k_cells
1232
1233 CHARACTER(len=*), PARAMETER :: routinen = 'get_nl_nodes_local'
1234
1235 INTEGER :: handle, iatom, icol_blk, image, inode, &
1236 irow_blk, jatom, natoms
1237 INTEGER(kind=8), DIMENSION(2) :: ncells_siesta_local
1238 INTEGER, DIMENSION(2) :: ncells_siesta
1239 INTEGER, DIMENSION(3) :: cell_ijk_abs, cell_ijk_dbcsr, &
1240 cell_ijk_siesta
1241 LOGICAL :: do_symmetric
1242 REAL(kind=dp), DIMENSION(3) :: r_ij
1244 DIMENSION(:), POINTER :: nl_iterator
1245
1246 CALL timeset(routinen, handle)
1247 ! natoms only used to compute a merged 1D index of each DBCSR block
1248 natoms = SIZE(particle_coords, 2)
1249 CALL get_neighbor_list_set_p(neighbor_list_sets=sab_nl, symmetric=do_symmetric)
1250 ncells_siesta(1:2) = 2*max_ijk_cell_image(1:2) + 1
1251
1252 ncells_siesta_local(1:2) = int(2*max_ijk_cell_image_local(1:2) + 1, kind=int_8)
1253
1254 inode = 0
1255 CALL neighbor_list_iterator_create(nl_iterator, sab_nl)
1256 DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
1257 CALL get_iterator_info(nl_iterator, iatom=iatom, jatom=jatom, cell=cell_ijk_dbcsr, r=r_ij)
1258 CALL get_negf_cell_ijk(cell_ijk_abs, r_ij, r_i=particle_coords(1:3, iatom), r_j=particle_coords(1:3, jatom), cell=cell)
1259
1260 IF (abs(cell_ijk_abs(1)) <= max_ijk_cell_image(1) .AND. abs(cell_ijk_abs(2)) <= max_ijk_cell_image(2) .AND. &
1261 abs(cell_ijk_abs(3)) <= max_ijk_cell_image(3)) THEN
1262
1263 inode = inode + 1
1264
1265 image = get_index_by_cell(cell_ijk_dbcsr, cell_to_index)
1266 cpassert(image > 0)
1267 nl_local(neighbor_list_dbcsr_image_index, inode) = image
1268
1269 IF (do_symmetric .AND. iatom > jatom) THEN
1270 irow_blk = jatom
1271 icol_blk = iatom
1272 cell_ijk_abs(1:3) = -cell_ijk_abs(1:3)
1273 ELSE
1274 irow_blk = iatom
1275 icol_blk = jatom
1276 END IF
1277
1278 nl_local(neighbor_list_iatom_index, inode) = irow_blk
1279 nl_local(neighbor_list_jatom_index, inode) = icol_blk
1280
1281 cell_ijk_siesta(1:3) = index_in_canonical_enumeration(cell_ijk_abs(1:3)) ! absolute -> SIESTA
1282
1283 IF (do_merge) THEN
1284 node_merged_indices(inode) = (((cell_ijk_siesta(2) - 1)*ncells_siesta_local(1) + cell_ijk_siesta(1) - 1)* &
1285 int(natoms, kind=int_8) + icol_blk - 1)*int(natoms, kind=int_8) + int(irow_blk - 1, kind=int_8)
1286 image = cell_ijk_siesta(1) + ncells_siesta(1)*(cell_ijk_siesta(2) - 1)
1287 ELSE
1288 node_merged_indices(inode) = ((((cell_ijk_siesta(3) - 1)*ncells_siesta_local(2) + &
1289 cell_ijk_siesta(2) - 1)*ncells_siesta_local(1) + cell_ijk_siesta(1) - 1)* &
1290 int(natoms, kind=int_8) + icol_blk - 1)*int(natoms, kind=int_8) + int(irow_blk - 1, kind=int_8)
1291 image = cell_ijk_siesta(1) + ncells_siesta(1)*(cell_ijk_siesta(2) - 1 + ncells_siesta(2)*(cell_ijk_siesta(3) - 1))
1292 END IF
1293 k_cells(inode) = cell_ijk_siesta(3)
1294 nl_local(neighbor_list_siesta_image_index, inode) = image
1295
1296 IF (do_symmetric .AND. irow_blk /= icol_blk) THEN
1297 cell_ijk_abs(1:3) = -cell_ijk_abs(1:3)
1298 cell_ijk_siesta(1:3) = index_in_canonical_enumeration(cell_ijk_abs(1:3)) ! absolute -> SIESTA
1299 IF (do_merge) cell_ijk_siesta(3) = 1
1300 nl_local(neighbor_list_siesta_transp_image_index, inode) = &
1301 cell_ijk_siesta(1) + ncells_siesta(1)*(cell_ijk_siesta(2) - 1 + ncells_siesta(2)*(cell_ijk_siesta(3) - 1))
1302 ELSE
1303 nl_local(neighbor_list_siesta_transp_image_index, inode) = 0
1304 END IF
1305 END IF
1306 END DO
1307 CALL neighbor_list_iterator_release(nl_iterator)
1308
1309 IF (debug_this_module) THEN
1310 cpassert(SIZE(nl_local, 2) == inode)
1311 END IF
1312
1313 CALL timestop(handle)
1314 END SUBROUTINE get_nl_nodes_local
1315
1316! **************************************************************************************************
1317!> \brief Replicate (and optionally merge) pair-wise neighbour list.
1318!> \param repl_nl replicated neighbour list. It needs to be deallocated elsewhere [allocated]
1319!> \param n_dbcsr_cell_images_to_merge number of merged blocks per neighbour-list node [allocated]
1320!> \param dbcsr_cell_image_to_merge list of DBCSR image indices to merge [allocated]
1321!> \param nnodes_per_proc number of merged nodes on each parallel processes [out]
1322!> \param max_ijk_cell_image largest index of cell images along i, j and k cell vectors [inout]
1323!> \param sab_nl pair-wise neighbour list [in]
1324!> \param para_env MPI parallel environment [in]
1325!> \param particle_coords list of atomic coordinates subject to periodic boundary conditions [in]
1326!> \param cell simulation unit cell [in]
1327!> \param cell_to_index array to convert 3-D cell indices to 1-D DBCSR image indices [in]
1328!> \param do_merge merge cell images along transport direction [in]
1329! **************************************************************************************************
1330 SUBROUTINE replicate_neighbour_list(repl_nl, n_dbcsr_cell_images_to_merge, dbcsr_cell_image_to_merge, &
1331 nnodes_per_proc, max_ijk_cell_image, sab_nl, para_env, particle_coords, &
1332 cell, cell_to_index, do_merge)
1333 INTEGER, ALLOCATABLE, DIMENSION(:, :), &
1334 INTENT(inout) :: repl_nl
1335 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(inout) :: n_dbcsr_cell_images_to_merge, &
1336 dbcsr_cell_image_to_merge
1337 INTEGER, DIMENSION(0:), INTENT(out) :: nnodes_per_proc
1338 INTEGER, DIMENSION(3), INTENT(inout) :: max_ijk_cell_image
1339 TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1340 INTENT(in), POINTER :: sab_nl
1341 TYPE(mp_para_env_type), INTENT(in), POINTER :: para_env
1342 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
1343 INTENT(in) :: particle_coords
1344 TYPE(cell_type), INTENT(in), POINTER :: cell
1345 INTEGER, DIMENSION(:, :, :), INTENT(in), POINTER :: cell_to_index
1346 LOGICAL, INTENT(in) :: do_merge
1347
1348 CHARACTER(len=*), PARAMETER :: routinen = 'replicate_neighbour_list'
1349
1350 INTEGER :: handle, inode, iproc, kcell_closest, &
1351 nnodes_local, nnodes_merged, &
1352 nnodes_repl, offset_inode
1353 INTEGER(kind=int_8), ALLOCATABLE, DIMENSION(:) :: node_merged_indices
1354 INTEGER, ALLOCATABLE, DIMENSION(:) :: inodes_orig, k_cells
1355 INTEGER, ALLOCATABLE, DIMENSION(:, :) :: nl_local
1356 INTEGER, DIMENSION(3) :: max_ijk_cell_image_local
1357
1358 CALL timeset(routinen, handle)
1359 cpassert(.NOT. ALLOCATED(repl_nl))
1360 cpassert(.NOT. ALLOCATED(n_dbcsr_cell_images_to_merge))
1361 cpassert(.NOT. ALLOCATED(dbcsr_cell_image_to_merge))
1362
1363 CALL get_nnodes_local(nnodes_local, max_ijk_cell_image_local, max_ijk_cell_image, sab_nl, para_env, particle_coords, cell)
1364
1365 nnodes_per_proc(:) = 0
1366
1367 IF (nnodes_local > 0) THEN
1368 ALLOCATE (nl_local(neighbor_list_dim1, nnodes_local))
1369 ALLOCATE (node_merged_indices(nnodes_local))
1370 ALLOCATE (k_cells(nnodes_local))
1371 CALL get_nl_nodes_local(nl_local, max_ijk_cell_image_local, max_ijk_cell_image, sab_nl, particle_coords, cell, &
1372 cell_to_index, do_merge, node_merged_indices, k_cells)
1373
1374 ALLOCATE (inodes_orig(nnodes_local))
1375 CALL sort(node_merged_indices, nnodes_local, inodes_orig)
1376
1377 nnodes_merged = 1
1378 DO inode = 2, nnodes_local
1379 IF (node_merged_indices(inode) > node_merged_indices(inode - 1)) nnodes_merged = nnodes_merged + 1
1380 END DO
1381 ELSE
1382 nnodes_merged = 0
1383 END IF
1384
1385 nnodes_per_proc(para_env%mepos) = nnodes_merged
1386 CALL para_env%sum(nnodes_per_proc)
1387
1388 nnodes_repl = sum(nnodes_per_proc(:))
1389 ALLOCATE (repl_nl(neighbor_list_dim1, nnodes_repl))
1390
1391 IF (nnodes_local > 0) THEN
1392 IF (para_env%mepos > 0) THEN
1393 offset_inode = sum(nnodes_per_proc(0:para_env%mepos - 1))
1394 ELSE
1395 offset_inode = 0
1396 END IF
1397
1398 ALLOCATE (n_dbcsr_cell_images_to_merge(nnodes_merged))
1399 ALLOCATE (dbcsr_cell_image_to_merge(nnodes_local))
1400 n_dbcsr_cell_images_to_merge(:) = 0
1401
1402 nnodes_merged = 1 !offset_inode + 1
1403 repl_nl(:, offset_inode + 1) = nl_local(:, inodes_orig(1))
1404 n_dbcsr_cell_images_to_merge(1) = 1
1405 dbcsr_cell_image_to_merge(1) = nl_local(neighbor_list_dbcsr_image_index, inodes_orig(1))
1406 kcell_closest = k_cells(inodes_orig(1))
1407 DO inode = 2, nnodes_local
1408 IF (node_merged_indices(inode) > node_merged_indices(inode - 1)) THEN
1409 nnodes_merged = nnodes_merged + 1
1410 repl_nl(:, offset_inode + nnodes_merged) = nl_local(:, inodes_orig(inode))
1411 !n_dbcsr_cell_images_to_merge(nnodes_merged) = 1
1412 kcell_closest = k_cells(inodes_orig(inode))
1413 ELSE
1414 IF (abs(k_cells(inodes_orig(inode))) < abs(kcell_closest) .OR. &
1415 (abs(k_cells(inodes_orig(inode))) == abs(kcell_closest) .AND. kcell_closest < 0)) THEN
1416 repl_nl(:, offset_inode + nnodes_merged) = nl_local(:, inodes_orig(inode))
1417 kcell_closest = k_cells(inodes_orig(inode))
1418 END IF
1419 END IF
1420 dbcsr_cell_image_to_merge(inode) = nl_local(neighbor_list_dbcsr_image_index, inodes_orig(inode))
1421 n_dbcsr_cell_images_to_merge(nnodes_merged) = n_dbcsr_cell_images_to_merge(nnodes_merged) + 1
1422 END DO
1423
1424 IF (debug_this_module) THEN
1425 cpassert(sum(n_dbcsr_cell_images_to_merge) == nnodes_local)
1426 END IF
1427
1428 DEALLOCATE (inodes_orig)
1429 DEALLOCATE (node_merged_indices, k_cells)
1430 DEALLOCATE (nl_local)
1431 END IF
1432
1433 IF (para_env%num_pe > 1) THEN
1434 offset_inode = 0
1435 DO iproc = 0, para_env%num_pe - 1
1436 IF (nnodes_per_proc(iproc) > 0) THEN
1437 CALL para_env%bcast(repl_nl(:, offset_inode + 1:offset_inode + nnodes_per_proc(iproc)), iproc)
1438 offset_inode = offset_inode + nnodes_per_proc(iproc)
1439 END IF
1440 END DO
1441 END IF
1442
1443 CALL timestop(handle)
1444 END SUBROUTINE replicate_neighbour_list
1445
1446! **************************************************************************************************
1447!> \brief Count number of DBCSR matrix elements that should be received from (*,nelements_dbcsr_recv)
1448!> and send to (*,nelements_dbcsr_send) each parallel process.
1449!> \param nelements_per_proc number of non-zero matrix elements for each MPI process
1450!> \param nnodes_per_proc number of non-zero DBCSR matrix blocks (neighbour-list nodes)
1451!> \param nl_repl replicated neighbour list
1452!> \param matrix_dbcsr_kp DBCSR matrix
1453!> \param symmetric whether the DBCSR matrix is a symmetric one
1454!> \param para_env parallel environment
1455!> \param gather_root if >=0, gather all non-zero matrix element on the MPI process with
1456!> gather_root rank (useful for bulk transport calculation).
1457!> If <0, distribute non-zero matrix element across all MPI processes
1458! **************************************************************************************************
1459 SUBROUTINE count_remote_dbcsr_elements(nelements_per_proc, nnodes_per_proc, nl_repl, matrix_dbcsr_kp, &
1460 symmetric, para_env, gather_root)
1461 INTEGER(kind=int_8), DIMENSION(0:, :), INTENT(out) :: nelements_per_proc
1462 INTEGER, DIMENSION(0:), INTENT(in) :: nnodes_per_proc
1463 INTEGER, DIMENSION(:, :), INTENT(in) :: nl_repl
1464 TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in) :: matrix_dbcsr_kp
1465 LOGICAL, INTENT(in) :: symmetric
1466 TYPE(mp_para_env_type), INTENT(in), POINTER :: para_env
1467 INTEGER, INTENT(in) :: gather_root
1468
1469 CHARACTER(len=*), PARAMETER :: routinen = 'count_remote_dbcsr_elements'
1470
1471 INTEGER :: first_row_minus_one, handle, icol_blk, image, image_transp, inode, inode_proc, &
1472 iproc, iproc_orb, irow_blk, irow_local, mepos, ncols_blk, ncols_local, nnodes_proc, &
1473 nprocs, nrows_blk, nrows_local, offset_inode
1474 INTEGER, DIMENSION(:), POINTER :: col_blk_offset, col_blk_size, &
1475 row_blk_offset, row_blk_size
1476 LOGICAL :: do_distribute
1477
1478 CALL timeset(routinen, handle)
1479 nelements_per_proc(:, :) = 0
1480 mepos = para_env%mepos
1481 nprocs = para_env%num_pe
1482 do_distribute = gather_root < 0
1483 IF (debug_this_module) THEN
1484 cpassert(SIZE(nnodes_per_proc) == nprocs)
1485 END IF
1486
1487 CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
1488 nblkrows_total=nrows_blk, nblkcols_total=ncols_blk, &
1489 row_blk_size=row_blk_size, col_blk_size=col_blk_size, &
1490 row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
1491
1492 offset_inode = 0
1493 iproc_orb = gather_root ! if do_distribute == .FALSE., send all matrix elements to MPI process with rank gather_root
1494 DO iproc = lbound(nnodes_per_proc, 1), ubound(nnodes_per_proc, 1)
1495 nnodes_proc = nnodes_per_proc(iproc)
1496 DO inode_proc = 1, nnodes_proc
1497 inode = inode_proc + offset_inode
1498
1499 irow_blk = nl_repl(neighbor_list_iatom_index, inode)
1500 icol_blk = nl_repl(neighbor_list_jatom_index, inode)
1501 cpassert(irow_blk <= icol_blk .OR. (.NOT. symmetric))
1502 image = nl_repl(neighbor_list_siesta_image_index, inode)
1503 image_transp = nl_repl(neighbor_list_siesta_transp_image_index, inode)
1504
1505 IF (image > 0) THEN
1506 nrows_local = row_blk_size(irow_blk)
1507 first_row_minus_one = row_blk_offset(irow_blk) - 1
1508 ncols_local = col_blk_size(icol_blk)
1509 DO irow_local = 1, nrows_local
1510 IF (do_distribute) THEN
1511#if defined(__SMEAGOL)
1512 CALL whichnodeorb(irow_local + first_row_minus_one, nprocs, iproc_orb)
1513#else
1514 CALL cp_abort(__location__, &
1515 "CP2K was compiled with no SMEAGOL support.")
1516#endif
1517 END IF
1518 IF (iproc_orb == mepos) THEN
1519 nelements_per_proc(iproc, nelements_dbcsr_recv) = nelements_per_proc(iproc, nelements_dbcsr_recv) + &
1520 ncols_local
1521 END IF
1522
1523 IF (iproc == mepos) THEN
1524 nelements_per_proc(iproc_orb, nelements_dbcsr_send) = nelements_per_proc(iproc_orb, nelements_dbcsr_send) + &
1525 ncols_local
1526 END IF
1527 END DO
1528 END IF
1529
1530 ! transposed block
1531 IF (image_transp > 0) THEN
1532 nrows_local = col_blk_size(icol_blk)
1533 first_row_minus_one = col_blk_offset(icol_blk) - 1
1534 ncols_local = row_blk_size(irow_blk)
1535 DO irow_local = 1, nrows_local
1536 IF (do_distribute) THEN
1537#if defined(__SMEAGOL)
1538 CALL whichnodeorb(irow_local + first_row_minus_one, nprocs, iproc_orb)
1539#else
1540 CALL cp_abort(__location__, &
1541 "CP2K was compiled with no SMEAGOL support.")
1542#endif
1543 END IF
1544 IF (iproc_orb == mepos) THEN
1545 nelements_per_proc(iproc, nelements_dbcsr_recv) = nelements_per_proc(iproc, nelements_dbcsr_recv) + &
1546 ncols_local
1547 END IF
1548
1549 IF (iproc == mepos) THEN
1550 nelements_per_proc(iproc_orb, nelements_dbcsr_send) = nelements_per_proc(iproc_orb, nelements_dbcsr_send) + &
1551 ncols_local
1552 END IF
1553 END DO
1554 END IF
1555 END DO
1556 offset_inode = offset_inode + nnodes_proc
1557 END DO
1558 CALL timestop(handle)
1559 END SUBROUTINE count_remote_dbcsr_elements
1560
1561! **************************************************************************************************
1562!> \brief Construct list of non-zero matrix elements' indices in SIESTA format.
1563!> \param n_nonzero_cols number of non-zero matrix elements on each matrix row local to the current
1564!> MPI process
1565!> \param row_offset offset of the first non-zero matrix elements for each locally-stores row
1566!> \param col_index sorted list of column indices of non-zero matrix element
1567!> \param packed_index original order of non-sorted column indices
1568!> \param nl_repl replicated neighbour list
1569!> \param matrix_dbcsr_kp DBCSR matrix
1570!> \param symmetric whether the DBCSR matrix is a symmetric one
1571!> \param para_env parallel environment
1572!> \param gather_root if >=0, gather all non-zero matrix element on the MPI process with
1573!> gather_root rank (useful for bulk transport calculation).
1574!> If <0, distribute non-zero matrix element across all MPI processes
1575! **************************************************************************************************
1576 SUBROUTINE get_nonzero_element_indices(n_nonzero_cols, row_offset, col_index, packed_index, &
1577 nl_repl, matrix_dbcsr_kp, symmetric, para_env, gather_root)
1578 INTEGER, DIMENSION(:), INTENT(out) :: n_nonzero_cols, row_offset, col_index, &
1579 packed_index
1580 INTEGER, DIMENSION(:, :), INTENT(in) :: nl_repl
1581 TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in) :: matrix_dbcsr_kp
1582 LOGICAL, INTENT(in) :: symmetric
1583 TYPE(mp_para_env_type), INTENT(in), POINTER :: para_env
1584 INTEGER, INTENT(in) :: gather_root
1585
1586 CHARACTER(len=*), PARAMETER :: routinen = 'get_nonzero_element_indices'
1587
1588 INTEGER :: first_col_minus_one, first_row_minus_one, handle, icol_blk, icol_local, &
1589 icol_offset, image, image_transp, inode, irow_blk, irow_local, irow_proc, mepos, &
1590 ncols_blk, ncols_local, ncols_total, nnodes, nprocs, nrows_blk, nrows_local, nrows_total
1591 INTEGER, DIMENSION(:), POINTER :: col_blk_offset, col_blk_size, &
1592 row_blk_offset, row_blk_size
1593 LOGICAL :: do_distribute, is_root_rank
1594
1595 CALL timeset(routinen, handle)
1596 n_nonzero_cols(:) = 0
1597 mepos = para_env%mepos
1598 nprocs = para_env%num_pe
1599 do_distribute = gather_root < 0
1600 is_root_rank = gather_root == mepos
1601
1602 CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
1603 nblkrows_total=nrows_blk, nblkcols_total=ncols_blk, &
1604 nfullrows_total=nrows_total, nfullcols_total=ncols_total, &
1605 row_blk_size=row_blk_size, col_blk_size=col_blk_size, &
1606 row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
1607
1608 nnodes = SIZE(nl_repl, 2)
1609 DO inode = 1, nnodes
1610 irow_blk = nl_repl(neighbor_list_iatom_index, inode)
1611 icol_blk = nl_repl(neighbor_list_jatom_index, inode)
1612 cpassert(irow_blk <= icol_blk .OR. (.NOT. symmetric))
1613 image = nl_repl(neighbor_list_siesta_image_index, inode)
1614 image_transp = nl_repl(neighbor_list_siesta_transp_image_index, inode)
1615
1616 IF (image > 0) THEN
1617 nrows_local = row_blk_size(irow_blk)
1618 first_row_minus_one = row_blk_offset(irow_blk) - 1
1619 ncols_local = col_blk_size(icol_blk)
1620 DO irow_local = 1, nrows_local
1621 IF (do_distribute) THEN
1622#if defined(__SMEAGOL)
1623 CALL globaltolocalorb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
1624#else
1625 CALL cp_abort(__location__, &
1626 "CP2K was compiled with no SMEAGOL support.")
1627#endif
1628 ELSE
1629 IF (is_root_rank) THEN
1630 irow_proc = irow_local + first_row_minus_one
1631 ELSE
1632 irow_proc = 0
1633 END IF
1634 END IF
1635 IF (irow_proc > 0) THEN
1636 n_nonzero_cols(irow_proc) = n_nonzero_cols(irow_proc) + ncols_local
1637 END IF
1638 END DO
1639 END IF
1640
1641 ! transposed block
1642 IF (image_transp > 0) THEN
1643 nrows_local = col_blk_size(icol_blk)
1644 first_row_minus_one = col_blk_offset(icol_blk) - 1
1645 ncols_local = row_blk_size(irow_blk)
1646 DO irow_local = 1, nrows_local
1647 IF (do_distribute) THEN
1648#if defined(__SMEAGOL)
1649 CALL globaltolocalorb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
1650#else
1651 CALL cp_abort(__location__, &
1652 "CP2K was compiled with no SMEAGOL support.")
1653#endif
1654 ELSE
1655 IF (is_root_rank) THEN
1656 irow_proc = irow_local + first_row_minus_one
1657 ELSE
1658 irow_proc = 0
1659 END IF
1660 END IF
1661 IF (irow_proc > 0) THEN
1662 n_nonzero_cols(irow_proc) = n_nonzero_cols(irow_proc) + ncols_local
1663 END IF
1664 END DO
1665 END IF
1666 END DO
1667
1668 row_offset(1) = 0
1669 DO irow_local = 1, SIZE(n_nonzero_cols) - 1
1670 row_offset(irow_local + 1) = row_offset(irow_local) + n_nonzero_cols(irow_local)
1671 END DO
1672
1673 n_nonzero_cols(:) = 0
1674 col_index(:) = 0
1675 DO inode = 1, nnodes
1676 irow_blk = nl_repl(neighbor_list_iatom_index, inode)
1677 icol_blk = nl_repl(neighbor_list_jatom_index, inode)
1678 cpassert(irow_blk <= icol_blk .OR. (.NOT. symmetric))
1679 image = nl_repl(neighbor_list_siesta_image_index, inode)
1680 image_transp = nl_repl(neighbor_list_siesta_transp_image_index, inode)
1681
1682 IF (image > 0) THEN
1683 nrows_local = row_blk_size(irow_blk)
1684 first_row_minus_one = row_blk_offset(irow_blk) - 1
1685 ncols_local = col_blk_size(icol_blk)
1686 first_col_minus_one = col_blk_offset(icol_blk) + (image - 1)*ncols_total - 1
1687 DO irow_local = 1, nrows_local
1688 IF (do_distribute) THEN
1689#if defined(__SMEAGOL)
1690 CALL globaltolocalorb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
1691#else
1692 CALL cp_abort(__location__, &
1693 "CP2K was compiled with no SMEAGOL support.")
1694#endif
1695 ELSE
1696 IF (is_root_rank) THEN
1697 irow_proc = irow_local + first_row_minus_one
1698 ELSE
1699 irow_proc = 0
1700 END IF
1701 END IF
1702 IF (irow_proc > 0) THEN
1703 icol_offset = row_offset(irow_proc) + n_nonzero_cols(irow_proc)
1704 DO icol_local = 1, ncols_local
1705 col_index(icol_offset + icol_local) = first_col_minus_one + icol_local
1706 END DO
1707 n_nonzero_cols(irow_proc) = n_nonzero_cols(irow_proc) + ncols_local
1708 END IF
1709 END DO
1710 END IF
1711
1712 ! transposed block
1713 IF (image_transp > 0) THEN
1714 nrows_local = col_blk_size(icol_blk)
1715 first_row_minus_one = col_blk_offset(icol_blk) - 1
1716 ncols_local = row_blk_size(irow_blk)
1717 first_col_minus_one = row_blk_offset(irow_blk) + (image_transp - 1)*nrows_total - 1
1718 DO irow_local = 1, nrows_local
1719 IF (do_distribute) THEN
1720#if defined(__SMEAGOL)
1721 CALL globaltolocalorb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
1722#else
1723 CALL cp_abort(__location__, &
1724 "CP2K was compiled with no SMEAGOL support.")
1725#endif
1726 ELSE
1727 IF (is_root_rank) THEN
1728 irow_proc = irow_local + first_row_minus_one
1729 ELSE
1730 irow_proc = 0
1731 END IF
1732 END IF
1733 IF (irow_proc > 0) THEN
1734 icol_offset = row_offset(irow_proc) + n_nonzero_cols(irow_proc)
1735 DO icol_local = 1, ncols_local
1736 col_index(icol_offset + icol_local) = first_col_minus_one + icol_local
1737 END DO
1738 n_nonzero_cols(irow_proc) = n_nonzero_cols(irow_proc) + ncols_local
1739 END IF
1740 END DO
1741 END IF
1742 END DO
1743
1744 IF (SIZE(n_nonzero_cols) > 0) THEN
1745 DO irow_local = 1, SIZE(n_nonzero_cols)
1746 CALL sort(col_index(row_offset(irow_local) + 1:row_offset(irow_local) + n_nonzero_cols(irow_local)), &
1747 n_nonzero_cols(irow_local), &
1748 packed_index(row_offset(irow_local) + 1:row_offset(irow_local) + n_nonzero_cols(irow_local)))
1749 END DO
1750 END IF
1751
1752 CALL timestop(handle)
1753 END SUBROUTINE get_nonzero_element_indices
1754
1755! **************************************************************************************************
1756!> \brief Get absolute i, j, and k indices of cell image for the given DBCSR matrix block.
1757!> Effective cell image recorded in the neighbour-list (where the matrix block is actually
1758!> stored) depends on atomic coordinates can be significantly different.
1759!> \param cell_ijk array with 3 indices along the cell's vectors
1760!> \param r_ij actual interatomic distance (vector R_j - r_i), where R_j is the coordinates
1761!> of the j-th atom in the supercell
1762!> \param r_i coordinates of the i-th atom in the primary unit cell
1763!> \param r_j coordinates of the j-th atom in the primary unit cell
1764!> \param cell unit cell
1765! **************************************************************************************************
1766 SUBROUTINE get_negf_cell_ijk(cell_ijk, r_ij, r_i, r_j, cell)
1767 INTEGER, DIMENSION(3), INTENT(out) :: cell_ijk
1768 REAL(kind=dp), DIMENSION(3), INTENT(in) :: r_ij, r_i, r_j
1769 TYPE(cell_type), INTENT(in), POINTER :: cell
1770
1771 REAL(kind=dp), DIMENSION(3) :: coords_scaled, r
1772
1773 r(:) = r_ij(:) + r_i(:) - r_j(:)
1774 CALL real_to_scaled(coords_scaled, r, cell)
1775 cell_ijk(:) = nint(coords_scaled(:))
1776 END SUBROUTINE get_negf_cell_ijk
1777
1778! **************************************************************************************************
1779!> \brief Return the index of an integer number in the sequence 0, 1, -1, ..., n, -n, ...
1780!> (canonical enumeration of integers, oeis.org/A001057).
1781!> \param inum integer number [in]
1782!> \return index of 'inum' in A001057
1783!> \note Cell images in SMEAGOL / SIESTA are ordered according to A001057. Therefore this
1784!> function converts the absolute index of a cell replica along some (x/y/z) dimension
1785!> into its corresponding SIESTA's index.
1786! **************************************************************************************************
1787 ELEMENTAL FUNCTION index_in_canonical_enumeration(inum) RESULT(ind)
1788 INTEGER, INTENT(in) :: inum
1789 INTEGER :: ind
1790
1791 INTEGER :: inum_abs, is_non_positive
1792
1793 inum_abs = abs(inum)
1794 !IF (inum <= 0) THEN; is_non_positive = 1; ELSE; is_non_positive = 0; END IF
1795 is_non_positive = merge(1, 0, inum <= 0)
1796
1797 ! inum = 0 -> inum_abs = 0, is_non_positive = 1 -> ind = 1
1798 ! inum = 1 -> inum_abs = 1, is_non_positive = 0 -> ind = 2
1799 ! inum = -1 -> inum_abs = 1, is_non_positive = 1 -> ind = 3
1800 ind = 2*inum_abs + is_non_positive
1801 END FUNCTION index_in_canonical_enumeration
1802
1803! **************************************************************************************************
1804!> \brief Return an integer number according to its index in the sequence 0, 1, -1, ..., n, -n, ...
1805!> (canonical enumeration of integers, oeis.org/A001057)
1806!> \param ind index in A001057 starting from 1
1807!> \return integer number according to its position 'ind' in A001057
1808!> \note Cell images in SMEAGOL / SIESTA are ordered according to A001057. Therefore this
1809!> function converts SIESTA's index of a cell replica along some (x/y/z) dimension
1810!> into the corresponding absolute index.
1811! **************************************************************************************************
1812 ELEMENTAL FUNCTION number_from_canonical_enumeration(ind) RESULT(inum)
1813 INTEGER, INTENT(in) :: ind
1814 INTEGER :: inum
1815
1816 ! ind < 1 is invalid
1817 ! ind = 1 -> SIGN(0, -1) = 0
1818 ! ind = 2 -> SIGN(1, 0) = 1
1819 ! ind = 3 -> SIGN(1, -1) = -1
1820 inum = sign(ind/2, -mod(ind, 2))
1821 END FUNCTION number_from_canonical_enumeration
1822
1823! **************************************************************************************************
1824!> \brief Apply periodic boundary conditions defined by a simulation cell to a position
1825!> vector r. Similar to pbc1 from cell_types.F but returns unscaled coordinates from
1826!> the scaled range [0, 1) instead of [-0.5, 0.5)
1827!> \param r_pbc position vector subject to the periodic boundary conditions [out]
1828!> \param r initial position vector [in]
1829!> \param cell simulation unit cell [in]
1830! **************************************************************************************************
1831 PURE SUBROUTINE pbc_0_1(r_pbc, r, cell)
1832 REAL(kind=dp), DIMENSION(3), INTENT(out) :: r_pbc
1833 REAL(kind=dp), DIMENSION(3), INTENT(in) :: r
1834 TYPE(cell_type), INTENT(in), POINTER :: cell
1835
1836 REAL(kind=dp), DIMENSION(3) :: s
1837
1838 IF (cell%orthorhombic) THEN
1839 r_pbc(1) = r(1) - cell%hmat(1, 1)*cell%perd(1)*real(floor(cell%h_inv(1, 1)*r(1)), dp)
1840 r_pbc(2) = r(2) - cell%hmat(2, 2)*cell%perd(2)*real(floor(cell%h_inv(2, 2)*r(2)), dp)
1841 r_pbc(3) = r(3) - cell%hmat(3, 3)*cell%perd(3)*real(floor(cell%h_inv(3, 3)*r(3)), dp)
1842 ELSE
1843 s(1) = cell%h_inv(1, 1)*r(1) + cell%h_inv(1, 2)*r(2) + cell%h_inv(1, 3)*r(3)
1844 s(2) = cell%h_inv(2, 1)*r(1) + cell%h_inv(2, 2)*r(2) + cell%h_inv(2, 3)*r(3)
1845 s(3) = cell%h_inv(3, 1)*r(1) + cell%h_inv(3, 2)*r(2) + cell%h_inv(3, 3)*r(3)
1846 s(1) = s(1) - cell%perd(1)*real(floor(s(1)), dp)
1847 s(2) = s(2) - cell%perd(2)*real(floor(s(2)), dp)
1848 s(3) = s(3) - cell%perd(3)*real(floor(s(3)), dp)
1849 r_pbc(1) = cell%hmat(1, 1)*s(1) + cell%hmat(1, 2)*s(2) + cell%hmat(1, 3)*s(3)
1850 r_pbc(2) = cell%hmat(2, 1)*s(1) + cell%hmat(2, 2)*s(2) + cell%hmat(2, 3)*s(3)
1851 r_pbc(3) = cell%hmat(3, 1)*s(1) + cell%hmat(3, 2)*s(2) + cell%hmat(3, 3)*s(3)
1852 END IF
1853 END SUBROUTINE pbc_0_1
1854
1855! **************************************************************************************************
1856!> \brief Computes the number of send requests from this MPI process to all the other processes.
1857!> Alternatively computes the number of recv requests per MPI process that the given process
1858!> expects.
1859!> \param mepos MPI rank of the given process
1860!> \param nelements_per_proc number of element to send / receive
1861!> \param max_nelements_per_packet maximum number of elements per single MPI request
1862!> \return number of MPI requests
1863! **************************************************************************************************
1864 PURE FUNCTION get_number_of_mpi_sendrecv_requests(mepos, nelements_per_proc, max_nelements_per_packet) RESULT(nrequests)
1865 INTEGER, INTENT(in) :: mepos
1866 INTEGER(kind=int_8), DIMENSION(0:), INTENT(in) :: nelements_per_proc
1867 INTEGER(kind=int_8), INTENT(in) :: max_nelements_per_packet
1868 INTEGER :: nrequests
1869
1870 INTEGER :: iproc
1871
1872 nrequests = 0
1873 DO iproc = lbound(nelements_per_proc, 1), ubound(nelements_per_proc, 1)
1874 ! there is no need to send data to the same MPI process
1875 IF (iproc /= mepos) THEN
1876 nrequests = nrequests + int(nelements_per_proc(iproc)/max_nelements_per_packet)
1877 IF (mod(nelements_per_proc(iproc), max_nelements_per_packet) > 0) THEN
1878 nrequests = nrequests + 1
1879 END IF
1880 END IF
1881 END DO
1882 END FUNCTION get_number_of_mpi_sendrecv_requests
1883
1884! **************************************************************************************************
1885!> \brief Map non-zero matrix elements on to MPI requests.
1886!> \param element_offset offset (index-1) of the first element [out]
1887!> \param nelements_per_request number of element for each request [out]
1888!> \param peer_rank rank of a peering MPI process
1889!> \param tag MPI tag
1890!> \param mepos MPI rank of a given MPI process
1891!> \param nelements_per_proc number of element to send / receive by the current MPI process
1892!> \param max_nelements_per_packet maximum number of elements per single MPI request
1893! **************************************************************************************************
1894 SUBROUTINE assign_nonzero_elements_to_requests(element_offset, nelements_per_request, peer_rank, tag, &
1895 mepos, nelements_per_proc, max_nelements_per_packet)
1896 INTEGER(kind=int_8), DIMENSION(:), INTENT(out) :: element_offset, nelements_per_request
1897 INTEGER, DIMENSION(:), INTENT(out) :: peer_rank, tag
1898 INTEGER, INTENT(in) :: mepos
1899 INTEGER(kind=int_8), DIMENSION(0:), INTENT(in) :: nelements_per_proc
1900 INTEGER(kind=int_8), INTENT(in) :: max_nelements_per_packet
1901
1902 INTEGER :: iproc, irequest, nrequests, &
1903 request_offset
1904 INTEGER(kind=int_8) :: element_offset_tmp, nelements
1905
1906 request_offset = 0
1907 element_offset_tmp = 0
1908 DO iproc = lbound(nelements_per_proc, 1), ubound(nelements_per_proc, 1)
1909 IF (iproc /= mepos) THEN
1910 nrequests = int(nelements_per_proc(iproc)/max_nelements_per_packet)
1911 IF (mod(nelements_per_proc(iproc), max_nelements_per_packet) > 0) nrequests = nrequests + 1
1912 cpassert(nrequests <= max_mpi_rank + 1)
1913 IF (nrequests > 0) THEN
1914 nelements = nelements_per_proc(iproc)/nrequests
1915 IF (nelements_per_proc(iproc) - nelements*nrequests > 0) nelements = nelements + 1
1916 cpassert(nelements <= max_nelements_per_packet)
1917
1918 DO irequest = 1, nrequests
1919 element_offset(request_offset + irequest) = (irequest - 1)*nelements + element_offset_tmp
1920 IF (irequest < nrequests) THEN
1921 nelements_per_request(request_offset + irequest) = nelements
1922 ELSE
1923 nelements_per_request(request_offset + irequest) = nelements_per_proc(iproc) - nelements*(nrequests - 1)
1924 END IF
1925 peer_rank(request_offset + irequest) = iproc
1926 tag(request_offset + irequest) = irequest - 1
1927 END DO
1928 END IF
1929 request_offset = request_offset + nrequests
1930 END IF
1931 element_offset_tmp = element_offset_tmp + nelements_per_proc(iproc)
1932 END DO
1933
1934 IF (debug_this_module) THEN
1935 cpassert(SIZE(element_offset) == request_offset)
1936 cpassert(SIZE(nelements_per_request) == request_offset)
1937 cpassert(SIZE(peer_rank) == request_offset)
1938 cpassert(SIZE(tag) == request_offset)
1939 END IF
1940 END SUBROUTINE assign_nonzero_elements_to_requests
1941
1942END MODULE smeagol_matrix_utils
Handles all functions related to the CELL.
Definition cell_types.F:15
subroutine, public scaled_to_real(r, s, cell)
Transform scaled cell coordinates real coordinates. r=h*s.
Definition cell_types.F:625
subroutine, public real_to_scaled(s, r, cell)
Transform real to scaled cell coordinates. s=h_inv*r.
Definition cell_types.F:595
subroutine, public dbcsr_get_block_p(matrix, row, col, block, found, row_size, col_size)
...
subroutine, public dbcsr_get_info(matrix, nblkrows_total, nblkcols_total, nfullrows_total, nfullcols_total, nblkrows_local, nblkcols_local, nfullrows_local, nfullcols_local, my_prow, my_pcol, local_rows, local_cols, proc_row_dist, proc_col_dist, row_blk_size, col_blk_size, row_blk_offset, col_blk_offset, distribution, name, matrix_type, group)
...
subroutine, public dbcsr_set(matrix, alpha)
...
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public int_8
Definition kinds.F:54
integer, parameter, public dp_size
Definition kinds.F:36
integer, parameter, public dp
Definition kinds.F:34
Interface to the message passing library MPI.
Helper routines to manipulate with matrices.
pure integer function, public get_index_by_cell(cell, cell_to_index)
Helper routine to obtain index of a DBCSR matrix image by its unit cell replica. Can be used with any...
Define the data structure for the particle information.
Define the neighbor list data types and the corresponding functionality.
subroutine, public neighbor_list_iterator_create(iterator_set, nl, search, nthread)
Neighbor list iterator functions.
subroutine, public neighbor_list_iterator_release(iterator_set)
...
subroutine, public get_neighbor_list_set_p(neighbor_list_sets, nlist, symmetric)
Return the components of the first neighbor list set.
integer function, public neighbor_list_iterate(iterator_set, mepos)
...
subroutine, public get_iterator_info(iterator_set, mepos, ikind, jkind, nkind, ilist, nlist, inode, nnode, iatom, jatom, r, cell)
...
types that represent a quickstep subsys
subroutine, public qs_subsys_get(subsys, atomic_kinds, atomic_kind_set, particles, particle_set, local_particles, molecules, molecule_set, molecule_kinds, molecule_kind_set, local_molecules, para_env, colvar_p, shell_particles, core_particles, gci, multipoles, natom, nparticle, ncore, nshell, nkind, atprop, virial, results, cell, cell_ref, use_ref_cell, energy, force, qs_kind_set, cp_subsys, nelectron_total, nelectron_spin)
...
Routines to convert sparse matrices between DBCSR (distributed-blocks compressed sparse rows) and SIE...
subroutine, public siesta_struct_release(siesta_struct)
Release a SIESTA matrix structure.
subroutine, public siesta_struct_create(siesta_struct, matrix_dbcsr_kp, subsys, cell_to_index, sab_nl, para_env, max_ij_cell_image, do_merge, gather_root)
Map non-zero matrix blocks between sparse matrices in DBCSR and SIESTA formats.
subroutine, public convert_distributed_siesta_to_dbcsr(matrix_dbcsr_kp, matrix_siesta, siesta_struct, para_env)
Convert matrix from DBCSR to sparse SIESTA format.
subroutine, public convert_dbcsr_to_distributed_siesta(matrix_siesta, matrix_dbcsr_kp, siesta_struct, para_env)
Convert matrix from DBCSR to sparse SIESTA format.
All kind of helpful little routines.
Definition util.F:14
Type defining parameters related to the simulation cell.
Definition cell_types.F:60
stores all the informations relevant to an mpi environment
Sparsity pattern of replicated SIESTA compressed sparse column (CSC) matrices.