Ejemplo n.º 1
0
    def test_get_nonempty_ranks(self):
        from ocgis.variable.dimension import Dimension
        comm, rank, size = get_standard_comm_state()

        if size not in [1, 3]:
            raise SkipTest('MPI_SIZE != 1 or 3')

        target = Dimension('a')
        live_ranks = get_nonempty_ranks(target, vm)
        if MPI_RANK == 0:
            self.assertEqual(live_ranks, tuple(range(size)))

        if MPI_SIZE == 3:
            targets = {0: Dimension('a', is_empty=True, dist=True),
                       1: Dimension('a'),
                       2: Dimension('a')}
            with vm.scoped('ner', vm.ranks):
                live_ranks = get_nonempty_ranks(targets[MPI_RANK], vm)
            self.assertEqual(live_ranks, (1, 2))
Ejemplo n.º 2
0
    def test_get_distributed_slice_on_rank_subset(self):
        """Test with some a priori empty dimensions."""
        if MPI_SIZE != 4:
            raise SkipTest('MPI_SIZE != 4')

        ompi = OcgDist()
        dim = ompi.create_dimension('eight', 8, dist=True)
        ompi.update_dimension_bounds()

        sub = dim.get_distributed_slice(slice(2, 6))

        live_ranks = get_nonempty_ranks(sub, vm)
        if MPI_RANK in [1, 2]:
            self.assertFalse(sub.is_empty)
        else:
            self.assertTrue(sub.is_empty)
            self.assertEqual(sub.bounds_local, (0, 0))
            self.assertEqual(sub.bounds_global, (0, 0))

        with vm.scoped('live rank dim subset', live_ranks):
            if not vm.is_null:
                sub2 = sub.get_distributed_slice(slice(2, 4))
            else:
                sub2 = None

        if MPI_RANK == 2:
            self.assertEqual(sub2.bounds_local, (0, 2))
            self.assertEqual(sub2.bounds_global, (0, 2))
            self.assertFalse(sub2.is_empty)
        else:
            self.assertTrue(sub2 is None or sub2.is_empty)

        # Try again w/out scoping.
        if sub.is_empty:
            with self.assertRaises(EmptyObjectError):
                sub.get_distributed_slice(slice(2, 4))
Ejemplo n.º 3
0
    def test_get_distributed_slice_on_rank_subset(self):
        """Test with some a priori empty dimensions."""
        if MPI_SIZE != 4:
            raise SkipTest('MPI_SIZE != 4')

        ompi = OcgDist()
        dim = ompi.create_dimension('eight', 8, dist=True)
        ompi.update_dimension_bounds()

        sub = dim.get_distributed_slice(slice(2, 6))

        live_ranks = get_nonempty_ranks(sub, vm)
        if MPI_RANK in [1, 2]:
            self.assertFalse(sub.is_empty)
        else:
            self.assertTrue(sub.is_empty)
            self.assertEqual(sub.bounds_local, (0, 0))
            self.assertEqual(sub.bounds_global, (0, 0))

        with vm.scoped('live rank dim subset', live_ranks):
            if not vm.is_null:
                sub2 = sub.get_distributed_slice(slice(2, 4))
            else:
                sub2 = None

        if MPI_RANK == 2:
            self.assertEqual(sub2.bounds_local, (0, 2))
            self.assertEqual(sub2.bounds_global, (0, 2))
            self.assertFalse(sub2.is_empty)
        else:
            self.assertTrue(sub2 is None or sub2.is_empty)

        # Try again w/out scoping.
        if sub.is_empty:
            with self.assertRaises(EmptyObjectError):
                sub.get_distributed_slice(slice(2, 4))
Ejemplo n.º 4
0
 def get_live_ranks_from_object(self, target):
     return get_nonempty_ranks(target, self)
Ejemplo n.º 5
0
    def get_distributed_slice(self, slc):
        """
        Slice the dimension in parallel. The sliced dimension object is a shallow copy. The returned dimension may be
        empty.
        
        :param slc: A :class:`slice`-like object or a fancy slice. If this is a fancy slice, ``slc`` must be
         processor-local. If the fancy slice uses integer indices, the indices must be local. In other words, a fancy
         ``slc`` is not manipulated or redistributed prior to slicing.
        :rtype: :class:`~ocgis.Dimension`
        :raises: :class:`~ocgis.exc.EmptyObjectError`
        """

        raise_if_empty(self)

        slc = get_formatted_slice(slc, 1)[0]
        is_fancy = not isinstance(slc, slice)

        if not is_fancy and slc == slice(None):
            ret = self.copy()
        # Use standard slicing for non-distributed dimensions.
        elif not self.dist:
            ret = self[slc]
        else:
            if is_fancy:
                local_slc = slc
            else:
                local_slc = get_global_to_local_slice((slc.start, slc.stop),
                                                      self.bounds_local)
                if local_slc is not None:
                    local_slc = slice(*local_slc)
            # Slice does not overlap local bounds. The dimension is now empty with size 0.
            if local_slc is None:
                ret = self.copy()
                ret.convert_to_empty()
                dimension_size = 0
            # Slice overlaps so do a slice on the dimension using the local slice.
            else:
                ret = self[local_slc]
                dimension_size = len(ret)
            assert dimension_size >= 0
            dimension_sizes = vm.gather(dimension_size)
            if vm.rank == 0:
                sum_dimension_size = 0
                for ds in dimension_sizes:
                    try:
                        sum_dimension_size += ds
                    except TypeError:
                        pass
                bounds_global = (0, sum_dimension_size)
            else:
                bounds_global = None
            bounds_global = vm.bcast(bounds_global)
            if not ret.is_empty:
                ret.bounds_global = bounds_global

            # Normalize the local bounds on live ranks.
            inner_live_ranks = get_nonempty_ranks(ret, vm)
            with vm.scoped('bounds normalization', inner_live_ranks):
                if not vm.is_null:
                    if vm.rank == 0:
                        adjust = len(ret)
                    else:
                        adjust = None
                    adjust = vm.bcast(adjust)
                    for current_rank in vm.ranks:
                        if vm.rank == current_rank:
                            if vm.rank != 0:
                                ret.bounds_local = [
                                    b + adjust for b in ret.bounds_local
                                ]
                                adjust += len(ret)
                        vm.barrier()
                        adjust = vm.bcast(adjust, root=current_rank)
        return ret
Ejemplo n.º 6
0
    def get_distributed_slice(self, slc):
        """
        Slice the dimension in parallel. The sliced dimension object is a shallow copy. The returned dimension may be
        empty.
        
        :param slc: A :class:`slice`-like object or a fancy slice. If this is a fancy slice, ``slc`` must be
         processor-local. If the fancy slice uses integer indices, the indices must be local. In other words, a fancy
         ``slc`` is not manipulated or redistributed prior to slicing.
        :rtype: :class:`~ocgis.Dimension`
        :raises: :class:`~ocgis.exc.EmptyObjectError`
        """

        raise_if_empty(self)

        slc = get_formatted_slice(slc, 1)[0]
        is_fancy = not isinstance(slc, slice)

        if not is_fancy and slc == slice(None):
            ret = self.copy()
        # Use standard slicing for non-distributed dimensions.
        elif not self.dist:
            ret = self[slc]
        else:
            if is_fancy:
                local_slc = slc
            else:
                local_slc = get_global_to_local_slice((slc.start, slc.stop), self.bounds_local)
                if local_slc is not None:
                    local_slc = slice(*local_slc)
            # Slice does not overlap local bounds. The dimension is now empty with size 0.
            if local_slc is None:
                ret = self.copy()
                ret.convert_to_empty()
                dimension_size = 0
            # Slice overlaps so do a slice on the dimension using the local slice.
            else:
                ret = self[local_slc]
                dimension_size = len(ret)
            assert dimension_size >= 0
            dimension_sizes = vm.gather(dimension_size)
            if vm.rank == 0:
                sum_dimension_size = 0
                for ds in dimension_sizes:
                    try:
                        sum_dimension_size += ds
                    except TypeError:
                        pass
                bounds_global = (0, sum_dimension_size)
            else:
                bounds_global = None
            bounds_global = vm.bcast(bounds_global)
            if not ret.is_empty:
                ret.bounds_global = bounds_global

            # Normalize the local bounds on live ranks.
            inner_live_ranks = get_nonempty_ranks(ret, vm)
            with vm.scoped('bounds normalization', inner_live_ranks):
                if not vm.is_null:
                    if vm.rank == 0:
                        adjust = len(ret)
                    else:
                        adjust = None
                    adjust = vm.bcast(adjust)
                    for current_rank in vm.ranks:
                        if vm.rank == current_rank:
                            if vm.rank != 0:
                                ret.bounds_local = [b + adjust for b in ret.bounds_local]
                                adjust += len(ret)
                        vm.barrier()
                        adjust = vm.bcast(adjust, root=current_rank)
        return ret