Return splitting positions based on the weights provided by each rank.
This is the SPARTA algorithm which uses simple splitting based on given weights for all elements in the mesh.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=rk), | intent(in) | :: | weight(:) |
Sorted list of weights corresponding to treeID order |
||
integer, | intent(in) | :: | myPart | |||
integer, | intent(in) | :: | nParts |
Number of procs the distribution should span |
||
integer, | intent(in) | :: | comm |
MPI Communicator |
||
integer, | intent(inout) | :: | myElems |
number of elements |
||
integer(kind=long_k), | intent(out) | :: | offset |
Array of offsets with the size nParts. Offset index starts at 0. This Array needs to be allocate and deallocated outside |
||
type(tem_sparta_type), | intent(inout) | :: | sparta |
subroutine tem_balance_sparta(weight, myPart, nParts, comm, myElems, offset, & & sparta ) ! --------------------------------------------------------------------------- !> Sorted list of weights corresponding to treeID order real(kind=rk),intent(in) :: weight(:) integer,intent(in) :: myPart !< Rank of the calling process !> Number of procs the distribution should span integer, intent(in) :: nParts !> MPI Communicator integer, intent(in) :: comm !> number of elements integer, intent(inout) :: myElems !> Array of offsets with the size nParts. Offset index starts at 0. !! This Array needs to be allocate and deallocated outside integer(kind=long_k), intent(out) :: offset ! Count variables that state which rank gets how many elements ! *_count(rank0, ...) ! Right now the size is nParts but that will be changed in ! the near future to avoid O(p) allocations type( tem_sparta_type ), intent(inout) :: sparta ! --------------------------------------------------------------------------- integer :: iErr ! MPI error variable integer :: iElem, iProc integer(kind=long_k) :: myElems_long real(kind=rk) :: w_sum, w_opt real(kind=rk) :: send, recv ! Send and receive buffers for MPI calls ! boundary values of the elements in which we search for splitters real(kind=rk) :: lower_boundary, upper_boundary ! local prefix sum array of myElems real(kind=rk), allocatable :: presum(:) integer :: rmin, rmax, lb, ub, left_off, mid real(kind=rk) :: opt_split, wsplit integer :: send_count(0:nParts-1) ! --------------------------------------------------------------------------- write(logUnit(5),*) "Balance by SpartA algorithm." send_count = 0 ! Allocate Array for Prefix sum allocate(presum(myElems)) ! Prefix sum over local weights. later on we will look for the splitter in ! this prefix sum presum(1) = weight(1) do iElem = 2,myElems presum(iElem) = presum(iElem-1) + weight(iElem) end do send = presum(myElems) ! sum up global total weight call MPI_ALLREDUCE(send, recv, 1, rk_mpi, mpi_sum, comm, iErr) w_sum = recv ! Calculate global optimum w_opt = w_sum / dble(nParts) ! Global prefix sum for weights call MPI_EXSCAN(send, recv, 1, rk_mpi, mpi_sum, comm, iErr) ! initialize splitter search lower_boundary=recv if (myPart == 0) lower_boundary = 0 upper_boundary = lower_boundary + presum(myElems) rmin = max(floor(lower_boundary/w_opt),0) rmax = min(ceiling(upper_boundary / w_opt),nParts-1) ! Do splitter search left_off = 1 do iProc = rmin,rmax lb = left_off ub = myelems opt_split = (iProc+1)*w_opt if (iProc*w_opt < upper_boundary) then do mid = (lb+ub)/2 wsplit = presum(mid) + lower_boundary if (wsplit .feq. opt_split) exit if (wsplit < opt_split) then lb = mid else ub = mid end if ! exit if a single element was found, need to do this if (lb >= ub-1) exit ! here, to have mid and wsplit set. end do if (ABS(wsplit - opt_split) > ABS(wsplit - opt_split - weight(mid))) then mid = mid - 1 ! return 0 if the splitter is left of the lower boundary else if (mid+1 <= myElems) then if (ABS(wsplit - opt_split) & & > ABS(wsplit - opt_split + weight(mid+1))) then mid = mid + 1 ! return myElems at most end if else if (opt_split > upper_boundary) mid = myElems end if end if send_count(iProc) = mid - left_off + 1 left_off = mid + 1 end if end do ! finished splitter search. Communciate results. ! Each process needs to know how many elements to receive from which process call tem_set_sparta( sparta, comm, nParts, send_count ) ! Calculate myElems and offset ----------------------------------- ! total number of my elements after exchanging elements myElems = sparta%new_size myElems_long = int(myElems, kind=long_k) call mpi_exscan(myElems_long, offset, 1, long_k_mpi, mpi_sum, comm, ierr) if (myPart == 0) offset = 0 ! write(*,"(3(A,I0))") 'myPart ', myPart, ' nElems: ', myElems, ' offset: ', offset ! Calculate myElems and offset ----------------------------------- end subroutine tem_balance_sparta