Octopus
partition_transfer.F90
Go to the documentation of this file.
1!! Copyright (C) 2011-2012 M. Oliveira
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
22 use debug_oct_m
23 use global_oct_m
24 use iihash_oct_m
26 use mpi_oct_m
29
30 implicit none
31
32 private
33 public :: &
39
44 private
45 type(mpi_grp_t) :: mpi_grp
46
47 integer, allocatable :: rdispls(:)
48 integer, allocatable :: sdispls(:)
49 integer, allocatable :: rcounts(:)
50 integer, allocatable :: scounts(:)
52
53
54contains
55
56 ! -----------------------------------------------------------------
71 subroutine partition_transfer_init(this, np, global_index, mpi_grp_in, mpi_grp_out, &
72 part_out, nsend, nrec, order_in, order_out, inverse)
73 type(partition_transfer_t), intent(out) :: this
74 integer, intent(in) :: np
75 integer(int64), intent(in) :: global_index(:)
76 type(mpi_grp_t), target, intent(in) :: mpi_grp_in
77 type(mpi_grp_t), target, intent(in) :: mpi_grp_out
78 integer, intent(in) :: part_out(:)
79 integer, intent(out) :: nsend
80 integer, intent(out) :: nrec
81 integer(int64), allocatable, intent(out) :: order_in(:)
82 integer(int64), allocatable, intent(out) :: order_out(:)
83 logical, optional, intent(in) :: inverse
84
85 logical :: found, inverse_
86 integer :: n12, tmp_partno(2), ipart, opart, ip, pcount, mycolumn, irec, isend, ipos
87 type(iihash_t) :: map_out
88 type(mpi_grp_t), pointer :: grp1, grp2
89 integer, allocatable :: partno_list(:,:), part_map(:,:)
90
91 push_sub_with_profile(partition_transfer_init)
92
93 inverse_ = optional_default(inverse, .false.)
94
95 ! In order to avoid unnecessary communications, all the data
96 ! transfer is going to be made from the point of view of the group
97 ! that has more processes.
98 if (mpi_grp_in%size >= mpi_grp_out%size) then
99 grp1 => mpi_grp_in
100 grp2 => mpi_grp_out
101 else
102 grp1 => mpi_grp_out
103 grp2 => mpi_grp_in
104 end if
105 call mpi_grp_copy(this%mpi_grp, grp1)
106
107 ! The number of partitions in group 1 should be a multiple of the
108 ! number of partitions in group 2.
109 if (mod(grp1%size, grp2%size) /= 0) then
110 message(1) = "Incompatible size of mpi groups in partition_transfer_init"
111 call messages_fatal(1)
112 end if
113 n12 = grp1%size/grp2%size
115 ! We need to know the partition number of all the processes in
116 ! both groups
117 safe_allocate(partno_list(1:2, 1:grp1%size))
118 tmp_partno(1) = grp1%rank + 1
119 tmp_partno(2) = grp2%rank + 1
120 call this%mpi_grp%allgather(tmp_partno(1), 2, mpi_integer, partno_list(1, 1), 2, mpi_integer)
121
122 ! Build partition map. This is a matrix with n12 columns and
123 ! grp2%size lines. Each line contains the partition numbers of the
124 ! processes of group 1 that also store the partition of group 2
125 ! with the same number as the line number. The number of columns
126 ! for each line should be exactly equal to grp1%size/grp2%size in
127 ! all cases and there should be no repeated values.
128 safe_allocate(part_map(1:grp2%size, 1:n12))
129 part_map = 0
130 do ipart = 1, grp2%size
131 pcount = 0
132 do ip = 1, grp1%size
133 if (partno_list(2, ip) == ipart) then
134 pcount = pcount + 1
135
136 if (pcount > n12 .or. any(partno_list(1, ip) == part_map(1:ipart,:))) then
137 message(1) = "Incompatible mpi groups in partition_transfer_init"
139 end if
140 part_map(ipart, pcount) = partno_list(1, ip)
141 if (ip == grp1%rank + 1) mycolumn = pcount
142 end if
143 end do
144 if (pcount /= n12) then
145 message(1) = "Incompatible mpi groups in partition_transfer_init"
146 call messages_fatal(1)
147 end if
148 end do
149
150 ! Build mapping between all the possible receivers and the ouput
151 ! group. This map is a hash table, where the keys are the possible
152 ! receivers and the values are the output partition these
153 ! receivers are responsible for.
154 ! If group 1 is the input group, then all members of group 1 are
155 ! possible receivers.
156 ! If group 1 is the output group, then, in order to avoid
157 ! unnecessary communications, each process will only send data to
158 ! a subset of all the possible receivers. We will choose the
159 ! processes that are on the same column of the partition map than
160 ! the local process. Note that this implies that there are always
161 ! mpi_grp_in%size possible receivers.
162 call iihash_init(map_out)
163 if (.not. inverse_) then
164 do ipart = 1, grp2%size
165 if (mpi_grp_in%size >= mpi_grp_out%size) then
166 do ip = 1, n12
167 call iihash_insert(map_out, part_map(ipart, ip), ipart)
168 end do
169 else
170 call iihash_insert(map_out, part_map(ipart, mycolumn), part_map(ipart, mycolumn))
171 end if
172 end do
173 else
174 do ipart = 1, grp2%size
175 if (mpi_grp_in%size >= mpi_grp_out%size) then
176 do ip = 1, n12
177 call iihash_insert(map_out, part_map(ipart, ip), part_map(ipart, ip))
178 end do
179 else
180 call iihash_insert(map_out, part_map(ipart, mycolumn), ipart)
181 end if
182 end do
183 end if
184
185 if (.not. inverse_) then
186 ! Total number of points to be sent
187 nsend = 0
188 do irec = 1, grp1%size
189 opart = iihash_lookup(map_out, irec, found)
190 if (.not. found) cycle
191 nsend = nsend + count(part_out(1:np) == opart)
192 end do
193 else
194 ! Total number of points to be received
195 nrec = 0
196 do isend = 1, grp1%size
197 opart = iihash_lookup(map_out, isend, found)
198 if (.not. found) cycle
199 nrec = nrec + count(part_out(1:np) == opart)
200 end do
201 end if
202
203 ! List of points to be send
204 safe_allocate(this%sdispls(1:grp1%size))
205 safe_allocate(this%scounts(1:grp1%size))
206 ! Displacements and number of points to be received
207 safe_allocate(this%rdispls(1:grp1%size))
208 safe_allocate(this%rcounts(1:grp1%size))
209
210 if (.not. inverse_) then
211 safe_allocate(order_in(1:max(1,nsend)))
212
213 ipos = 0
214 ! Loop over all possible receivers
215 do irec = 1, grp1%size
216 this%scounts(irec) = 0
217 this%sdispls(irec) = ipos
218
219 opart = iihash_lookup(map_out, irec, found)
220 if (.not. found) cycle
221
222 do ip = 1, np
223 ! Should point ip be sent to partition opart?
224 if (part_out(ip) == opart) then
225 ipos = ipos + 1
226 order_in(ipos) = global_index(ip)
227 this%scounts(irec) = this%scounts(irec) + 1
228 end if
229 end do
230
231 end do
232 else
233 safe_allocate(order_out(1:max(1,nrec)))
234
235 ipos = 0
236 ! Loop over all possible senders
237 do isend = 1, grp1%size
238 this%rcounts(isend) = 0
239 this%rdispls(isend) = ipos
240
241 opart = iihash_lookup(map_out, isend, found)
242 if (.not. found) cycle
243
244 do ip = 1, np
245 ! Should point ip be received from partition opart?
246 if (part_out(ip) == opart) then
247 ipos = ipos + 1
248 order_out(ipos) = global_index(ip)
249 this%rcounts(isend) = this%rcounts(isend) + 1
250 end if
251 end do
252
253 end do
254 end if
255
256 safe_deallocate_a(part_map)
257 safe_deallocate_a(partno_list)
258 call iihash_end(map_out)
259
260 if (.not. inverse_) then
261 ! assemble the receive information
262 call this%mpi_grp%alltoall(this%scounts, 1, mpi_integer, &
263 this%rcounts, 1, mpi_integer)
264 nrec = 0
265 do isend = 1, grp1%size
266 this%rdispls(isend) = nrec
267 nrec = nrec + this%rcounts(isend)
268 end do
269 else
270 ! assemble the send information
271 call this%mpi_grp%alltoall(this%rcounts, 1, mpi_integer, &
272 this%scounts, 1, mpi_integer)
273 nsend = 0
274 do irec = 1, grp1%size
275 this%sdispls(irec) = nsend
276 nsend = nsend + this%scounts(irec)
277 end do
278 end if
279
280 if (.not. inverse_) then
281 ! Ordering of the points at output
282 safe_allocate(order_out(1:max(1,nrec)))
283 call this%mpi_grp%alltoallv(order_in, this%scounts, this%sdispls, mpi_integer8, &
284 order_out, this%rcounts, this%rdispls, mpi_integer8)
285 else
286 ! Ordering of the points at input
287 safe_allocate(order_in(1:max(1,nsend)))
288 call this%mpi_grp%alltoallv(order_out, this%rcounts, this%rdispls, mpi_integer8, &
289 order_in, this%scounts, this%sdispls, mpi_integer8)
290 end if
291
292 pop_sub_with_profile(partition_transfer_init)
293 end subroutine partition_transfer_init
294
295 ! -----------------------------------------------------------------
296 subroutine partition_transfer_end(this)
297 type(partition_transfer_t), intent(inout) :: this
298
299 push_sub(partition_transfer_end)
300
301 safe_deallocate_a(this%rdispls)
302 safe_deallocate_a(this%sdispls)
303 safe_deallocate_a(this%rcounts)
304 safe_deallocate_a(this%scounts)
305
307 end subroutine partition_transfer_end
308
309#include "undef.F90"
310#include "real.F90"
311#include "partition_transfer_inc.F90"
312
313#include "undef.F90"
314#include "complex.F90"
315#include "partition_transfer_inc.F90"
316
318
319!! Local Variables:
320!! mode: f90
321!! coding: utf-8
322!! End:
This module implements a simple hash table for non-negative integer keys and integer values.
Definition: iihash.F90:125
subroutine, public iihash_end(h)
Free a hash table.
Definition: iihash.F90:184
subroutine, public iihash_insert(h, key, val)
Insert a (key, val) pair into the hash table h.
Definition: iihash.F90:206
integer function, public iihash_lookup(h, key, found)
Look up a value in the hash table h. If found is present, it indicates if key could be found in the t...
Definition: iihash.F90:231
subroutine, public iihash_init(h)
Initialize a hash table h.
Definition: iihash.F90:161
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:420
subroutine mpi_grp_copy(mpi_grp_out, mpi_grp_in)
Definition: mpi.F90:403
subroutine, public partition_transfer_init(this, np, global_index, mpi_grp_in, mpi_grp_out, part_out, nsend, nrec, order_in, order_out, inverse)
initialize the partition transfer object
subroutine, public partition_transfer_end(this)
subroutine, public zpartition_transfer(this, f_in, f_out)
subroutine, public dpartition_transfer(this, f_in, f_out)
The partition transfer object ensures that during a mesh transfer points are associated to the correc...