(git:6344619)
Loading...
Searching...
No Matches
dbt_reshape_ops.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 reshape / redistribute tensors
10!> \author Patrick Seewald
11! **************************************************************************************************
13
14
15 USE omp_lib, ONLY: omp_get_num_threads, omp_get_thread_num
16 USE dbt_block, ONLY: &
19 USE dbt_types, ONLY: dbt_blk_sizes, &
20 dbt_type, &
24 USE kinds, ONLY: dp
27 USE util, ONLY: get_limit
28
29#include "../base/base_uses.f90"
30
31 IMPLICIT NONE
32 PRIVATE
33 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'dbt_reshape_ops'
34
36
37 ! Communication buffers persist across calls such that pages are faulted in once rather than per
38 ! call; per block, the index holds the data offset (relative to the peer's data) and nd-index.
39 TYPE(matrix_buffer_type), SAVE :: buffer_send, buffer_recv
40
41CONTAINS
42
43! **************************************************************************************************
44!> \brief copy data (involves reshape)
45!> tensor_out = tensor_out + tensor_in move_data memory optimization:
46!> transfer data from tensor_in to tensor_out s.t. tensor_in is empty on return
47!> \param bounds crop tensor data while copying (as dbt_crop): start and end index per dimension
48!> \author Ole Schuett
49! **************************************************************************************************
50 SUBROUTINE dbt_reshape(tensor_in, tensor_out, summation, move_data, bounds)
51
52 TYPE(dbt_type), INTENT(INOUT) :: tensor_in, tensor_out
53 LOGICAL, INTENT(IN), OPTIONAL :: summation
54 LOGICAL, INTENT(IN), OPTIONAL :: move_data
55 INTEGER, DIMENSION(2, ndims_tensor(tensor_in)), INTENT(IN), OPTIONAL :: bounds
56
57 CHARACTER(LEN=*), PARAMETER :: routinen = 'dbt_reshape'
58
59 INTEGER :: iproc, numnodes, nindex, &
60 handle, handle2, iblk, offset, ndata
61 INTEGER, DIMENSION(2) :: block_range
62 TYPE(dbt_iterator_type) :: iter
63 CHARACTER(LEN=32) :: name_pack, name_unpack
64 INTEGER, DIMENSION(ndims_tensor(tensor_in)) :: blk_size, blk_offset, ind_nd
65 INTEGER, DIMENSION(2, ndims_tensor(tensor_in)) :: bounds_prv
66 LOGICAL :: found, summation_prv, move_prv, do_bounds, overlap, inside
67
68 INTEGER, ALLOCATABLE, DIMENSION(:) :: nblks_send_total, ndata_send_total, &
69 nblks_recv_total, ndata_recv_total, &
70 nblks_send_mythread, ndata_send_mythread
71 TYPE(mp_comm_type) :: mp_comm
72
73 CALL timeset(routinen, handle)
74
75 IF (PRESENT(summation)) THEN
76 summation_prv = summation
77 ELSE
78 summation_prv = .false.
79 END IF
80
81 IF (PRESENT(move_data)) THEN
82 move_prv = move_data
83 ELSE
84 move_prv = .false.
85 END IF
86
87 do_bounds = PRESENT(bounds)
88 IF (do_bounds) THEN
89 bounds_prv(:, :) = bounds(:, :)
90 ELSE
91 bounds_prv(:, :) = 0
92 END IF
93
94 cpassert(tensor_out%valid)
95
96 IF (.NOT. summation_prv) THEN
97 CALL timeset(routinen//"_clear", handle2)
98 CALL dbt_clear(tensor_out)
99 CALL timestop(handle2)
100 END IF
101
102 mp_comm = tensor_in%pgrid%mp_comm_2d
103 numnodes = mp_comm%num_pe
104 nindex = ndims_tensor(tensor_in) + 1
105 ! separate timers for blocks that need a reshape (index order differs from matrix layout)
106 name_pack = routinen//"_pack"
107 IF (permuted(tensor_in)) name_pack = routinen//"_pack_perm"
108 name_unpack = routinen//"_unpack"
109 IF (permuted(tensor_out)) name_unpack = routinen//"_unpack_perm"
110 ALLOCATE (nblks_send_total(0:numnodes - 1), ndata_send_total(0:numnodes - 1), source=0)
111 ALLOCATE (nblks_recv_total(0:numnodes - 1), ndata_recv_total(0:numnodes - 1), source=0)
112
113!$OMP PARALLEL DEFAULT(NONE) &
114!$OMP SHARED(tensor_in,tensor_out,summation,mp_comm,numnodes,nindex,name_pack,name_unpack) &
115!$OMP SHARED(do_bounds,bounds_prv,buffer_send,buffer_recv) &
116!$OMP SHARED(nblks_send_total,ndata_send_total,nblks_recv_total,ndata_recv_total) &
117!$OMP PRIVATE(nblks_send_mythread,ndata_send_mythread) &
118!$OMP PRIVATE(iter,ind_nd,blk_size,blk_offset,found,overlap,inside,iproc,handle2) &
119!$OMP PRIVATE(block_range,offset,ndata,iblk)
120 CALL timeset(routinen//"_count", handle2)
121 ALLOCATE (nblks_send_mythread(0:numnodes - 1), ndata_send_mythread(0:numnodes - 1), source=0)
122
123 CALL dbt_iterator_start(iter, tensor_in)
124 DO WHILE (dbt_iterator_blocks_left(iter))
125 CALL dbt_iterator_next_block(iter, ind_nd, blk_size=blk_size, blk_offset=blk_offset)
126 IF (do_bounds) THEN
127 CALL block_bounds(bounds_prv, blk_offset, blk_size, overlap, inside)
128 IF (.NOT. overlap) cycle
129 END IF
130 CALL dbt_get_stored_coordinates(tensor_out, ind_nd, iproc)
131 nblks_send_mythread(iproc) = nblks_send_mythread(iproc) + 1
132 ndata_send_mythread(iproc) = ndata_send_mythread(iproc) + product(blk_size)
133 END DO
134 CALL dbt_iterator_stop(iter)
135!$OMP CRITICAL(omp_dbt_reshape)
136 nblks_send_total(:) = nblks_send_total(:) + nblks_send_mythread(:)
137 ndata_send_total(:) = ndata_send_total(:) + ndata_send_mythread(:)
138 nblks_send_mythread(:) = nblks_send_total(:) ! current totals indicate slot for this thread
139 ndata_send_mythread(:) = ndata_send_total(:)
140!$OMP END CRITICAL(omp_dbt_reshape)
141!$OMP BARRIER
142
143!$OMP MASTER
144 CALL mp_comm%alltoall(nblks_send_total, nblks_recv_total, 1)
145 CALL mp_comm%alltoall(ndata_send_total, ndata_recv_total, 1)
146!$OMP END MASTER
147!$OMP BARRIER
148 CALL timestop(handle2)
149
150 CALL timeset(trim(name_pack), handle2)
151!$OMP MASTER
152 CALL grow_buffer(buffer_send, ndata_send_total, nblks_send_total, nindex)
153 CALL grow_buffer(buffer_recv, ndata_recv_total, nblks_recv_total, nindex)
154!$OMP END MASTER
155!$OMP BARRIER
156
157 CALL dbt_iterator_start(iter, tensor_in)
158 DO WHILE (dbt_iterator_blocks_left(iter))
159 CALL dbt_iterator_next_block(iter, ind_nd, blk_size=blk_size, blk_offset=blk_offset)
160 inside = .true.
161 IF (do_bounds) THEN
162 CALL block_bounds(bounds_prv, blk_offset, blk_size, overlap, inside)
163 IF (.NOT. overlap) cycle
164 END IF
165 CALL dbt_get_stored_coordinates(tensor_out, ind_nd, iproc)
166 ! insert block data
167 ndata = product(blk_size)
168 ndata_send_mythread(iproc) = ndata_send_mythread(iproc) - ndata
169 offset = buffer_send%msg_offset(iproc) + ndata_send_mythread(iproc)
170 CALL dbt_get_block_data(tensor_in, ind_nd, blk_size, buffer_send%msg(offset + 1:offset + ndata), found)
171 cpassert(found)
172 IF (.NOT. inside) CALL block_crop(bounds_prv, blk_offset, blk_size, buffer_send%msg(offset + 1:offset + ndata))
173 ! insert block index
174 nblks_send_mythread(iproc) = nblks_send_mythread(iproc) - 1
175 iblk = buffer_send%indx_offset(iproc) + nblks_send_mythread(iproc) + 1
176 buffer_send%indx(2:, iblk) = ind_nd(:)
177 ! Payload offsets in the metadata are relative to the peer's segment.
178 buffer_send%indx(1, iblk) = ndata_send_mythread(iproc)
179 END DO
180 CALL dbt_iterator_stop(iter)
181 DEALLOCATE (nblks_send_mythread, ndata_send_mythread)
182!$OMP BARRIER
183 CALL timestop(handle2)
184
185!$OMP MASTER
186 IF (numnodes > 1) THEN
187 CALL exchange_matrix_buffers(mp_comm, buffer_recv, buffer_send)
188 ELSE
189 ! A single rank receives what it sends: the buffers trade places.
190 CALL swap_buffers(buffer_send, buffer_recv)
191 END IF
192!$OMP END MASTER
193!$OMP BARRIER
194
195 CALL timeset(routinen//"_reserve", handle2)
196 block_range = get_limit(buffer_recv%indx_offset(numnodes), omp_get_num_threads(), omp_get_thread_num())
197 CALL dbt_reserve_blocks(tensor_out, transpose(buffer_recv%indx(2:, block_range(1):block_range(2))))
198!$OMP BARRIER
199 CALL timestop(handle2)
200
201 CALL timeset(trim(name_unpack), handle2)
202 DO iproc = 0, numnodes - 1
203!$OMP DO
204 DO iblk = buffer_recv%indx_offset(iproc) + 1, buffer_recv%indx_offset(iproc + 1)
205 ind_nd(:) = buffer_recv%indx(2:, iblk)
206 CALL dbt_blk_sizes(tensor_out, ind_nd, blk_size)
207 offset = buffer_recv%msg_offset(iproc) + buffer_recv%indx(1, iblk)
208 ndata = product(blk_size)
209 CALL dbt_put_block_data(tensor_out, ind_nd, blk_size, buffer_recv%msg(offset + 1:offset + ndata), &
210 summation=summation)
211 END DO
212!$OMP END DO
213 END DO
214 CALL timestop(handle2)
215!$OMP END PARALLEL
216
217 DEALLOCATE (nblks_recv_total, ndata_recv_total)
218 DEALLOCATE (nblks_send_total, ndata_send_total)
219
220 IF (move_prv) THEN
221 CALL timeset(routinen//"_clear", handle2)
222 CALL dbt_clear(tensor_in)
223 CALL timestop(handle2)
224 END IF
225
226 CALL timestop(handle)
227 END SUBROUTINE dbt_reshape
228
229! **************************************************************************************************
230!> \brief Release the communication buffers that persist across calls of dbt_reshape.
231!> \author Hans Pabst
232! **************************************************************************************************
234 CALL release_buffer(buffer_send)
235 CALL release_buffer(buffer_recv)
236 END SUBROUTINE dbt_reshape_release
237
238! **************************************************************************************************
239!> \brief Whether the tensor's index order differs from its matrix layout (blocks need a reshape).
240!> \author Hans Pabst
241! **************************************************************************************************
242 FUNCTION permuted(tensor)
243 TYPE(dbt_type), INTENT(IN) :: tensor
244 LOGICAL :: permuted
245
246 INTEGER :: i
247
248 associate(map1_2d => tensor%nd_index_blk%map1_2d, map2_2d => tensor%nd_index_blk%map2_2d)
249 permuted = .NOT. all([map1_2d, map2_2d] == [(i, i=1, SIZE(map1_2d) + SIZE(map2_2d))])
250 END associate
251 END FUNCTION permuted
252
253! **************************************************************************************************
254!> \brief Whether a block (element offset and size per dimension) overlaps the bounds, and whether
255!> it lies entirely inside them.
256!> \author Hans Pabst
257! **************************************************************************************************
258 PURE SUBROUTINE block_bounds(bounds, blk_offset, blk_size, overlap, inside)
259 INTEGER, DIMENSION(:, :), INTENT(IN) :: bounds
260 INTEGER, DIMENSION(:), INTENT(IN) :: blk_offset, blk_size
261 LOGICAL, INTENT(OUT) :: overlap, inside
262
263 overlap = all(blk_offset <= bounds(2, :) .AND. bounds(1, :) <= blk_offset + blk_size - 1)
264 inside = all(bounds(1, :) <= blk_offset .AND. blk_offset + blk_size - 1 <= bounds(2, :))
265 END SUBROUTINE block_bounds
266
267! **************************************************************************************************
268!> \brief Zero the elements of a block (column-major data) outside the bounds (as dbt_crop).
269!> \author Hans Pabst
270! **************************************************************************************************
271 PURE SUBROUTINE block_crop(bounds, blk_offset, blk_size, data)
272 INTEGER, DIMENSION(:, :), INTENT(IN) :: bounds
273 INTEGER, DIMENSION(:), INTENT(IN) :: blk_offset, blk_size
274 REAL(dp), DIMENSION(:), INTENT(INOUT) :: data
275
276 INTEGER :: d, i
277 INTEGER, DIMENSION(SIZE(blk_size)) :: hi, idx, lo
278
279 lo(:) = max(bounds(1, :) - blk_offset + 1, 1)
280 hi(:) = min(bounds(2, :) - blk_offset + 1, blk_size)
281 idx(:) = 1
282 DO i = 1, SIZE(data)
283 IF (any(idx < lo .OR. hi < idx)) data(i) = 0.0_dp
284 DO d = 1, SIZE(idx) ! next column-major index
285 idx(d) = idx(d) + 1
286 IF (idx(d) <= blk_size(d)) EXIT
287 idx(d) = 1
288 END DO
289 END DO
290 END SUBROUTINE block_crop
291
292! **************************************************************************************************
293!> \brief Lay out a persistent buffer for the given per-peer counts (as allocate_matrix_buffers),
294!> growing its arrays only when they are too small.
295!> \author Hans Pabst
296! **************************************************************************************************
297 SUBROUTINE grow_buffer(buffer, ndata, nblks, nindex)
298 TYPE(matrix_buffer_type), INTENT(INOUT) :: buffer
299 INTEGER, DIMENSION(0:), INTENT(IN) :: ndata, nblks
300 INTEGER, INTENT(IN) :: nindex
301
302 INTEGER :: iproc, nprocs
303
304 nprocs = SIZE(ndata)
305 IF (ALLOCATED(buffer%msg_offset)) THEN
306 IF (SIZE(buffer%msg_offset) /= nprocs + 1) DEALLOCATE (buffer%msg_offset, buffer%indx_offset)
307 END IF
308 IF (.NOT. ALLOCATED(buffer%msg_offset)) ALLOCATE (buffer%msg_offset(0:nprocs), buffer%indx_offset(0:nprocs))
309 buffer%msg_offset(0) = 0
310 buffer%indx_offset(0) = 0
311 DO iproc = 0, nprocs - 1
312 buffer%msg_offset(iproc + 1) = buffer%msg_offset(iproc) + ndata(iproc)
313 buffer%indx_offset(iproc + 1) = buffer%indx_offset(iproc) + nblks(iproc)
314 END DO
315
316 IF (ALLOCATED(buffer%msg)) THEN
317 IF (SIZE(buffer%msg) < buffer%msg_offset(nprocs)) DEALLOCATE (buffer%msg)
318 END IF
319 IF (.NOT. ALLOCATED(buffer%msg)) THEN
320 ALLOCATE (buffer%msg(max(buffer%msg_offset(nprocs) + buffer%msg_offset(nprocs)/4, 1)))
321 END IF
322 ! records are exchanged as whole columns: the leading dimension must match
323 IF (ALLOCATED(buffer%indx)) THEN
324 IF (SIZE(buffer%indx, 1) /= nindex .OR. SIZE(buffer%indx, 2) < buffer%indx_offset(nprocs)) THEN
325 DEALLOCATE (buffer%indx)
326 END IF
327 END IF
328 IF (.NOT. ALLOCATED(buffer%indx)) THEN
329 ALLOCATE (buffer%indx(nindex, max(buffer%indx_offset(nprocs) + buffer%indx_offset(nprocs)/4, 1)))
330 END IF
331 END SUBROUTINE grow_buffer
332
333! **************************************************************************************************
334!> \brief Exchange the contents of two buffers without copying.
335!> \author Hans Pabst
336! **************************************************************************************************
337 SUBROUTINE swap_buffers(a, b)
338 TYPE(matrix_buffer_type), INTENT(INOUT) :: a, b
339
340 TYPE(matrix_buffer_type) :: t
341
342 CALL move_alloc(a%msg, t%msg)
343 CALL move_alloc(b%msg, a%msg)
344 CALL move_alloc(t%msg, b%msg)
345 CALL move_alloc(a%indx, t%indx)
346 CALL move_alloc(b%indx, a%indx)
347 CALL move_alloc(t%indx, b%indx)
348 CALL move_alloc(a%msg_offset, t%msg_offset)
349 CALL move_alloc(b%msg_offset, a%msg_offset)
350 CALL move_alloc(t%msg_offset, b%msg_offset)
351 CALL move_alloc(a%indx_offset, t%indx_offset)
352 CALL move_alloc(b%indx_offset, a%indx_offset)
353 CALL move_alloc(t%indx_offset, b%indx_offset)
354 END SUBROUTINE swap_buffers
355
356! **************************************************************************************************
357!> \brief Deallocate the arrays of a buffer.
358!> \author Hans Pabst
359! **************************************************************************************************
360 SUBROUTINE release_buffer(buffer)
361 TYPE(matrix_buffer_type), INTENT(INOUT) :: buffer
362
363 IF (ALLOCATED(buffer%msg)) DEALLOCATE (buffer%msg)
364 IF (ALLOCATED(buffer%indx)) DEALLOCATE (buffer%indx)
365 IF (ALLOCATED(buffer%msg_offset)) DEALLOCATE (buffer%msg_offset)
366 IF (ALLOCATED(buffer%indx_offset)) DEALLOCATE (buffer%indx_offset)
367 END SUBROUTINE release_buffer
368
369END MODULE dbt_reshape_ops
static GRID_HOST_DEVICE int idx(const orbital a)
Return coset index of given orbital angular momentum.
struct tensor_ tensor
Methods to operate on n-dimensional tensor blocks.
Definition dbt_block.F:12
logical function, public dbt_iterator_blocks_left(iterator)
Generalization of block_iterator_blocks_left for tensors.
Definition dbt_block.F:200
subroutine, public dbt_get_block_data(tensor, ind, sizes, data, found)
Get a block into the given contiguous data (arbitrary tensor rank), without allocating any intermedia...
Definition dbt_block.F:520
subroutine, public dbt_iterator_stop(iterator)
Generalization of block_iterator_stop for tensors.
Definition dbt_block.F:137
subroutine, public dbt_iterator_start(iterator, tensor)
Generalization of block_iterator_start for tensors.
Definition dbt_block.F:124
subroutine, public dbt_put_block_data(tensor, ind, sizes, data, summation)
Put a block from the given contiguous data (arbitrary tensor rank), without allocating any intermedia...
Definition dbt_block.F:546
subroutine, public dbt_iterator_next_block(iterator, ind_nd, blk_size, blk_offset)
iterate over nd blocks of an nd rank tensor, index only (blocks must be retrieved by calling dbt_get_...
Definition dbt_block.F:164
Routines to reshape / redistribute tensors.
subroutine, public dbt_reshape_release()
Release the communication buffers that persist across calls of dbt_reshape.
subroutine, public dbt_reshape(tensor_in, tensor_out, summation, move_data, bounds)
copy data (involves reshape) tensor_out = tensor_out + tensor_in move_data memory optimization: trans...
DBT tensor framework for block-sparse tensor contraction: Types and create/destroy routines.
Definition dbt_types.F:12
subroutine, public dbt_blk_sizes(tensor, ind, blk_size)
Size of tensor block.
Definition dbt_types.F:1468
pure integer function, public ndims_tensor(tensor)
tensor rank
Definition dbt_types.F:1216
subroutine, public dbt_clear(tensor)
Clear tensor (s.t. it does not contain any blocks).
Definition dbt_types.F:1768
subroutine, public dbt_get_stored_coordinates(tensor, ind_nd, processor)
Generalization of block_get_stored_coordinates for tensors.
Definition dbt_types.F:1499
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.
Interface to the message passing library MPI.
All kind of helpful little routines.
Definition util.F:14
pure integer function, dimension(2), public get_limit(m, n, me)
divide m entries into n parts, return size of part me
Definition util.F:333