sisl.mpi.BlockCyclicDistribution

class sisl.mpi.BlockCyclicDistribution

Bases: Distribution

Blocks of blocksize indices dealt round-robin, as ScaLAPACK lays out a matrix.

Ranks are visited in order, each taking blocksize consecutive indices, and the cycle repeats until the indices run out. A rank therefore receives one block, then later another, and so on – which is why range returns several runs rather than one.

Equivalently: index i belongs to block i // blocksize, and block k to rank k % size.

blocksize=1, the default, is plain cyclic: index i to rank i % size. That balances load when work varies systematically along the axis, at the cost of destroying locality. Larger values give the block-cyclic layout a distributed dense eigensolver expects, so a matrix destined for one should be laid out this way from the start rather than redistributed later.

blocksize=0 is shorthand for one contiguous block per rank – the whole axis split into size pieces, which maximises locality, e.g. k-points or energies that are close to each other end up on the same rank.

Contiguous is a special case rather than a large blocksize because no uniform block size reproduces it. With n=10, size=3 a contiguous split is [4, 3, 3], while blocksize=4 gives [4, 4, 2]; worse, at n=5, size=4 a uniform blocksize=2 yields [2, 2, 1, 0] and leaves a rank idle, where the contiguous split gives [2, 1, 1, 1]. The remainder is therefore spread one index per rank across the leading ranks, so the largest block exceeds the smallest by at most one.

For any other blocksize the load is only even when the blocks divide the indices evenly. That is inherent – the block structure is the point, and evening out the tail would break it.

Parameters:
  • n – number of indices to distribute

  • blocksize – indices per block. 1 is plain cyclic, 0 one contiguous block per rank; ScaLAPACK codes typically use 32 or 64.

  • comm – communicator the indices are distributed across, None means sisl.mpi.get_comm.

Examples

With comm having 3 ranks:

>>> BlockCyclicDistribution(10, comm=comm).indices(1)
array([1, 4, 7], dtype=int32)
>>> BlockCyclicDistribution(10, 2, comm=comm).range(0)
((0, 2), (6, 8))
>>> BlockCyclicDistribution(10, 0, comm=comm).counts
array([4, 3, 3], dtype=int32)

Methods

count([rank])

Number of indices owned by rank (default this process).

indices([rank])

The indices owned by rank (default this process), ascending.

owner(indices)

Which block an index falls in decides the rank.

range([rank])

The contiguous runs owned by rank, as ascending (start, stop) pairs.

Attributes

blocksize

Indices per block; 0 meaning one contiguous block per rank.

comm

The communicator the indices are distributed across.

contiguous

Whether each rank holds a single unbroken run.

counts

Number of indices owned by each rank.

n

Total number of indices across all ranks.

period

Indices consumed by one full cycle over all ranks.

rank

The rank of this process in comm.

size

Number of ranks this is distributed across.

__call__(obj, *args, **kwargs)

Distribute obj, by calling obj.__sisl_distribute__(self, *args, **kwargs).

count(rank=None)

Number of indices owned by rank (default this process).

Parameters:

rank (int | None)

Return type:

int

indices(rank=None)

The indices owned by rank (default this process), ascending.

Parameters:

rank (int | None)

Return type:

ndarray

owner(indices)[source]

Which block an index falls in decides the rank.

Return type:

ndarray

range(rank=None)[source]

The contiguous runs owned by rank, as ascending (start, stop) pairs.

A rank owning nothing gets an empty tuple. Runs are the primitive rather than a flat index array because they slice: a consumer can copy each run directly instead of gathering scattered elements.

Parameters:

rank (int | None) – defaults to the rank of this process.

Return type:

tuple[tuple[int, int], …]

property blocksize: int

Indices per block; 0 meaning one contiguous block per rank.

property comm

The communicator the indices are distributed across.

property contiguous: bool

Whether each rank holds a single unbroken run.

property counts: ndarray

Number of indices owned by each rank.

property n: int

Total number of indices across all ranks.

property period: int

Indices consumed by one full cycle over all ranks.

property rank: int

The rank of this process in comm.

property size: int

Number of ranks this is distributed across.