(git:6344619)
Loading...
Searching...
No Matches
cp_fm_redistribution.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 Redistribution of full matrices and distributed arrays.
10! **************************************************************************************************
13 USE cp_fm_types, ONLY: cp_fm_create,&
17 USE kinds, ONLY: dp
22#include "../base/base_uses.f90"
23
24 IMPLICIT NONE
25 PRIVATE
26 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_fm_redistribution'
29
30CONTAINS
31
32! **************************************************************************************************
33!> \brief Add a permuted or restricted four-index tensor stored as a full matrix.
34!> \param fm_out destination; entries outside the mapped region remain unchanged
35!> \param fm_in source; each matrix coordinate encodes two tensor indices
36!> \param beta factor multiplying the source contribution
37!> \param nrow_secidx_in size of the second tensor index in source rows
38!> \param ncol_secidx_in size of the second tensor index in source columns
39!> \param nrow_secidx_out stride of the first tensor index in destination rows
40!> \param ncol_secidx_out stride of the first tensor index in destination columns
41!> \param reordering permutation of the four source indices
42!> \param row_offset zero-based destination row offset
43!> \param col_offset zero-based destination column offset
44!> \param first_index first retained value of each source index (default: one)
45!> \param last_index last retained value of each source index (default: full extent)
46!> \note The index map must be one-to-one and fit within the destination.
47! **************************************************************************************************
48 SUBROUTINE cp_fm_permute(fm_out, fm_in, beta, nrow_secidx_in, ncol_secidx_in, &
49 nrow_secidx_out, ncol_secidx_out, reordering, row_offset, col_offset, &
50 first_index, last_index)
51 TYPE(cp_fm_type), INTENT(INOUT) :: fm_out
52 TYPE(cp_fm_type), INTENT(IN) :: fm_in
53 REAL(kind=dp), INTENT(IN) :: beta
54 INTEGER, INTENT(IN) :: nrow_secidx_in, ncol_secidx_in, &
55 nrow_secidx_out, ncol_secidx_out
56 INTEGER, DIMENSION(4), INTENT(IN) :: reordering
57 INTEGER, INTENT(IN), OPTIONAL :: row_offset, col_offset
58 INTEGER, DIMENSION(4), INTENT(IN), OPTIONAL :: first_index, last_index
59
60 CHARACTER(LEN=*), PARAMETER :: routinen = 'cp_fm_permute'
61
62 INTEGER :: handle, i, idx_col, idx_row, ientry, ii, &
63 iproc, j, jj, my_col_offset, &
64 my_row_offset, ncol_local, nprocs, &
65 nrow_local, pass
66 INTEGER, ALLOCATABLE, DIMENSION(:) :: entry_counter, num_entries_rec, &
67 num_entries_send
68 INTEGER, DIMENSION(4) :: first, indices, last, mapped
69 INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
70 TYPE(matrix_buffer_type) :: buffer_rec, buffer_send
71 TYPE(mp_para_env_type), POINTER :: para_env
72
73 CALL timeset(routinen, handle)
74 para_env => fm_out%matrix_struct%para_env
75 nprocs = para_env%num_pe
76 my_row_offset = 0
77 my_col_offset = 0
78 IF (PRESENT(row_offset)) my_row_offset = row_offset
79 IF (PRESENT(col_offset)) my_col_offset = col_offset
80 first = 1
81 last = [fm_in%matrix_struct%nrow_global/nrow_secidx_in, nrow_secidx_in, &
82 fm_in%matrix_struct%ncol_global/ncol_secidx_in, ncol_secidx_in]
83 IF (PRESENT(first_index)) first = first_index
84 IF (PRESENT(last_index)) last = last_index
85
86 CALL cp_fm_get_info(fm_in, nrow_local=nrow_local, ncol_local=ncol_local, &
87 row_indices=row_indices, col_indices=col_indices)
88 ALLOCATE (num_entries_send(0:nprocs - 1), num_entries_rec(0:nprocs - 1), &
89 entry_counter(0:nprocs - 1))
90 num_entries_send = 0
91
92 ! Count and then pack using the same coordinate map.
93 DO pass = 1, 2
94 IF (pass == 2) THEN
95 CALL para_env%alltoall(num_entries_send, num_entries_rec, 1)
96 CALL allocate_matrix_buffers(num_entries_send, num_entries_rec, 2, buffer_send, buffer_rec)
97 entry_counter(:) = buffer_send%msg_offset(:nprocs - 1)
98 END IF
99
100 DO i = 1, nrow_local
101 indices(1) = (row_indices(i) - 1)/nrow_secidx_in + 1
102 indices(2) = mod(row_indices(i) - 1, nrow_secidx_in) + 1
103 DO j = 1, ncol_local
104 indices(3) = (col_indices(j) - 1)/ncol_secidx_in + 1
105 indices(4) = mod(col_indices(j) - 1, ncol_secidx_in) + 1
106 IF (any(indices < first) .OR. any(indices > last)) cycle
107 mapped = indices - first + 1
108 idx_row = my_row_offset + mapped(reordering(2)) + &
109 (mapped(reordering(1)) - 1)*nrow_secidx_out
110 idx_col = my_col_offset + mapped(reordering(4)) + &
111 (mapped(reordering(3)) - 1)*ncol_secidx_out
112 iproc = fm_out%matrix_struct%context%blacs2mpi( &
113 fm_out%matrix_struct%g2p_row(idx_row), fm_out%matrix_struct%g2p_col(idx_col))
114 IF (pass == 1) THEN
115 num_entries_send(iproc) = num_entries_send(iproc) + 1
116 ELSE
117 entry_counter(iproc) = entry_counter(iproc) + 1
118 ientry = entry_counter(iproc)
119 buffer_send%msg(ientry) = fm_in%local_data(i, j)
120 buffer_send%indx(:, ientry) = [idx_row, idx_col]
121 END IF
122 END DO
123 END DO
124 END DO
125
126 CALL exchange_matrix_buffers(para_env, buffer_rec, buffer_send)
127
128!$OMP PARALLEL DO DEFAULT(NONE) &
129!$OMP SHARED(fm_out, buffer_rec, beta) &
130!$OMP PRIVATE(ientry, ii, jj)
131 DO ientry = 1, SIZE(buffer_rec%msg)
132 ii = fm_out%matrix_struct%g2l_row(buffer_rec%indx(1, ientry))
133 jj = fm_out%matrix_struct%g2l_col(buffer_rec%indx(2, ientry))
134 fm_out%local_data(ii, jj) = fm_out%local_data(ii, jj) + beta*buffer_rec%msg(ientry)
135 END DO
136!$OMP END PARALLEL DO
137 CALL timestop(handle)
138 END SUBROUTINE cp_fm_permute
139
140! **************************************************************************************************
141!> \brief Assemble a full matrix from distributed rectangular array tiles.
142!> \param mat2D local tile; tiles on the FM communicator must not overlap
143!> \param fm_struct structure defining the destination layout and communicator
144!> \param my_start_row first global row of the tile
145!> \param my_end_row last global row of the tile
146!> \param my_start_col first global column of the tile
147!> \param my_end_col last global column of the tile
148!> \param fm_mat destination; entries outside the supplied tiles are zero
149!> \param do_release_mat whether to deallocate the input tile (default: true)
150! **************************************************************************************************
151 SUBROUTINE cp_array_to_fm(mat2D, fm_struct, my_start_row, my_end_row, &
152 my_start_col, my_end_col, fm_mat, do_release_mat)
153 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
154 INTENT(INOUT) :: mat2d
155 TYPE(cp_fm_struct_type), POINTER :: fm_struct
156 INTEGER, INTENT(IN) :: my_start_row, my_end_row, my_start_col, &
157 my_end_col
158 TYPE(cp_fm_type), INTENT(OUT) :: fm_mat
159 LOGICAL, INTENT(IN), OPTIONAL :: do_release_mat
160
161 CHARACTER(LEN=*), PARAMETER :: routinen = 'cp_array_to_fm'
162
163 INTEGER :: handle, i, ientry, j, pass, peer
164 INTEGER, ALLOCATABLE :: cursor(:), recv_count(:), send_count(:)
165 LOGICAL :: release_mat
166 TYPE(matrix_buffer_type) :: buffer_rec, buffer_send
167 TYPE(mp_para_env_type), POINTER :: para_env
168
169 CALL timeset(routinen, handle)
170 para_env => fm_struct%para_env
171 release_mat = .true.
172 IF (PRESENT(do_release_mat)) release_mat = do_release_mat
173 CALL cp_fm_create(fm_mat, fm_struct, name="fm_mat", set_zero=.true.)
174 ALLOCATE (send_count(0:para_env%num_pe - 1), recv_count(0:para_env%num_pe - 1), &
175 cursor(0:para_env%num_pe - 1))
176 send_count = 0
177
178 DO pass = 1, 2
179 IF (pass == 2) THEN
180 CALL para_env%alltoall(send_count, recv_count, 1)
181 CALL allocate_matrix_buffers(send_count, recv_count, 2, buffer_send, buffer_rec)
182 cursor(:) = buffer_send%msg_offset(:para_env%num_pe - 1)
183 END IF
184 DO j = my_start_col, my_end_col
185 DO i = my_start_row, my_end_row
186 peer = fm_struct%context%blacs2mpi(fm_struct%g2p_row(i), fm_struct%g2p_col(j))
187 IF (pass == 1) THEN
188 send_count(peer) = send_count(peer) + 1
189 ELSE
190 cursor(peer) = cursor(peer) + 1
191 ientry = cursor(peer)
192 buffer_send%indx(:, ientry) = [i, j]
193 buffer_send%msg(ientry) = mat2d(i - my_start_row + 1, j - my_start_col + 1)
194 END IF
195 END DO
196 END DO
197 END DO
198 IF (release_mat) DEALLOCATE (mat2d)
199 CALL exchange_matrix_buffers(para_env, buffer_rec, buffer_send)
200 DO ientry = 1, SIZE(buffer_rec%msg)
201 i = fm_struct%g2l_row(buffer_rec%indx(1, ientry))
202 j = fm_struct%g2l_col(buffer_rec%indx(2, ientry))
203 fm_mat%local_data(i, j) = buffer_rec%msg(ientry)
204 END DO
205 CALL timestop(handle)
206 END SUBROUTINE cp_array_to_fm
207
208! **************************************************************************************************
209!> \brief Extract distributed rectangular array tiles from a full matrix.
210!> \param mat2D output tile, allocated here
211!> \param my_start_row first global row requested by this rank
212!> \param my_end_row last global row requested by this rank
213!> \param my_start_col first global column requested by this rank
214!> \param my_end_col last global column requested by this rank
215!> \param fm_mat source, released on return
216! **************************************************************************************************
217 SUBROUTINE cp_fm_to_array(mat2D, my_start_row, my_end_row, my_start_col, my_end_col, fm_mat)
218 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
219 INTENT(OUT) :: mat2d
220 INTEGER, INTENT(IN) :: my_start_row, my_end_row, my_start_col, &
221 my_end_col
222 TYPE(cp_fm_type), INTENT(INOUT) :: fm_mat
223
224 CHARACTER(LEN=*), PARAMETER :: routinen = 'cp_fm_to_array'
225
226 INTEGER :: handle, i, ientry, j, ncols, nrows, peer
227 INTEGER, ALLOCATABLE :: bounds(:, :), recv_count(:), &
228 send_count(:)
229 INTEGER, POINTER :: col_indices(:), row_indices(:)
230 TYPE(matrix_buffer_type) :: buffer_rec, buffer_send
231 TYPE(mp_para_env_type), POINTER :: para_env
232
233 CALL timeset(routinen, handle)
234 CALL cp_fm_get_info(fm_mat, nrow_local=nrows, ncol_local=ncols, row_indices=row_indices, &
235 col_indices=col_indices, para_env=para_env)
236 ALLOCATE (mat2d(my_end_row - my_start_row + 1, my_end_col - my_start_col + 1))
237 mat2d = 0.0_dp
238 ALLOCATE (bounds(4, 0:para_env%num_pe - 1), send_count(0:para_env%num_pe - 1), &
239 recv_count(0:para_env%num_pe - 1))
240 CALL para_env%allgather([my_start_row, my_end_row, my_start_col, my_end_col], bounds)
241
242 ! The ordered FM row/column lists turn each rectangle into local index ranges.
243 DO peer = 0, para_env%num_pe - 1
244 bounds(:, peer) = [count(row_indices(:nrows) < bounds(1, peer)) + 1, &
245 count(row_indices(:nrows) <= bounds(2, peer)), &
246 count(col_indices(:ncols) < bounds(3, peer)) + 1, &
247 count(col_indices(:ncols) <= bounds(4, peer))]
248 send_count(peer) = max(0, bounds(2, peer) - bounds(1, peer) + 1)* &
249 max(0, bounds(4, peer) - bounds(3, peer) + 1)
250 END DO
251 CALL para_env%alltoall(send_count, recv_count, 1)
252 CALL allocate_matrix_buffers(send_count, recv_count, 2, buffer_send, buffer_rec)
253 DO peer = 0, para_env%num_pe - 1
254 IF (send_count(peer) == 0) cycle
255 ientry = buffer_send%msg_offset(peer)
256 DO j = bounds(3, peer), bounds(4, peer)
257 DO i = bounds(1, peer), bounds(2, peer)
258 ientry = ientry + 1
259 buffer_send%indx(:, ientry) = [row_indices(i), col_indices(j)]
260 buffer_send%msg(ientry) = fm_mat%local_data(i, j)
261 END DO
262 END DO
263 END DO
264 CALL exchange_matrix_buffers(para_env, buffer_rec, buffer_send)
265 DO ientry = 1, SIZE(buffer_rec%msg)
266 i = buffer_rec%indx(1, ientry) - my_start_row + 1
267 j = buffer_rec%indx(2, ientry) - my_start_col + 1
268 mat2d(i, j) = buffer_rec%msg(ientry)
269 END DO
270 CALL cp_fm_release(fm_mat)
271 CALL timestop(handle)
272 END SUBROUTINE cp_fm_to_array
273
274! **************************************************************************************************
275!> \brief ...
276!> \param fm_S ...
277!> \param array_S ...
278!> \param weight ...
279!> \param add ...
280! **************************************************************************************************
281 SUBROUTINE cp_fm_to_replicated_array(fm_S, array_S, weight, add)
282
283 TYPE(cp_fm_type), DIMENSION(:) :: fm_s
284 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: array_s
285 REAL(kind=dp), OPTIONAL :: weight
286 LOGICAL, OPTIONAL :: add
287
288 CHARACTER(LEN=*), PARAMETER :: routinen = 'cp_fm_to_replicated_array'
289
290 INTEGER :: handle, i, i_row_local, img, j, &
291 j_col_local, n_basis, ncol_local, &
292 nimages, nrow_local
293 INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
294 LOGICAL :: my_add
295 REAL(kind=dp) :: my_weight
296 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: array_tmp
297
298 CALL timeset(routinen, handle)
299
300 my_weight = 1.0_dp
301 IF (PRESENT(weight)) my_weight = weight
302
303 my_add = .false.
304 IF (PRESENT(add)) my_add = add
305
306 n_basis = SIZE(array_s, 1)
307 nimages = SIZE(array_s, 3)
308
309 ! checks
310 cpassert(SIZE(array_s, 2) == n_basis)
311 cpassert(SIZE(fm_s) == nimages)
312 cpassert(lbound(array_s, 1) == 1)
313 cpassert(lbound(array_s, 2) == 1)
314 cpassert(lbound(array_s, 3) == 1)
315
316 CALL cp_fm_get_info(matrix=fm_s(1), &
317 nrow_local=nrow_local, &
318 ncol_local=ncol_local, &
319 row_indices=row_indices, &
320 col_indices=col_indices)
321
322 IF (.NOT. my_add) array_s(:, :, :) = 0.0_dp
323 ALLOCATE (array_tmp(SIZE(array_s, 1), SIZE(array_s, 2), SIZE(array_s, 3)))
324 array_tmp(:, :, :) = 0.0_dp
325
326 DO img = 1, nimages
327 DO i_row_local = 1, nrow_local
328 i = row_indices(i_row_local)
329 DO j_col_local = 1, ncol_local
330 j = col_indices(j_col_local)
331 array_tmp(i, j, img) = fm_s(img)%local_data(i_row_local, j_col_local)
332 END DO ! j_col_local
333 END DO ! i_row_local
334 END DO ! img
335
336 CALL fm_s(1)%matrix_struct%para_env%sum(array_tmp)
337
338 array_s(:, :, :) = array_s(:, :, :) + my_weight*array_tmp(:, :, :)
339
340 CALL timestop(handle)
341
342 END SUBROUTINE cp_fm_to_replicated_array
343
344! **************************************************************************************************
345!> \brief Redistribute a column-distributed array into an existing full matrix.
346!> \param matrix destination; cleared before filling the received entries
347!> \param array local columns, with global rows and a one-based local column index
348!> \param para_env communicator covering all source and destination ranks
349!> \param first_col first global column held locally
350!> \param last_col last global column held locally
351!> \param nrows number of source rows to copy
352! **************************************************************************************************
353 SUBROUTINE cp_array_columns_to_fm(matrix, array, para_env, first_col, last_col, nrows)
354 TYPE(cp_fm_type), INTENT(IN) :: matrix
355 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
356 INTENT(IN) :: array
357 TYPE(mp_para_env_type), INTENT(IN) :: para_env
358 INTEGER, INTENT(IN) :: first_col, last_col, nrows
359
360 CHARACTER(LEN=*), PARAMETER :: routinen = 'cp_array_columns_to_fm'
361
362 INTEGER :: handle, i, ientry, j, nprocs, pass, peer
363 INTEGER, ALLOCATABLE :: counter(:), recv_count(:), send_count(:)
364 TYPE(matrix_buffer_type) :: buffer_rec, buffer_send
365
366 CALL timeset(routinen, handle)
367 nprocs = para_env%num_pe
368 ALLOCATE (send_count(0:nprocs - 1), recv_count(0:nprocs - 1), counter(0:nprocs - 1))
369 send_count = 0
370 DO pass = 1, 2
371 IF (pass == 2) THEN
372 CALL para_env%alltoall(send_count, recv_count, 1)
373 CALL allocate_matrix_buffers(send_count, recv_count, 2, buffer_send, buffer_rec)
374 counter(:) = buffer_send%msg_offset(:nprocs - 1)
375 END IF
376 DO j = first_col, last_col
377 DO i = 1, nrows
378 peer = matrix%matrix_struct%context%blacs2mpi( &
379 matrix%matrix_struct%g2p_row(i), matrix%matrix_struct%g2p_col(j))
380 IF (pass == 1) THEN
381 send_count(peer) = send_count(peer) + 1
382 ELSE
383 counter(peer) = counter(peer) + 1
384 ientry = counter(peer)
385 buffer_send%msg(ientry) = array(i, j - first_col + 1)
386 buffer_send%indx(:, ientry) = [i, j]
387 END IF
388 END DO
389 END DO
390 END DO
391
392 CALL exchange_matrix_buffers(para_env, buffer_rec, buffer_send)
393 matrix%local_data = 0.0_dp
394 DO ientry = 1, SIZE(buffer_rec%msg)
395 i = matrix%matrix_struct%g2l_row(buffer_rec%indx(1, ientry))
396 j = matrix%matrix_struct%g2l_col(buffer_rec%indx(2, ientry))
397 matrix%local_data(i, j) = buffer_rec%msg(ientry)
398 END DO
399 CALL timestop(handle)
400 END SUBROUTINE cp_array_columns_to_fm
401
402END MODULE cp_fm_redistribution
Redistribution of full matrices and distributed arrays.
subroutine, public cp_fm_permute(fm_out, fm_in, beta, nrow_secidx_in, ncol_secidx_in, nrow_secidx_out, ncol_secidx_out, reordering, row_offset, col_offset, first_index, last_index)
Add a permuted or restricted four-index tensor stored as a full matrix.
subroutine, public cp_array_columns_to_fm(matrix, array, para_env, first_col, last_col, nrows)
Redistribute a column-distributed array into an existing full matrix.
subroutine, public cp_fm_to_array(mat2d, my_start_row, my_end_row, my_start_col, my_end_col, fm_mat)
Extract distributed rectangular array tiles from a full matrix.
subroutine, public cp_array_to_fm(mat2d, fm_struct, my_start_row, my_end_row, my_start_col, my_end_col, fm_mat, do_release_mat)
Assemble a full matrix from distributed rectangular array tiles.
subroutine, public cp_fm_to_replicated_array(fm_s, array_s, weight, add)
...
represent the structure of a full matrix
represent a full matrix distributed on many processors
Definition cp_fm_types.F:15
subroutine, public cp_fm_get_info(matrix, name, nrow_global, ncol_global, nrow_block, ncol_block, nrow_local, ncol_local, row_indices, col_indices, local_data, context, nrow_locals, ncol_locals, matrix_struct, para_env)
returns all kind of information about the full matrix
subroutine, public cp_fm_create(matrix, matrix_struct, name, nrow, ncol, set_zero)
creates a new full matrix with the given structure
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Exchange numerical matrix payloads and integer metadata between MPI ranks.
subroutine, public exchange_matrix_buffers(comm, buffer_rec, buffer_send, do_indx, do_msg)
Synchronously exchange flat matrix buffers with nonempty peers.
subroutine, public allocate_matrix_buffers(send_count, recv_count, n_index, buffer_send, buffer_rec, send_blocks, recv_blocks)
Allocate flat, rank-grouped matrix buffers and their zero-based offsets.
Interface to the message passing library MPI.
keeps the information about the structure of a full matrix
represent a full matrix
stores all the informations relevant to an mpi environment