Octopus
partition.F90
Go to the documentation of this file.
1!! Copyright (C) 2013 M. Oliveira
2!! Copyright (C) 2021 S. Ohlmann
3!!
4!! This program is free software; you can redistribute it and/or modify
5!! it under the terms of the GNU General Public License as published by
6!! the Free Software Foundation; either version 2, or (at your option)
7!! any later version.
8!!
9!! This program is distributed in the hope that it will be useful,
10!! but WITHOUT ANY WARRANTY; without even the implied warranty of
11!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12!! GNU General Public License for more details.
13!!
14!! You should have received a copy of the GNU General Public License
15!! along with this program; if not, write to the Free Software
16!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17!! 02110-1301, USA.
18!!
19
20#include "global.h"
21
22module partition_oct_m
23 use debug_oct_m
24 use global_oct_m
25 use io_oct_m
28 use mpi_oct_m
31
32 implicit none
33
34 private
35
36 public :: &
50
51
71 !
72 type partition_t
73 private
74
75 ! The following components are the same for all processes:
76 type(mpi_grp_t) :: mpi_grp
77 integer(int64) :: np_global
78 integer :: npart = 1
79 integer, allocatable :: np_local_vec(:)
80 integer(int64), allocatable :: istart_vec(:)
81
82 ! The following components are process-dependent:
83 integer :: partno
84 integer :: np_local
85 integer(int64) :: istart
86 integer, allocatable :: part(:)
87 end type partition_t
88
92
93
94contains
95
96 !---------------------------------------------------------
98 !
99 subroutine partition_init(partition, np_global, mpi_grp)
100 type(partition_t), intent(out) :: partition
101 integer(int64), intent(in) :: np_global
102 type(mpi_grp_t), intent(in) :: mpi_grp
103
104 integer :: ipart
105 integer(int64) :: iend
106
107 push_sub(partition_init)
108
109 !Global variables
110 partition%mpi_grp = mpi_grp
111 partition%np_global = np_global
112 partition%npart = mpi_grp%size
113 partition%partno = mpi_grp%rank + 1
114
115 safe_allocate(partition%np_local_vec(1:partition%npart))
116 safe_allocate(partition%istart_vec(1:partition%npart))
118 ! use block data decomposition
119 do ipart = 1, partition%npart
120 ! according to Fortran 2008 standard, Sect. 7.1.9.3, point 4, the kind parameter of an
121 ! operation is the one with a larger decimal range, i.e., everything is promoted to i8
122 partition%istart_vec(ipart) = (ipart-1) * np_global/partition%npart + 1
123 iend = ipart * np_global/partition%npart
124 partition%np_local_vec(ipart) = i8_to_i4(iend - partition%istart_vec(ipart) + 1)
125 end do
126 partition%istart = partition%istart_vec(partition%partno)
127 partition%np_local = partition%np_local_vec(partition%partno)
128
129 !Allocate memory for the partition
130 safe_allocate(partition%part(1:partition%np_local))
131
132 pop_sub(partition_init)
133 end subroutine partition_init
134
135 ! ---------------------------------------------------------
136 subroutine partition_end(partition)
137 type(partition_t), intent(inout) :: partition
138
139 push_sub(partition_end)
140
141 safe_deallocate_a(partition%part)
142 safe_deallocate_a(partition%np_local_vec)
143 safe_deallocate_a(partition%istart_vec)
144
145 pop_sub(partition_end)
146 end subroutine partition_end
147
148 ! ---------------------------------------------------------
149 subroutine partition_set(partition, part)
150 type(partition_t), intent(inout) :: partition
151 integer, intent(in) :: part(:)
152
153 push_sub(partition_set)
154
155 partition%part(1:partition%np_local) = part(1:partition%np_local)
156
157 pop_sub(partition_set)
158 end subroutine partition_set
159
160 ! ---------------------------------------------------------
165 !
166 subroutine partition_dump(partition, dir, filename, ierr)
167 type(partition_t), intent(in) :: partition
168 character(len=*), intent(in) :: dir
169 character(len=*), intent(in) :: filename
170 integer, intent(out) :: ierr
172 integer :: err
173 character(len=MAX_PATH_LEN) :: full_filename
176
177 ierr = 0
178 full_filename = trim(dir)//'/'//trim(filename)
180 ! Write the header (root only) and wait
181 if (partition%mpi_grp%is_root()) then
182 call iwrite_header(full_filename, partition%np_global, err)
183 if (err /= 0) ierr = ierr + 1
184 end if
185 call partition%mpi_grp%bcast(ierr, 1, mpi_integer, 0)
186
187 assert(all(partition%part(:) > 0))
188
189 ! Each process writes a portion of the partition
190 if (ierr == 0) then
191 call mpi_debug_in(partition%mpi_grp%comm, c_mpi_file_write)
192 ! Only one rank per partition group should write the partition restart information
193 ! Otherwise, more than once is trying to be written data
194 ! TODO(Martin): this "one writer per replicated partition copy" selection uses mpi_world
195 ! coordinates; under per-system sub-communicators it must use the grp the partition was carved
196 ! from (needs a parent grp stored on partition_t).
197 ! This need to be done when parallising over ensembles (see #1494).
198 if (mod(mpi_world%rank, mpi_world%size/partition%mpi_grp%size) == 0) then
199 call io_binary_write_parallel(full_filename, partition%mpi_grp%comm, partition%istart, &
200 partition%np_local, partition%part, err)
201 call mpi_debug_out(partition%mpi_grp%comm, c_mpi_file_write)
202 if (err /= 0) ierr = ierr + 2
203 end if
204 end if
205
206 pop_sub(partition_dump)
207 end subroutine partition_dump
208
209 ! ---------------------------------------------------------
214 !
215 subroutine partition_load(partition, dir, filename, ierr)
216 use iso_c_binding, only: c_sizeof
217 type(partition_t), intent(inout) :: partition
218 character(len=*), intent(in) :: dir
219 character(len=*), intent(in) :: filename
220 integer, intent(out) :: ierr
221
222 integer :: err
223 integer(int64) :: np, file_size
224 integer, allocatable :: scounts(:)
225 integer(int64), allocatable :: sdispls(:)
226 character(len=MAX_PATH_LEN) :: full_filename
227
228 push_sub(partition_load)
229
230 ierr = 0
231 full_filename = trim(dir)//'/'//trim(filename)
232
233 ! This is a writing to avoid an optimization of gfortran with -O3
234 write(message(1),'(a,i8)') "Info: number of points in the partition (in root process) =", size(partition%part)
235 call messages_info(1)
236
237 ! Check if the file exists and has the proper size (only world root)
238 ! TODO(Martin): root-read + bcast is over mpi_world; under per-system sub-communicators this
239 ! should use partition%mpi_grp so each system reads its own restart. This should be changed
240 ! as part of issue #1494 (parallelisation over ensembles)
241 if (mpi_world%is_root()) then
242 call io_binary_get_info(full_filename, np, file_size, err)
243 assert(np == partition%np_global)
244 end if
245
246 ! All nodes need to know the result
247 call mpi_world%bcast(err, 1, mpi_integer, 0)
248 call mpi_world%bcast(file_size, 1, mpi_integer8, 0)
249
250 if (err /= 0) then
251 ierr = ierr + 1
252 pop_sub(partition_load)
253 return
254 end if
255 ! The size of the file is not as big as np_global
256 if (file_size - 64 /= partition%np_global * c_sizeof(int(0))) then
257 ierr = ierr + 2
258 pop_sub(partition_load)
259 return
260 end if
262 ! Calculate displacements for reading
263 safe_allocate(scounts(1:partition%npart))
264 safe_allocate(sdispls(1:partition%npart))
265
266 scounts(:) = partition%np_local_vec(:)
267 sdispls(:) = partition%istart_vec(:) - 1
268
269 assert(sum(scounts(:)) == partition%np_global)
270
271#ifdef HAVE_MPI
272 call mpi_debug_in(partition%mpi_grp%comm, c_mpi_file_read)
273 call io_binary_read_parallel(full_filename, partition%mpi_grp%comm, partition%istart, &
274 partition%np_local, partition%part, err)
275 call mpi_debug_out(partition%mpi_grp%comm, c_mpi_file_read)
276 if (err /= 0) ierr = ierr + 4
277#endif
278
279 if (any(partition%part(:) <= 0)) then
280 write(message(1),'(a)') 'Internal error: some elements of partition are <= 0.'
281 write(message(2),*) 'filename = ', full_filename
282 write(message(3),*) 'scounts = ', scounts(:)
283 write(message(4),*) 'sdispls = ', sdispls(:)
284 call messages_fatal(6)
285 end if
286
287 safe_deallocate_a(scounts)
288 safe_deallocate_a(sdispls)
289
290 pop_sub(partition_load)
291 end subroutine partition_load
292
293 ! ---------------------------------------------------------
294 subroutine partition_get_local_size(partition, istart, np_local)
295 type(partition_t), intent(in) :: partition
296 integer(int64), intent(out) :: istart
297 integer, intent(out) :: np_local
298
300
301 istart = partition%istart
302 np_local = partition%np_local
303
305 end subroutine partition_get_local_size
306
307 ! ---------------------------------------------------------
310 subroutine partition_get_global(partition, part_global, root)
311 type(partition_t), intent(in) :: partition
312 integer, intent(out) :: part_global(:)
313 integer, optional, intent(in) :: root
314
315 integer(int64), allocatable :: rdispls(:)
316 integer, allocatable :: rcounts(:)
317
318 push_sub(partition_get_global)
319
320 safe_allocate(rdispls(1:partition%npart))
321 safe_allocate(rcounts(1:partition%npart))
322
323 rcounts(:) = partition%np_local_vec(:)
324 rdispls(:) = partition%istart_vec(:) - 1
325
326 assert(all(partition%part(1:partition%np_local) > 0))
327
328 if (present(root)) then
329 call partition%mpi_grp%gatherv(partition%part, partition%np_local, mpi_integer, &
330 part_global, rcounts, rdispls, mpi_integer, root)
331 else
332 call partition%mpi_grp%allgatherv(partition%part, partition%np_local, mpi_integer, &
333 part_global, rcounts, rdispls, mpi_integer)
334 end if
335
336 if (.not. present(root) .or. partition%mpi_grp%is_root()) then
337 assert(all(part_global(:) > 0))
338 end if
339
340#ifndef HAVE_MPI
341 part_global = partition%part
342#endif
343
344 safe_deallocate_a(rdispls)
345 safe_deallocate_a(rcounts)
346
347 pop_sub(partition_get_global)
348 end subroutine partition_get_global
349
350 ! ---------------------------------------------------------
355 subroutine partition_get_partition_number_4(partition, np, points, partno)
356 type(partition_t), intent(in) :: partition
357 integer, intent(in) :: np
358 integer(int64), intent(in) :: points(:)
359 integer, intent(out) :: partno(:)
360
361 integer :: ip, nproc, rnp
362 integer(int64), allocatable :: sbuffer(:), rbuffer(:)
363 integer, allocatable :: scounts(:), rcounts(:)
364 integer, allocatable :: sdispls(:), rdispls(:)
365 integer, allocatable :: ipos(:), order(:)
366
368
369 safe_allocate(scounts(1:partition%npart))
370 safe_allocate(rcounts(1:partition%npart))
371 safe_allocate(sdispls(1:partition%npart))
372 safe_allocate(rdispls(1:partition%npart))
373
374 ! How many points will we have to send/receive from each process?
375 scounts = 0
376 do ip = 1, np
377 ! Who knows where points(ip) is stored?
378 nproc = partition_get_number(partition, points(ip))
379 ! We increase the respective counter
380 scounts(nproc) = scounts(nproc) + 1
381 end do
382
383
384 !Tell each process how many points we will need from it
385 call partition%mpi_grp%alltoall(scounts, 1, mpi_integer, &
386 rcounts, 1, mpi_integer)
387
388 !Build displacement arrays
389 sdispls(1) = 0
390 rdispls(1) = 0
391 do ip = 2, partition%npart
392 sdispls(ip) = sdispls(ip-1) + scounts(ip-1)
393 rdispls(ip) = rdispls(ip-1) + rcounts(ip-1)
394 end do
395
396
397 rnp = sum(rcounts)
398 safe_allocate(sbuffer(1:np))
399 safe_allocate(rbuffer(1:rnp))
400
401 !Put points in correct order for sending
402 safe_allocate(ipos(1:partition%npart))
403 safe_allocate(order(1:np))
404 ipos = 0
405 do ip = 1, np
406 ! Who knows where points(ip) is stored?
407 nproc = partition_get_number(partition, points(ip))
408
409 !We increase the respective counter
410 ipos(nproc) = ipos(nproc) + 1
411
412 !Put the point in the correct place
413 order(ip) = sdispls(nproc) + ipos(nproc)
414 sbuffer(order(ip)) = points(ip) ! global index of the point
415 end do
416 safe_deallocate_a(ipos)
417
418 !Send the global indices of the points to the process that knows what is the corresponding partition
419 call partition%mpi_grp%alltoallv(sbuffer, scounts, sdispls, mpi_integer8, &
420 rbuffer, rcounts, rdispls, mpi_integer8)
421
422 !We get the partition number from the global index. This will be send back.
423 do ip = 1, rnp
424 if (rbuffer(ip) == 0) cycle
425 rbuffer(ip) = partition%part(rbuffer(ip) - partition%istart + 1)
426 end do
427
428 !Now we send the information backwards
429 call partition%mpi_grp%alltoallv(rbuffer, rcounts, rdispls, mpi_integer8, &
430 sbuffer, scounts, sdispls, mpi_integer8)
431
432 !Reorder the points
433 do ip = 1, np
434 partno(ip) = i8_to_i4(sbuffer(order(ip)))
435 end do
436
437 !Deallocate memory
438 safe_deallocate_a(order)
439 safe_deallocate_a(sbuffer)
440 safe_deallocate_a(scounts)
441 safe_deallocate_a(sdispls)
442 safe_deallocate_a(rbuffer)
443 safe_deallocate_a(rcounts)
444 safe_deallocate_a(rdispls)
445
448
449 !---------------------------------------------------------
450 subroutine partition_get_partition_number_8(partition, np, points, partno)
451 type(partition_t), intent(in) :: partition
452 integer(int64), intent(in) :: np
453 integer(int64), intent(in) :: points(:)
454 integer, intent(out) :: partno(:)
455
456 integer(int64) :: rounds, offset, iround
457
459
460 rounds = np/huge(0_int32)
461 do iround = 1, rounds
462 offset = (iround - 1)*huge(0_int32) + 1
463 call partition_get_partition_number(partition, huge(0_int32), points(offset:offset+huge(0_int32)), &
464 partno(offset:offset+huge(0_int32)))
465 end do
466 offset = rounds*huge(0_int32) + 1
467 call partition_get_partition_number(partition, i8_to_i4(np-offset+1), points(offset:np), &
468 partno(offset:np))
469
472
473 !---------------------------------------------------------
476 subroutine partition_get_np_local(partition, np_local_vec)
477 type(partition_t), intent(in) :: partition
478 integer, contiguous, intent(inout) :: np_local_vec(:)
479
480 integer, allocatable :: np_local_vec_tmp(:)
481 integer :: ip
482
483 push_sub(partition_get_np_local)
484
485 assert(ubound(np_local_vec, 1) >= partition%npart)
486 assert(partition%npart > 0)
487 assert(all(partition%part(:) > 0))
488 safe_allocate(np_local_vec_tmp(1:partition%npart))
489 np_local_vec_tmp = 0
490
491 ! Calculate locally the local points of each partition
492 do ip = 1, partition%np_local
493 np_local_vec_tmp(partition%part(ip)) = np_local_vec_tmp(partition%part(ip)) + 1
494 end do
495
496 ! Collect all the local points
497 call partition%mpi_grp%allreduce(np_local_vec_tmp, np_local_vec, partition%npart, mpi_integer, mpi_sum)
498 safe_deallocate_a(np_local_vec_tmp)
499
501 end subroutine partition_get_np_local
502
503 !---------------------------------------------------------
505 pure integer function partition_get_npart(partition) result(npart)
506 type(partition_t), intent(in) :: partition
507 npart = partition%npart
508 end function partition_get_npart
509
510 !---------------------------------------------------------
512 pure integer function partition_get_part(partition, local_point) result(part)
513 type(partition_t), intent(in) :: partition
514 integer, intent(in) :: local_point
515 part = partition%part(local_point)
516 end function partition_get_part
517
518 !---------------------------------------------------------
521 pure integer function partition_get_number(partition, global_point) result(part)
522 type(partition_t), intent(in) :: partition
523 integer(int64), intent(in) :: global_point
524
525 if (global_point == 0) then
526 part = partition%partno
527 else
528 part = i8_to_i4((partition%npart*global_point - 1)/partition%np_global + 1)
529 end if
530 end function partition_get_number
531
532 !---------------------------------------------------------
536 subroutine partition_get_local(partition, rbuffer, np_local)
537 type(partition_t), intent(in) :: partition
538 integer(int64), contiguous, intent(inout) :: rbuffer(:)
539 integer, intent(out) :: np_local
540
541 integer :: ip, ipart
542 integer(int64) :: istart
543 integer(int64), allocatable :: sdispls(:), rdispls(:), sbuffer(:)
544 integer, allocatable :: scounts(:), rcounts(:)
545
546 push_sub(partition_get_local)
547
548 safe_allocate(sdispls(1:partition%npart))
549 safe_allocate(scounts(1:partition%npart))
550 safe_allocate(rcounts(1:partition%npart))
551
552 ! Calculate the starting point of the running process
553 istart = partition%istart - 1
554
555 scounts = 0
556 ! Count and store the local points for each partition
557 do ip = 1, partition%np_local
558 ipart = partition%part(ip)
559 scounts(ipart) = scounts(ipart) + 1
560 end do
561
562 ! Create displacements
563 sdispls(1) = 0
564 do ipart = 2, partition%npart
565 sdispls(ipart) = sdispls(ipart-1) + scounts(ipart-1)
566 end do
567
568 ! Allocate and fill the send buffer
569 np_local = sum(scounts)
570 scounts = 0
571 safe_allocate(sbuffer(1:np_local))
572 do ip = 1, np_local
573 ipart = partition%part(ip)
574 scounts(ipart) = scounts(ipart) + 1
575 sbuffer(sdispls(ipart) + scounts(ipart)) = ip + istart
576 end do
577
578 ! Tell each process how many points we will need from it
579 call partition%mpi_grp%alltoall(scounts, 1, mpi_integer, &
580 rcounts, 1, mpi_integer)
581
582 ! Create the displacement vector from the counts vector
583 np_local = sum(rcounts)
584 safe_allocate(rdispls(1:partition%npart))
585 assert(ubound(rbuffer, 1) >= np_local)
586
587 rdispls(1) = 0
588 do ipart = 2, partition%npart
589 rdispls(ipart) = rcounts(ipart-1) + rdispls(ipart-1)
590 end do
591
592 ! Collect the corresponding points to each process
593 call partition%mpi_grp%alltoallv(sbuffer, scounts, sdispls, mpi_integer8, &
594 rbuffer, rcounts, rdispls, mpi_integer8)
595
596 safe_deallocate_a(sdispls)
597 safe_deallocate_a(scounts)
598 safe_deallocate_a(sbuffer)
599 safe_deallocate_a(rcounts)
600 safe_deallocate_a(rdispls)
601
602 pop_sub(partition_get_local)
603 end subroutine partition_get_local
604end module partition_oct_m
605
606!! Local Variables:
607!! mode: f90
608!! coding: utf-8
609!! End:
subroutine, public io_binary_get_info(fname, np, file_size, ierr)
subroutine, public iwrite_header(fname, np_global, ierr)
Definition: io.F90:116
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
integer, parameter, public c_mpi_file_read
Definition: mpi_debug.F90:139
subroutine, public mpi_debug_in(comm, index)
Definition: mpi_debug.F90:233
subroutine, public mpi_debug_out(comm, index)
Definition: mpi_debug.F90:257
integer, parameter, public c_mpi_file_write
Definition: mpi_debug.F90:139
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:272
subroutine, public partition_init(partition, np_global, mpi_grp)
initialize the partition table
Definition: partition.F90:195
pure integer function partition_get_number(partition, global_point)
Returns the partition number for a given global index If the index is zero, return local partition.
Definition: partition.F90:609
pure integer function, public partition_get_npart(partition)
Returns the total number of partitions.
Definition: partition.F90:593
subroutine, public partition_set(partition, part)
Definition: partition.F90:245
subroutine partition_get_partition_number_4(partition, np, points, partno)
Given a list of global indices, return the partition number where those points are stored....
Definition: partition.F90:443
subroutine, public partition_get_np_local(partition, np_local_vec)
Given the partition, returns the corresponding number of local points that each partition has.
Definition: partition.F90:564
pure integer function, public partition_get_part(partition, local_point)
Returns the partition of the local point.
Definition: partition.F90:600
subroutine partition_get_partition_number_8(partition, np, points, partno)
Definition: partition.F90:538
subroutine, public partition_dump(partition, dir, filename, ierr)
write the partition data
Definition: partition.F90:262
subroutine, public partition_get_local(partition, rbuffer, np_local)
Calculates the local vector of all partitions in parallel. Local vector stores the global point indic...
Definition: partition.F90:624
subroutine, public partition_end(partition)
Definition: partition.F90:232
subroutine, public partition_load(partition, dir, filename, ierr)
read the partition data
Definition: partition.F90:311
subroutine, public partition_get_local_size(partition, istart, np_local)
Definition: partition.F90:382
subroutine, public partition_get_global(partition, part_global, root)
Returns the global partition. If root is present, the partition is gathered only in that node....
Definition: partition.F90:398
The partition is an array that contains the mapping between some global index and a process,...
Definition: partition.F90:167