def take_turns(comm, at_a_time, func, *args, **kwargs):
    """
    Processes call a function in groups.

    Any extra positional and keyword arguments are passed to the function.

    Args:
        comm:  mpi4py.MPI.Comm or None.
        at_a_time (int): the maximum number of processes to run at a time.
        func: the function to call.

    Returns:
        The return value on each process is the return value of the function.

    """
    if comm is None:
        # just run the function
        return func(*args, **kwargs)

    nproc = comm.size
    rank = comm.rank

    if at_a_time >= nproc:
        # every process just runs at once
        return func(*args, **kwargs)

    # we split the communicator to enforce a fixed number of processes
    # running at once.

    groupsize = nproc // at_a_time
    if nproc % at_a_time != 0:
        groupsize += 1

    group = rank // groupsize
    group_rank =  rank % groupsize

    comm_group = comm.Split(color=group, key=group_rank)

    # within each group, processes take turns

    ret = None
    for p in range(comm_group.size):
        if p == comm_group.rank:
            ret = func(*args, **kwargs)
        comm_group.barrier()

    return ret