Example #1
0
    def test_subdimleft_parallel(self):
        """
        Tests application of an Operator consisting of a subdimension
        defined over different sub-regions, explicitly created through the
        use of :class:`SubDimension`s.

        This tests that flow direction is not being automatically inferred
        from whether the subdimension is on the left or right boundary.
        """
        grid = Grid(shape=(20, 20))
        x, y = grid.dimensions
        t = grid.stepping_dim
        thickness = 4

        u = TimeFunction(name='u', save=None, grid=grid, space_order=0, time_order=1)

        xl = SubDimension.left(name='xl', parent=x, thickness=thickness)

        yi = SubDimension.middle(name='yi', parent=y,
                                 thickness_left=thickness, thickness_right=thickness)

        # Can be done in parallel
        eq = Eq(u[t+1, xl, yi], u[t, xl, yi] + 1)

        op = Operator([eq])

        iterations = FindNodes(Iteration).visit(op)
        assert all(i.is_Affine and i.is_Parallel for i in iterations if i.dim in [xl, yi])

        op.apply(time_m=0, time_M=0)

        assert np.all(u.data[1, 0:thickness, 0:thickness] == 0)
        assert np.all(u.data[1, 0:thickness, -thickness:] == 0)
        assert np.all(u.data[1, 0:thickness, thickness:-thickness] == 1)
        assert np.all(u.data[1, thickness+1:, :] == 0)
Example #2
0
    def test_subdimensions(self):
        nt = 10
        grid = Grid(shape=(10, 10, 10))
        x, y, z = grid.dimensions
        xi = SubDimension.middle(name='xi',
                                 parent=x,
                                 thickness_left=2,
                                 thickness_right=2)
        yi = SubDimension.middle(name='yi',
                                 parent=y,
                                 thickness_left=2,
                                 thickness_right=2)
        zi = SubDimension.middle(name='zi',
                                 parent=z,
                                 thickness_left=2,
                                 thickness_right=2)

        u = TimeFunction(name='u', grid=grid, save=nt)
        u1 = TimeFunction(name='u', grid=grid, save=nt)

        eqn = Eq(u.forward, u + 1).xreplace({x: xi, y: yi, z: zi})

        op0 = Operator(eqn, opt='noop')
        op1 = Operator(eqn, opt='buffering')

        # Check generated code
        assert len(retrieve_iteration_tree(op1)) == 2
        assert len([i for i in FindSymbols().visit(op1) if i.is_Array]) == 1

        op0.apply(time_M=nt - 2)
        op1.apply(time_M=nt - 2, u=u1)

        assert np.all(u.data == u1.data)
Example #3
0
    def test_save_w_subdims(self):
        nt = 10
        grid = Grid(shape=(10, 10))
        x, y = grid.dimensions
        time_dim = grid.time_dim
        xi = SubDimension.middle(name='xi', parent=x, thickness_left=3, thickness_right=3)
        yi = SubDimension.middle(name='yi', parent=y, thickness_left=3, thickness_right=3)

        factor = Constant(name='factor', value=2, dtype=np.int32)
        time_sub = ConditionalDimension(name="time_sub", parent=time_dim, factor=factor)

        u = TimeFunction(name='u', grid=grid)
        usave = TimeFunction(name='usave', grid=grid, time_order=0,
                             save=int(nt//factor.data), time_dim=time_sub)

        eqns = [Eq(u.forward, u + 1),
                Eq(usave, u.forward)]
        eqns = [e.xreplace({x: xi, y: yi}) for e in eqns]

        op = Operator(eqns, opt=('buffering', 'tasking', 'orchestrate'))

        op.apply(time_M=nt-1)

        for i in range(usave.save):
            assert np.all(usave.data[i, 3:-3, 3:-3] == 2*i + 1)
            assert np.all(usave.data[i, :3, :] == 0)
            assert np.all(usave.data[i, -3:, :] == 0)
            assert np.all(usave.data[i, :, :3] == 0)
            assert np.all(usave.data[i, :, -3:] == 0)
Example #4
0
    def test_expandingbox_like(self, opt):
        """
        Make sure SubDimensions aren't an obstacle to expanding boxes.
        """
        grid = Grid(shape=(8, 8))
        x, y = grid.dimensions

        u = TimeFunction(name='u', grid=grid)
        xi = SubDimension.middle(name='xi', parent=x, thickness_left=2, thickness_right=2)
        yi = SubDimension.middle(name='yi', parent=y, thickness_left=2, thickness_right=2)

        eqn = Eq(u.forward, u + 1)
        eqn = eqn.subs({x: xi, y: yi})

        op = Operator(eqn, opt=opt)

        op.apply(time=3, x_m=2, x_M=5, y_m=2, y_M=5,
                 xi_ltkn=0, xi_rtkn=0, yi_ltkn=0, yi_rtkn=0)

        assert np.all(u.data[0, 2:-2, 2:-2] == 4.)
        assert np.all(u.data[1, 2:-2, 2:-2] == 3.)
        assert np.all(u.data[:, :2] == 0.)
        assert np.all(u.data[:, -2:] == 0.)
        assert np.all(u.data[:, :, :2] == 0.)
        assert np.all(u.data[:, :, -2:] == 0.)
Example #5
0
def setup_w_over_q(wOverQ, w, qmin, qmax, npad, sigma=0):
    """
    Initialise spatially variable w/Q field used to implement attenuation and
    absorb outgoing waves at the edges of the model. Uses Devito Operator.

    Parameters
    ----------
    wOverQ : Function, required
        The omega over Q field used to implement attenuation in the model,
        and the absorbing boundary condition for outgoing waves.
    w : float32, required
        center angular frequency, e.g. peak frequency of Ricker source wavelet
        used for modeling.
    qmin : float32, required
        Q value at the edge of the model. Typically set to 0.1 to strongly
        attenuate outgoing waves.
    qmax : float32, required
        Q value in the interior of the model. Typically set to 100 as a
        reasonable and physically meaningful Q value.
    npad : int, required
        Number of points in the absorbing boundary region. Note that we expect
        this to be the same on all sides of the model.
    sigma : float32, optional, defaults to None
        sigma value for call to scipy gaussian smoother, default 5.
    """
    # sanity checks
    assert w > 0, "supplied w value [%f] must be positive" % (w)
    assert qmin > 0, "supplied qmin value [%f] must be positive" % (qmin)
    assert qmax > 0, "supplied qmax value [%f] must be positive" % (qmax)
    assert npad > 0, "supplied npad value [%f] must be positive" % (npad)
    for n in wOverQ.grid.shape:
        if n - 2 * npad < 1:
            raise ValueError("2 * npad must not exceed dimension size!")

    lqmin = np.log(qmin)
    lqmax = np.log(qmax)

    # 1. Get distance to closest boundary in all dimensions
    # 2. Logarithmic variation between qmin, qmax across the absorbing boundary
    eqs = [Eq(wOverQ, 1)]
    for d in wOverQ.dimensions:
        # left
        dim_l = SubDimension.left(name='abc_%s_l' % d.name,
                                  parent=d,
                                  thickness=npad)
        pos = Abs(dim_l - d.symbolic_min) / float(npad)
        eqs.append(
            Eq(wOverQ.subs({d: dim_l}), Min(wOverQ.subs({d: dim_l}), pos)))
        # right
        dim_r = SubDimension.right(name='abc_%s_r' % d.name,
                                   parent=d,
                                   thickness=npad)
        pos = Abs(d.symbolic_max - dim_r) / float(npad)
        eqs.append(
            Eq(wOverQ.subs({d: dim_r}), Min(wOverQ.subs({d: dim_r}), pos)))

    eqs.append(Eq(wOverQ, w / exp(lqmin + wOverQ * (lqmax - lqmin))))
    # 2020.05.04 currently does not support spatial smoothing of the Q field
    # due to MPI weirdness in reassignment of the numpy array
    Operator(eqs, name='WOverQ_Operator')()
Example #6
0
def initialize_function(function, data, nbpml):
    """
    Initialize a `Function` with the given ``data``. ``data``
    does *not* include the PML layers for the absorbing boundary conditions;
    these are added via padding by this function.

    Parameters
    ----------
    function : Function
        The initialised object.
    data : ndarray
        The data array used for initialisation.
    nbpml : int
        Number of PML layers for boundary damping.
    """
    slices = tuple([slice(nbpml, -nbpml) for _ in range(function.grid.dim)])
    function.data[slices] = data
    eqs = []

    for d in function.dimensions:
        dim_l = SubDimension.left(name='abc_%s_l' % d.name,
                                  parent=d,
                                  thickness=nbpml)
        to_copy = nbpml
        eqs += [Eq(function.subs({d: dim_l}), function.subs({d: to_copy}))]
        dim_r = SubDimension.right(name='abc_%s_r' % d.name,
                                   parent=d,
                                   thickness=nbpml)
        to_copy = d.symbolic_max - nbpml
        eqs += [Eq(function.subs({d: dim_r}), function.subs({d: to_copy}))]

    # TODO: Figure out why yask doesn't like it with dse/dle
    Operator(eqs, name='padfunc', dse='noop', dle='noop')()
Example #7
0
def test_everything():
    nt = 50
    grid = Grid(shape=(6, 6))
    x, y = grid.dimensions
    time = grid.time_dim
    xi = SubDimension.middle(name='xi',
                             parent=x,
                             thickness_left=2,
                             thickness_right=2)
    yi = SubDimension.middle(name='yi',
                             parent=y,
                             thickness_left=2,
                             thickness_right=2)

    factor = Constant(name='factor', value=5, dtype=np.int32)
    t_sub = ConditionalDimension('t_sub', parent=time, factor=factor)
    save_shift = Constant(name='save_shift', dtype=np.int32)

    u = TimeFunction(name='u', grid=grid, time_order=0)
    u1 = TimeFunction(name='u', grid=grid, time_order=0)
    va = TimeFunction(name='va',
                      grid=grid,
                      time_order=0,
                      save=(int(nt // factor.data)),
                      time_dim=t_sub)
    vb = TimeFunction(name='vb',
                      grid=grid,
                      time_order=0,
                      save=(int(nt // factor.data)),
                      time_dim=t_sub)

    for i in range(va.save):
        va.data[i, :] = i
        vb.data[i, :] = i * 2 - 1

    vas = va.subs(t_sub, t_sub - save_shift)
    vasb = va.subs(t_sub, t_sub - 1 - save_shift)
    vasf = va.subs(t_sub, t_sub + 1 - save_shift)

    eqns = [Eq(u.forward, u + (vasb + vas + vasf) * 2. + vb)]

    eqns = [e.xreplace({x: xi, y: yi}) for e in eqns]

    op0 = Operator(eqns, opt='noop')
    op1 = Operator(eqns, opt='buffering')

    # Check generated code
    assert len([i for i in FindSymbols().visit(op1) if i.is_Array]) == 2

    op0.apply(time_m=15, time_M=35, save_shift=0)
    op1.apply(time_m=15, time_M=35, save_shift=0, u=u1)

    assert np.all(u.data == u1.data)
Example #8
0
    def test_sub_dimension(self):
        """Test that different SubDimensions have different hash value."""
        d0 = Dimension(name='d')
        d1 = Dimension(name='d', spacing=Scalar(name='s'))

        di0 = SubDimension.middle('di', d0, 1, 1)
        di1 = SubDimension.middle('di', d1, 1, 1)
        assert hash(di0) != hash(d0)
        assert hash(di0) != hash(di1)

        dl0 = SubDimension.left('dl', d0, 2)
        assert hash(dl0) != hash(di0)
Example #9
0
    def test_subdimmiddle_notparallel(self):
        """
        Tests application of an Operator consisting of a subdimension
        defined over different sub-regions, explicitly created through the
        use of :class:`SubDimension`s.

        Different from ``test_subdimmiddle_parallel`` because an interior
        dimension cannot be evaluated in parallel.
        """
        grid = Grid(shape=(20, 20))
        x, y = grid.dimensions
        t = grid.stepping_dim
        thickness = 4

        u = TimeFunction(name='u',
                         save=None,
                         grid=grid,
                         space_order=0,
                         time_order=1)

        xi = SubDimension.middle(name='xi',
                                 parent=x,
                                 thickness_left=thickness,
                                 thickness_right=thickness)

        yi = SubDimension.middle(name='yi',
                                 parent=y,
                                 thickness_left=thickness,
                                 thickness_right=thickness)

        # flow dependencies in x and y which should force serial execution
        # in reverse direction
        centre = Eq(u[t + 1, xi, yi], u[t, xi, yi] + u[t + 1, xi + 1, yi + 1])
        u.data[0, 10, 10] = 1.0

        op = Operator([centre])

        iterations = FindNodes(Iteration).visit(op)
        assert all(i.is_Affine and i.is_Sequential for i in iterations
                   if i.dim == xi)
        assert all(i.is_Affine and i.is_Parallel for i in iterations
                   if i.dim == yi)

        op.apply(time_m=0, time_M=0)

        for i in range(4, 11):
            assert u.data[1, i, i] == 1.0
            u.data[1, i, i] = 0.0

        assert np.all(u.data[1, :] == 0)
Example #10
0
    def test_subdimmiddle_parallel(self):
        """
        Tests application of an Operator consisting of a subdimension
        defined over different sub-regions, explicitly created through the
        use of :class:`SubDimension`s.
        """
        grid = Grid(shape=(20, 20))
        x, y = grid.dimensions
        t = grid.stepping_dim
        thickness = 4

        u = TimeFunction(name='u',
                         save=None,
                         grid=grid,
                         space_order=0,
                         time_order=1)

        xi = SubDimension.middle(name='xi',
                                 parent=x,
                                 thickness_left=thickness,
                                 thickness_right=thickness)

        yi = SubDimension.middle(name='yi',
                                 parent=y,
                                 thickness_left=thickness,
                                 thickness_right=thickness)

        # a 5 point stencil that can be computed in parallel
        centre = Eq(
            u[t + 1, xi, yi], u[t, xi, yi] + u[t, xi - 1, yi] +
            u[t, xi + 1, yi] + u[t, xi, yi - 1] + u[t, xi, yi + 1])

        u.data[0, 10, 10] = 1.0

        op = Operator([centre])

        iterations = FindNodes(Iteration).visit(op)
        assert all(i.is_Affine and i.is_Parallel for i in iterations
                   if i.dim in [xi, yi])

        op.apply(time_m=0, time_M=0)

        assert np.all(u.data[1, 9:12, 10] == 1.0)
        assert np.all(u.data[1, 10, 9:12] == 1.0)

        # Other than those, it should all be 0
        u.data[1, 9:12, 10] = 0.0
        u.data[1, 10, 9:12] = 0.0
        assert np.all(u.data[1, :] == 0)
Example #11
0
    def test_bcs(self):
        """
        Tests application of an Operator consisting of multiple equations
        defined over different sub-regions, explicitly created through the
        use of :class:`SubDimension`s.
        """
        grid = Grid(shape=(20, 20))
        x, y = grid.dimensions
        t = grid.stepping_dim
        thickness = 4

        u = TimeFunction(name='u',
                         save=None,
                         grid=grid,
                         space_order=0,
                         time_order=1)

        xleft = SubDimension.left(name='xleft', parent=x, thickness=thickness)
        xi = SubDimension.middle(name='xi',
                                 parent=x,
                                 thickness_left=thickness,
                                 thickness_right=thickness)
        xright = SubDimension.right(name='xright',
                                    parent=x,
                                    thickness=thickness)

        yi = SubDimension.middle(name='yi',
                                 parent=y,
                                 thickness_left=thickness,
                                 thickness_right=thickness)

        t_in_centre = Eq(u[t + 1, xi, yi], 1)
        leftbc = Eq(u[t + 1, xleft, yi], u[t + 1, xleft + 1, yi] + 1)
        rightbc = Eq(u[t + 1, xright, yi], u[t + 1, xright - 1, yi] + 1)

        op = Operator([t_in_centre, leftbc, rightbc])

        op.apply(time_m=1, time_M=1)

        assert np.all(u.data[0, :, 0:thickness] == 0.)
        assert np.all(u.data[0, :, -thickness:] == 0.)
        assert all(
            np.all(u.data[0, i, thickness:-thickness] == (thickness + 1 - i))
            for i in range(thickness))
        assert all(
            np.all(u.data[0, -i, thickness:-thickness] == (thickness + 2 - i))
            for i in range(1, thickness + 1))
        assert np.all(u.data[0, thickness:-thickness,
                             thickness:-thickness] == 1.)
Example #12
0
    def test_iteration_property_vector(self, exprs, expected):
        """Tests detection of vector Iterations when using subdimensions."""
        grid = Grid(shape=(20, 20))
        x, y = grid.dimensions  # noqa
        t = grid.time_dim  # noqa

        # The leftmost 10 elements
        yleft = SubDimension(name='yleft',
                             grid=grid,
                             parent=y,
                             lower=0,
                             upper=-10)  # noqa

        u = TimeFunction(name='u',
                         grid=grid,
                         save=10,
                         time_order=0,
                         space_order=1)  # noqa

        # List comprehension would need explicit locals/globals mappings to eval
        for i, e in enumerate(list(exprs)):
            exprs[i] = eval(e)

        op = Operator(exprs)
        iterations = FindNodes(Iteration).visit(op)
        vectorizable = [i.dim.name for i in iterations if i.is_Vectorizable]
        assert set(vectorizable) == set(expected)
Example #13
0
def test_cache_blocking_structure_subdims():
    """
    Test that:

        * With local SubDimensions no-blocking is expected.
        * With non-local SubDimensions, blocking is expected.
    """
    grid = Grid(shape=(4, 4, 4))
    x, y, z = grid.dimensions
    t = grid.stepping_dim
    xl = SubDimension.left(name='xl', parent=x, thickness=4)

    f = TimeFunction(name='f', grid=grid)

    assert xl.local

    # Local SubDimension -> no blocking expected
    op = Operator(Eq(f[t + 1, xl, y, z], f[t, xl, y, z] + 1))
    assert len(op._func_table) == 0

    # Non-local SubDimension -> blocking expected
    op = Operator(Eq(f.forward, f + 1, subdomain=grid.interior))
    trees = retrieve_iteration_tree(op._func_table['bf0'].root)
    assert len(trees) == 1
    tree = trees[0]
    assert len(tree) == 5
    assert isinstance(tree[0].dim, BlockDimension) and tree[0].dim.root is x
    assert isinstance(tree[1].dim, BlockDimension) and tree[1].dim.root is y
    assert not isinstance(tree[2].dim, BlockDimension)
Example #14
0
    def test_symbolic_size(self):
        """Check the symbolic size of all possible SubDimensions is as expected."""
        grid = Grid(shape=(4,))
        x, = grid.dimensions
        thickness = 4

        xleft = SubDimension.left(name='xleft', parent=x, thickness=thickness)
        assert xleft.symbolic_size == xleft.thickness.left[0]

        xi = SubDimension.middle(name='xi', parent=x,
                                 thickness_left=thickness, thickness_right=thickness)
        assert xi.symbolic_size == (x.symbolic_max - x.symbolic_min -
                                    xi.thickness.left[0] - xi.thickness.right[0] + 1)

        xright = SubDimension.right(name='xright', parent=x, thickness=thickness)
        assert xright.symbolic_size == xright.thickness.right[0]
Example #15
0
    def test_nested_cache_blocking_structure_subdims(self, blocklevels):
        """
        Test that:

            * With non-local SubDimensions, nested blocking works fine when expected.
            * With non-local SubDimensions, hierarchical nested blocking works fine
            when expected.
        """
        grid = Grid(shape=(4, 4, 4))
        x, y, z = grid.dimensions
        xi, yi, zi = grid.interior.dimensions
        xl = SubDimension.left(name='xl', parent=x, thickness=4)

        f = TimeFunction(name='f', grid=grid)

        assert xl.local

        # Non-local SubDimension -> nested blocking can works as expected
        op = Operator(Eq(f.forward, f + 1, subdomain=grid.interior),
                      opt=('blocking', 'openmp', {
                          'par-nested': 0,
                          'blocklevels': blocklevels,
                          'par-collapse-ncores': 2,
                          'par-dynamic-work': 0
                      }))

        bns, _ = assert_blocking(op, {'i0x0_blk0'})

        trees = retrieve_iteration_tree(bns['i0x0_blk0'])
        assert len(trees) == 1
        tree = trees[0]
        assert len(tree) == 5 + (blocklevels - 1) * 2
        assert tree[0].dim.is_Incr and tree[0].dim.parent is xi and tree[
            0].dim.root is x
        assert tree[1].dim.is_Incr and tree[1].dim.parent is yi and tree[
            1].dim.root is y
        assert tree[2].dim.is_Incr and tree[2].dim.parent is tree[0].dim and\
            tree[2].dim.root is x
        assert tree[3].dim.is_Incr and tree[3].dim.parent is tree[1].dim and\
            tree[3].dim.root is y

        if blocklevels == 1:
            assert not tree[4].dim.is_Incr and tree[4].dim is zi and\
                tree[4].dim.parent is z
        elif blocklevels == 2:
            assert tree[3].dim.is_Incr and tree[3].dim.parent is tree[1].dim and\
                tree[3].dim.root is y
            assert tree[4].dim.is_Incr and tree[4].dim.parent is tree[2].dim and\
                tree[4].dim.root is x
            assert tree[5].dim.is_Incr and tree[5].dim.parent is tree[3].dim and\
                tree[5].dim.root is y
            assert not tree[6].dim.is_Incr and tree[6].dim is zi and\
                tree[6].dim.parent is z

        assert trees[0][0].pragmas[0].value ==\
            'omp for collapse(2) schedule(dynamic,1)'
        assert trees[0][2].pragmas[0].value == ('omp parallel for collapse(2) '
                                                'schedule(dynamic,1) '
                                                'num_threads(nthreads_nested)')
Example #16
0
    def test_subdimleft_notparallel(self):
        """
        Tests application of an Operator consisting of a subdimension
        defined over different sub-regions, explicitly created through the
        use of :class:`SubDimension`s.

        This tests that flow direction is not being automatically inferred
        from whether the subdimension is on the left or right boundary.
        """
        grid = Grid(shape=(20, 20))
        x, y = grid.dimensions
        t = grid.stepping_dim
        thickness = 4

        u = TimeFunction(name='u',
                         save=None,
                         grid=grid,
                         space_order=1,
                         time_order=0)

        xl = SubDimension.left(name='xl', parent=x, thickness=thickness)

        yi = SubDimension.middle(name='yi',
                                 parent=y,
                                 thickness_left=thickness,
                                 thickness_right=thickness)

        # Flows inward (i.e. forward) rather than outward
        eq = Eq(u[t + 1, xl, yi], u[t + 1, xl - 1, yi] + 1)

        op = Operator([eq])

        iterations = FindNodes(Iteration).visit(op)
        assert all(i.is_Affine and i.is_Sequential for i in iterations
                   if i.dim == xl)
        assert all(i.is_Affine and i.is_Parallel for i in iterations
                   if i.dim == yi)

        op.apply(time_m=1, time_M=1)

        assert all(
            np.all(u.data[0, :thickness, thickness + i] == [1, 2, 3, 4])
            for i in range(12))
        assert np.all(u.data[0, thickness:] == 0)
        assert np.all(u.data[0, :, thickness + 12:] == 0)
Example #17
0
def initialize_damp(damp, padsizes, spacing, fs=False):
    """
    Initialise damping field with an absorbing boundary layer.
    Includes basic constant Q setup (not interfaced yet) and assumes that
    the peak frequency is 1/(10 * spacing).

    Parameters
    ----------
    damp : Function
        The damping field for absorbing boundary condition.
    nbl : int
        Number of points in the damping layer.
    spacing :
        Grid spacing coefficient.
    mask : bool, optional
        whether the dampening is a mask or layer.
        mask => 1 inside the domain and decreases in the layer
        not mask => 0 inside the domain and increase in the layer
    """
    lqmin = np.log(.1)
    lqmax = np.log(100)
    w0 = 1 / (10 * np.max(spacing))

    z = damp.dimensions[-1]
    eqs = [Eq(damp, 1)]

    for (nbl, nbr), d in zip(padsizes, damp.dimensions):
        if not fs or d is not z:
            nbl = max(5, nbl - 10) if d is z else nbl
            # left
            dim_l = SubDimension.left(name='abc_%s_l' % d.name,
                                      parent=d,
                                      thickness=nbl)
            pos = Abs(dim_l - d.symbolic_min) / float(nbl)
            eqs.append(
                Eq(damp.subs({d: dim_l}), Min(damp.subs({d: dim_l}), pos)))
        # right
        dim_r = SubDimension.right(name='abc_%s_r' % d.name,
                                   parent=d,
                                   thickness=nbr)
        pos = Abs(d.symbolic_max - dim_r) / float(nbr)
        eqs.append(Eq(damp.subs({d: dim_r}), Min(damp.subs({d: dim_r}), pos)))

    eqs.append(Eq(damp, w0 / exp(lqmin + damp * (lqmax - lqmin))))
    Operator(eqs, name='initdamp')()
Example #18
0
    def test_bcs_basic(self):
        """
        Test MPI in presence of boundary condition loops. Here, no halo exchange
        is expected (as there is no stencil in the computed expression) but we
        check that:

            * the left BC loop is computed by the leftmost rank only
            * the right BC loop is computed by the rightmost rank only
        """
        grid = Grid(shape=(20, ))
        x = grid.dimensions[0]
        t = grid.stepping_dim

        thickness = 4

        u = TimeFunction(name='u', grid=grid, time_order=1)

        xleft = SubDimension.left(name='xleft', parent=x, thickness=thickness)
        xi = SubDimension.middle(name='xi',
                                 parent=x,
                                 thickness_left=thickness,
                                 thickness_right=thickness)
        xright = SubDimension.right(name='xright',
                                    parent=x,
                                    thickness=thickness)

        t_in_centre = Eq(u[t + 1, xi], 1)
        leftbc = Eq(u[t + 1, xleft], u[t + 1, xleft + 1] + 1)
        rightbc = Eq(u[t + 1, xright], u[t + 1, xright - 1] + 1)

        op = Operator([t_in_centre, leftbc, rightbc])

        op.apply(time_m=1, time_M=1)

        glb_pos_map = u.grid.distributor.glb_pos_map
        if LEFT in glb_pos_map[x]:
            assert np.all(u.data_ro_domain[0, thickness:] == 1.)
            assert np.all(
                u.data_ro_domain[0, :thickness] == range(thickness + 1, 1, -1))
        else:
            assert np.all(u.data_ro_domain[0, :-thickness] == 1.)
            assert np.all(
                u.data_ro_domain[0, -thickness:] == range(2, thickness + 2))
Example #19
0
def test_sub_dimension():
    di = SubDimension.middle('di', Dimension(name='d'), 1, 1)

    pkl_di = pickle.dumps(di)
    new_di = pickle.loads(pkl_di)

    assert di.name == new_di.name
    assert di.dtype == new_di.dtype
    assert di.parent == new_di.parent
    assert di._thickness == new_di._thickness
    assert di._interval == new_di._interval
Example #20
0
def initialize_damp(damp, padsizes, spacing, abc_type="damp", fs=False):
    """
    Initialize damping field with an absorbing boundary layer.

    Parameters
    ----------
    damp : Function
        The damping field for absorbing boundary condition.
    nbl : int
        Number of points in the damping layer.
    spacing :
        Grid spacing coefficient.
    mask : bool, optional
        whether the dampening is a mask or layer.
        mask => 1 inside the domain and decreases in the layer
        not mask => 0 inside the domain and increase in the layer
    """

    eqs = [Eq(damp, 1.0 if abc_type == "mask" else 0.0)]
    for (nbl, nbr), d in zip(padsizes, damp.dimensions):
        if not fs or d is not damp.dimensions[-1]:
            dampcoeff = 1.5 * np.log(1.0 / 0.001) / (nbl)
            # left
            dim_l = SubDimension.left(name='abc_%s_l' % d.name,
                                      parent=d,
                                      thickness=nbl)
            pos = Abs((nbl - (dim_l - d.symbolic_min) + 1) / float(nbl))
            val = dampcoeff * (pos - sin(2 * np.pi * pos) / (2 * np.pi))
            val = -val if abc_type == "mask" else val
            eqs += [Inc(damp.subs({d: dim_l}), val / d.spacing)]
        # right
        dampcoeff = 1.5 * np.log(1.0 / 0.001) / (nbr)
        dim_r = SubDimension.right(name='abc_%s_r' % d.name,
                                   parent=d,
                                   thickness=nbr)
        pos = Abs((nbr - (d.symbolic_max - dim_r) + 1) / float(nbr))
        val = dampcoeff * (pos - sin(2 * np.pi * pos) / (2 * np.pi))
        val = -val if abc_type == "mask" else val
        eqs += [Inc(damp.subs({d: dim_r}), val / d.spacing)]

    Operator(eqs, name='initdamp')()
Example #21
0
def test_nofission_as_unprofitable():
    """
    Test there's no fission if not gonna increase number of collapsable loops.
    """
    grid = Grid(shape=(20, 20))
    x, y = grid.dimensions
    t = grid.stepping_dim

    yl = SubDimension.left(name='yl', parent=y, thickness=4)
    yr = SubDimension.right(name='yr', parent=y, thickness=4)

    u = TimeFunction(name='u', grid=grid)

    eqns = [
        Eq(u.forward, u[t + 1, x, y + 1] + 1.).subs(y, yl),
        Eq(u.forward, u[t + 1, x, y - 1] + 1.).subs(y, yr)
    ]

    op = Operator(eqns, opt='fission')

    assert_structure(op, ['t,x,yl', 't,x,yr'], 't,x,yl,yr')
Example #22
0
def initialize_damp(damp, nbl, spacing, mask=False):
    """
    Initialise damping field with an absorbing boundary layer.

    Parameters
    ----------
    damp : Function
        The damping field for absorbing boundary condition.
    nbl : int
        Number of points in the damping layer.
    spacing :
        Grid spacing coefficient.
    mask : bool, optional
        whether the dampening is a mask or layer.
        mask => 1 inside the domain and decreases in the layer
        not mask => 0 inside the domain and increase in the layer
    """
    dampcoeff = 1.5 * np.log(1.0 / 0.001) / (40)

    eqs = [Eq(damp, 1.0)] if mask else []
    for d in damp.dimensions:
        # left
        dim_l = SubDimension.left(name='abc_%s_l' % d.name,
                                  parent=d,
                                  thickness=nbl)
        pos = Abs((nbl - (dim_l - d.symbolic_min) + 1) / float(nbl))
        val = dampcoeff * (pos - sin(2 * np.pi * pos) / (2 * np.pi))
        val = -val if mask else val
        eqs += [Inc(damp.subs({d: dim_l}), val / d.spacing)]
        # right
        dim_r = SubDimension.right(name='abc_%s_r' % d.name,
                                   parent=d,
                                   thickness=nbl)
        pos = Abs((nbl - (d.symbolic_max - dim_r) + 1) / float(nbl))
        val = dampcoeff * (pos - sin(2 * np.pi * pos) / (2 * np.pi))
        val = -val if mask else val
        eqs += [Inc(damp.subs({d: dim_r}), val / d.spacing)]

    # TODO: Figure out why yask doesn't like it with dse/dle
    Operator(eqs, name='initdamp', dse='noop', dle='noop')()
Example #23
0
    def test_index_mode_detection(self, indexed, expected):
        """
        Test detection of IterationInstance access modes (AFFINE vs IRREGULAR).

        Proper detection of access mode is a prerequisite to any sort of
        data dependence analysis.
        """
        grid = Grid(shape=(4, 4, 4))
        x, y, z = grid.dimensions  # noqa

        sx = SubDimension.middle('sx', x, 1, 1)  # noqa

        u = Function(name='u', grid=grid)  # noqa
        c = Constant(name='c')  # noqa
        sc = Scalar(name='sc', is_const=True)  # noqa
        s = Scalar(name='s')  # noqa

        ii = IterationInstance(eval(indexed))
        assert ii.index_mode == expected
Example #24
0
    def test_index_mode_detection(self, indexed, expected):
        """
        Test detection of IterationInstance access modes (AFFINE vs IRREGULAR).

        Proper detection of access mode is a prerequisite to any sort of
        data dependence analysis.
        """
        grid = Grid(shape=(4, 4, 4))
        x, y, z = grid.dimensions  # noqa

        sx = SubDimension.middle('sx', x, 1, 1)  # noqa

        u = Function(name='u', grid=grid)  # noqa
        c = Constant(name='c')  # noqa
        sc = Scalar(name='sc', is_const=True)  # noqa
        s = Scalar(name='s')  # noqa

        ii = IterationInstance(eval(indexed))
        assert ii.index_mode == expected
Example #25
0
def test_cache_blocking_structure_subdims():
    """
    Test that:

        * With local SubDimensions no-blocking is expected.
        * With non-local SubDimensions, blocking is expected.
    """
    grid = Grid(shape=(4, 4, 4))
    x, y, z = grid.dimensions
    xi, yi, zi = grid.interior.dimensions
    t = grid.stepping_dim
    xl = SubDimension.left(name='xl', parent=x, thickness=4)

    f = TimeFunction(name='f', grid=grid)

    assert xl.local

    # Local SubDimension -> no blocking expected
    op = Operator(Eq(f[t + 1, xl, y, z], f[t, xl, y, z] + 1))

    assert_blocking(op, {})

    # Non-local SubDimension -> blocking expected
    op = Operator(Eq(f.forward, f + 1, subdomain=grid.interior))

    bns, _ = assert_blocking(op, {'i0x0_blk0'})

    trees = retrieve_iteration_tree(bns['i0x0_blk0'])
    tree = trees[0]
    assert len(tree) == 5
    assert tree[
        0].dim.is_Incr and tree[0].dim.parent is xi and tree[0].dim.root is x
    assert tree[
        1].dim.is_Incr and tree[1].dim.parent is yi and tree[1].dim.root is y
    assert tree[2].dim.is_Incr and tree[2].dim.parent is tree[0].dim and\
        tree[2].dim.root is x
    assert tree[3].dim.is_Incr and tree[3].dim.parent is tree[1].dim and\
        tree[3].dim.root is y
    assert not tree[
        4].dim.is_Incr and tree[4].dim is zi and tree[4].dim.parent is z
Example #26
0
def test_sub_dimension_cache():
    """
    Test that SubDimensions with same name but different attributes do not
    alias to the same SubDimension. Conversely, if the name and the attributes
    are the same, they must alias to the same SubDimension.
    """
    x = Dimension('x')
    xi0 = SubDimension.middle('xi', x, 1, 1)
    xi1 = SubDimension.middle('xi', x, 1, 1)
    assert xi0 is xi1

    xl0 = SubDimension.left('xl', x, 2)
    xl1 = SubDimension.left('xl', x, 2)
    assert xl0 is xl1
    xl2asxi = SubDimension.left('xi', x, 2)
    assert xl2asxi is not xl1
    assert xl2asxi is not xi1

    xr0 = SubDimension.right('xr', x, 1)
    xr1 = SubDimension.right('xr', x, 1)
    assert xr0 is xr1
Example #27
0
    def test_sub_dimension(self):
        """
        Test that SubDimensions with same name but different attributes do not
        alias to the same SubDimension. Conversely, if the name and the attributes
        are the same, they must alias to the same SubDimension.
        """
        x = Dimension('x')
        xi0 = SubDimension.middle('xi', x, 1, 1)
        xi1 = SubDimension.middle('xi', x, 1, 1)
        assert xi0 is xi1

        xl0 = SubDimension.left('xl', x, 2)
        xl1 = SubDimension.left('xl', x, 2)
        assert xl0 is xl1
        xl2asxi = SubDimension.left('xi', x, 2)
        assert xl2asxi is not xl1
        assert xl2asxi is not xi1

        xr0 = SubDimension.right('xr', x, 1)
        xr1 = SubDimension.right('xr', x, 1)
        assert xr0 is xr1
Example #28
0
    def test_subdim_middle(self):
        """
        Tests that instantiating SubDimensions using the classmethod
        constructors works correctly.
        """
        grid = Grid(shape=(4, 4, 4))
        x, y, z = grid.dimensions
        t = grid.stepping_dim  # noqa

        u = TimeFunction(name='u', grid=grid)  # noqa
        xi = SubDimension.middle(name='xi', parent=x,
                                 thickness_left=1,
                                 thickness_right=1)
        eqs = [Eq(u.forward, u + 1)]
        eqs = [e.subs(x, xi) for e in eqs]

        op = Operator(eqs)

        u.data[:] = 1.0
        op.apply(time_M=1)
        assert np.all(u.data[1, 0, :, :] == 1)
        assert np.all(u.data[1, -1, :, :] == 1)
        assert np.all(u.data[1, 1:3, :, :] == 2)
Example #29
0
    def test_nontrivial_operator(self):
        """
        Test MPI in a non-trivial scenario: ::

            * 9 processes logically organised in a 3x3 cartesian grid (as opposed to
              most tests in this module, which only use 2 or 4 processed);
            * star-like stencil expression;
            * non-trivial Higdon-like BCs;
            * simultaneous presence of TimeFunction(grid), Function(grid), and
              Function(dimensions)
        """
        size_x, size_y = 9, 9
        tkn = 2

        # Grid and Dimensions
        grid = Grid(shape=(
            size_x,
            size_y,
        ))
        x, y = grid.dimensions
        t = grid.stepping_dim

        # SubDimensions to implement BCs
        xl, yl = [SubDimension.left('%sl' % d.name, d, tkn) for d in [x, y]]
        xi, yi = [
            SubDimension.middle('%si' % d.name, d, tkn, tkn) for d in [x, y]
        ]
        xr, yr = [SubDimension.right('%sr' % d.name, d, tkn) for d in [x, y]]

        # Functions
        u = TimeFunction(name='f', grid=grid)
        m = Function(name='m', grid=grid)
        c = Function(name='c', grid=grid, dimensions=(x, ), shape=(size_x, ))

        # Data initialization
        u.data_with_halo[:] = 0.
        m.data_with_halo[:] = 1.
        c.data_with_halo[:] = 0.

        # Equations
        c_init = Eq(c, 1.)
        eqn = Eq(u[t + 1, xi, yi], u[t, xi, yi] + m[xi, yi] + c[xi] + 1.)
        bc_left = Eq(u[t + 1, xl, yi], u[t + 1, xl + 1, yi] + 1.)
        bc_right = Eq(u[t + 1, xr, yi], u[t + 1, xr - 1, yi] + 1.)
        bc_top = Eq(u[t + 1, xi, yl], u[t + 1, xi, yl + 1] + 1.)
        bc_bottom = Eq(u[t + 1, xi, yr], u[t + 1, xi, yr - 1] + 1.)

        op = Operator([c_init, eqn, bc_left, bc_right, bc_top, bc_bottom])
        op.apply(time=0)

        # Expected (global view):
        # 0 0 5 5 5 5 5 0 0
        # 0 0 4 4 4 4 4 0 0
        # 5 4 3 3 3 3 3 4 5
        # 5 4 3 3 3 3 3 4 5
        # 5 4 3 3 3 3 3 4 5
        # 5 4 3 3 3 3 3 4 5
        # 0 0 4 4 4 4 4 0 0
        # 0 0 5 5 5 5 5 0 0

        assert np.all(u.data_ro_domain[0] == 0)  # The write occures at t=1

        glb_pos_map = u.grid.distributor.glb_pos_map
        # Check cornes
        if LEFT in glb_pos_map[x] and LEFT in glb_pos_map[y]:
            assert np.all(
                u.data_ro_domain[1] == [[0, 0, 5], [0, 0, 4], [5, 4, 3]])
        elif LEFT in glb_pos_map[x] and RIGHT in glb_pos_map[y]:
            assert np.all(
                u.data_ro_domain[1] == [[5, 0, 0], [4, 0, 0], [3, 4, 5]])
        elif RIGHT in glb_pos_map[x] and LEFT in glb_pos_map[y]:
            assert np.all(
                u.data_ro_domain[1] == [[5, 4, 3], [0, 0, 4], [0, 0, 5]])
        elif RIGHT in glb_pos_map[x] and RIGHT in glb_pos_map[y]:
            assert np.all(
                u.data_ro_domain[1] == [[3, 4, 5], [4, 0, 0], [5, 0, 0]])
        # Check sides
        if not glb_pos_map[x] and LEFT in glb_pos_map[y]:
            assert np.all(
                u.data_ro_domain[1] == [[5, 4, 3], [5, 4, 3], [5, 4, 3]])
        elif not glb_pos_map[x] and RIGHT in glb_pos_map[y]:
            assert np.all(
                u.data_ro_domain[1] == [[3, 4, 5], [3, 4, 5], [3, 4, 5]])
        elif LEFT in glb_pos_map[x] and not glb_pos_map[y]:
            assert np.all(
                u.data_ro_domain[1] == [[5, 5, 5], [4, 4, 4], [3, 3, 3]])
        elif RIGHT in glb_pos_map[x] and not glb_pos_map[y]:
            assert np.all(
                u.data_ro_domain[1] == [[3, 3, 3], [4, 4, 4], [5, 5, 5]])
        # Check center
        if not glb_pos_map[x] and not glb_pos_map[y]:
            assert np.all(u.data_ro_domain[1] == 3)