Example #1
0
    def test_dynamic_nthreads(self):
        grid = Grid(shape=(16, 16, 16))
        f = TimeFunction(name='f', grid=grid)
        sf = SparseTimeFunction(name='sf', grid=grid, npoint=1, nt=5)

        eqns = [Eq(f.forward, f + 1)]
        eqns += sf.interpolate(f)

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

        parregions = FindNodes(OmpRegion).visit(op)
        assert len(parregions) == 2

        # Check suitable `num_threads` appear in the generated code
        # Not very elegant, but it does the trick
        assert 'num_threads(nthreads)' in str(parregions[0].header[0])
        assert 'num_threads(nthreads_nonaffine)' in str(
            parregions[1].header[0])

        # Check `op` accepts the `nthreads*` kwargs
        op.apply(time=0)
        op.apply(time_m=1, time_M=1, nthreads=4)
        op.apply(time_m=1, time_M=1, nthreads=4, nthreads_nonaffine=2)
        op.apply(time_m=1, time_M=1, nthreads_nonaffine=2)
        assert np.all(f.data[0] == 2.)

        # Check the actual value assumed by `nthreads` and `nthreads_nonaffine`
        assert op.arguments(time=0, nthreads=123)['nthreads'] == 123
        assert op.arguments(
            time=0, nthreads_nonaffine=100)['nthreads_nonaffine'] == 100
Example #2
0
    def test_basic(self):
        grid = Grid(shape=(3, 3, 3))

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

        op = Operator(Eq(u.forward, u + 1), dle=('blocking', 'openmp'))

        # Does it compile? Honoring the OpenMP specification isn't trivial
        assert op.cfunction

        # Does it produce the right result
        op.apply(t_M=9)
        assert np.all(u.data[0] == 10)

        # Try again but this time supplying specific values for the num_threads
        u.data[:] = 0.
        op.apply(t_M=9, nthreads=1, nthreads_nested=2)
        assert np.all(u.data[0] == 10)
        assert op.arguments(t_M=9, nthreads_nested=2)['nthreads_nested'] == 2
        # Same as above, but with the alias
        assert op.arguments(t_M=9, nthreads1=2)['nthreads_nested'] == 2

        iterations = FindNodes(Iteration).visit(op._func_table['bf0'])
        assert iterations[0].pragmas[
            0].value == 'omp for collapse(1) schedule(dynamic,1)'
        assert iterations[2].pragmas[0].value == (
            'omp parallel for collapse(1) '
            'schedule(dynamic,1) '
            'num_threads(nthreads_nested)')
Example #3
0
    def test_argument_derivation_order(self, nt=100):
        """ Ensure the precedence order of arguments is respected
        Defaults < (overriden by) Tensor Arguments < Dimensions < Scalar Arguments
        """
        i, j, k = dimify('i j k')
        shape = (10, 10, 10)
        grid = Grid(shape=shape, dimensions=(i, j, k))
        a = Function(name='a', grid=grid)
        b = TimeFunction(name='b', grid=grid, save=nt)
        time = b.indices[0]
        op = Operator(Eq(b, a))

        # Simple case, same as that tested above.
        # Repeated here for clarity of further tests.
        op_arguments = op.arguments()
        assert(op_arguments[time.min_name] == 0)
        assert(op_arguments[time.max_name] == nt-1)

        # Providing a tensor argument should infer the dimension size from its shape
        b1 = TimeFunction(name='b1', grid=grid, save=nt+1)
        op_arguments = op.arguments(b=b1)
        assert(op_arguments[time.min_name] == 0)
        assert(op_arguments[time.max_name] == nt)

        # Providing a dimension size explicitly should override the automatically inferred
        op_arguments = op.arguments(b=b1, time=nt - 1)
        assert(op_arguments[time.min_name] == 0)
        assert(op_arguments[time.max_name] == nt - 1)

        # Providing a scalar argument explicitly should override the automatically
        # inferred
        op_arguments = op.arguments(b=b1, time_M=nt - 2)
        assert(op_arguments[time.min_name] == 0)
        assert(op_arguments[time.max_name] == nt - 2)
Example #4
0
    def check_deviceid(self):
        grid = Grid(shape=(6, 6))

        u = TimeFunction(name='u', grid=grid, space_order=2, save=10)

        op = Operator(Eq(u.forward, u.dx + 1))

        deviceid = self.get_param(op, DeviceID)
        assert deviceid is not None
        assert op.arguments()[deviceid.name] == -1
        assert op.arguments(deviceid=0)[deviceid.name] == 0
Example #5
0
    def test_default_composite_functions(self):
        """
        Test the default argument derivation for composite functions.
        """
        grid = Grid(shape=(5, 6, 7))
        f = TimeFunction(name='f', grid=grid)
        s = SparseFunction(name='s', grid=grid, npoint=3, nt=4)
        s.coordinates.data[:, 0] = np.arange(0., 3.)
        s.coordinates.data[:, 1] = np.arange(1., 4.)
        s.coordinates.data[:, 2] = np.arange(2., 5.)
        op = Operator(s.interpolate(f))

        expected = {
            's': s.data,
            's_coords': s.coordinates.data,
            # Default dimensions of the sparse data
            'p_size': 3,
            'p_s': 0,
            'p_e': 3,
            'd_size': 3,
            'p_s': 0,
            'p_e': 3,
            'time_size': 4,
            'time_s': 0,
            'time_e': 4,
        }
        self.verify_arguments(op.arguments(), expected)
Example #6
0
    def test_default_functions(self):
        """
        Test the default argument derivation for functions.
        """
        grid = Grid(shape=(5, 6, 7))
        f = TimeFunction(name='f', grid=grid)
        g = Function(name='g', grid=grid)
        op = Operator(Eq(g, g + f))

        expected = {
            'x_size': 5,
            'x_s': 0,
            'x_e': 5,
            'y_size': 6,
            'y_s': 0,
            'y_e': 6,
            'z_size': 7,
            'z_s': 0,
            'z_e': 7,
            'f': f.data,
            'g': g.data,
        }
        self.verify_arguments(op.arguments(time=4), expected)
        exp_parameters = [
            'f', 'g', 'x_s', 'x_e', 'x_size', 'y_s', 'y_e', 'y_size', 'z_s',
            'z_e', 'z_size', 'time_s', 'time_e'
        ]
        self.verify_parameters(op.parameters, exp_parameters)
Example #7
0
    def test_basic(self):
        grid = Grid(shape=(3, 3, 3))

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

        op = Operator(Eq(u.forward, u + 1),
                      opt=('blocking', 'openmp', {
                          'par-nested': 0,
                          'par-collapse-ncores': 10000,
                          'par-dynamic-work': 0
                      }))

        # Does it compile? Honoring the OpenMP specification isn't trivial
        assert op.cfunction

        # Does it produce the right result
        op.apply(t_M=9)
        assert np.all(u.data[0] == 10)

        # Try again but this time supplying specific values for the num_threads
        u.data[:] = 0.
        op.apply(t_M=9, nthreads=1, nthreads_nested=2)
        assert np.all(u.data[0] == 10)
        assert op.arguments(t_M=9, nthreads_nested=2)['nthreads_nested'] == 2

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

        iterations = FindNodes(Iteration).visit(bns['x0_blk0'])
        assert iterations[0].pragmas[
            0].value == 'omp for collapse(1) schedule(dynamic,1)'
        assert iterations[2].pragmas[0].value == (
            'omp parallel for collapse(1) '
            'schedule(dynamic,1) '
            'num_threads(nthreads_nested)')
Example #8
0
    def test_override_function_subrange(self):
        """
        Test runtime start/end override for :class:`Function` dimensions.
        """
        grid = Grid(shape=(5, 6, 7))
        g = Function(name='g', grid=grid)

        op = Operator(Eq(g, 1.))
        args = {'x_s': 1, 'x_e': 3, 'y_s': 2, 'y_e': 4, 'z_s': 3, 'z_e': 5}
        arguments = op.arguments(**args)
        expected = {
            'x_size': 5,
            'x_s': 1,
            'x_e': 3,
            'y_size': 6,
            'y_s': 2,
            'y_e': 4,
            'z_size': 7,
            'z_s': 3,
            'z_e': 5,
            'g': g.data
        }
        self.verify_arguments(arguments, expected)
        # Verify execution
        op(**args)
        mask = np.ones((5, 6, 7), dtype=np.bool)
        mask[1:3, 2:4, 3:5] = False
        assert (g.data[mask] == 0.).all()
        assert (g.data[1:3, 2:4, 3:5] == 1.).all()
Example #9
0
    def test_override_composite_data(self):
        i, j = dimify('i j')
        grid = Grid(shape=(10, 10), dimensions=(i, j))
        original_coords = (1., 1.)
        new_coords = (2., 2.)
        p_dim = Dimension('p_src')
        u = TimeFunction(name='u', grid=grid, time_order=2, space_order=2)
        src1 = SparseFunction(name='src1',
                              grid=grid,
                              dimensions=[time, p_dim],
                              npoint=1,
                              nt=10,
                              coordinates=original_coords)
        src2 = SparseFunction(name='src1',
                              grid=grid,
                              dimensions=[time, p_dim],
                              npoint=1,
                              nt=10,
                              coordinates=new_coords)
        op = Operator(src1.inject(u, src1))

        # Move the source from the location where the setup put it so we can test
        # whether the override picks up the original coordinates or the changed ones

        # Operator.arguments() returns a tuple of (data, dimension_sizes)
        args = op.arguments(src1=src2)[0]
        arg_name = src1.name + "_coords"
        assert (np.array_equal(args[arg_name], np.asarray((new_coords, ))))
Example #10
0
    def test_override_timefunction_subrange(self):
        """
        Test runtime start/end overrides for :class:`TimeFunction` dimensions.
        """
        grid = Grid(shape=(5, 6, 7))
        f = TimeFunction(name='f', grid=grid, time_order=0)

        # Suppress DLE to work around a know bug with GCC and OpenMP:
        # https://github.com/opesci/devito/issues/320
        op = Operator(Eq(f, 1.), dle=None)
        # TODO: Currently we require the `time` subrange to be set
        # explicitly. Ideally `t` would directly alias with `time`,
        # but this seems broken currently.
        args = {'x_m': 1, 'x_M': 3, 'y_m': 2, 'y_M': 4,
                'z_m': 3, 'z_M': 5, 't_m': 1, 't_M': 4}
        arguments = op.arguments(**args)
        expected = {
            'x_size': 5, 'x_m': 1, 'x_M': 3,
            'y_size': 6, 'y_m': 2, 'y_M': 4,
            'z_size': 7, 'z_m': 3, 'z_M': 5,
            'time_m': 1, 'time_M': 4,
            'f': f.data_allocated
        }
        self.verify_arguments(arguments, expected)
        # Verify execution
        op(**args)
        mask = np.ones((1, 5, 6, 7), dtype=np.bool)
        mask[:, 1:4, 2:5, 3:6] = False
        assert (f.data[mask] == 0.).all()
        assert (f.data[:, 1:4, 2:5, 3:6] == 1.).all()
Example #11
0
    def test_override_sparse_data_fix_dim(self):
        """
        Ensure the arguments are derived correctly for an input SparseFunction.
        The dimensions are forced to be the same in this case to verify
        the aliasing on the SparseFunction name.
        """
        grid = Grid(shape=(10, 10))
        time = grid.time_dim

        u = TimeFunction(name='u', grid=grid, time_order=2, space_order=2)

        original_coords = (1., 1.)
        new_coords = (2., 2.)
        p_dim = Dimension(name='p_src')
        src1 = SparseTimeFunction(name='src1', grid=grid, dimensions=[time, p_dim], nt=10,
                                  npoint=1, coordinates=original_coords, time_order=2)
        src2 = SparseTimeFunction(name='src2', grid=grid, dimensions=[time, p_dim],
                                  npoint=1, nt=10, coordinates=new_coords, time_order=2)
        op = Operator(src1.inject(u, src1))

        # Move the source from the location where the setup put it so we can test
        # whether the override picks up the original coordinates or the changed ones

        args = op.arguments(src1=src2, time=0)
        arg_name = src1.name + "_coords"
        assert(np.array_equal(args[arg_name], np.asarray((new_coords,))))
Example #12
0
    def test_override_function_size(self):
        """
        Test runtime size overrides for :class:`Function` dimensions.

        Note: The current behaviour for size-only arguments seems
        ambiguous (eg. op(x=3, y=4), as it sets `dim_size` as well as
        `dim_end`. Since `dim_size` is used for the cast, we can get
        garbage results if it does not agree with the shape of the
        provided data. This should error out, or potentially we could
        set the corresponding size, while aliasing `dim` to `dim_e`?

        The same should be tested for :class:`TimeFunction` once fixed.
        """
        grid = Grid(shape=(5, 6, 7))
        g = Function(name='g', grid=grid)

        op = Operator(Eq(g, 1.))
        args = {'x': 3, 'y': 4, 'z': 5}
        arguments = op.arguments(**args)
        expected = {
            'x_size': 5, 'x_m': 0, 'x_M': 3,
            'y_size': 6, 'y_m': 0, 'y_M': 4,
            'z_size': 7, 'z_m': 0, 'z_M': 5,
            'g': g.data_allocated
        }
        self.verify_arguments(arguments, expected)
        # Verify execution
        op(**args)
        assert (g.data[4:] == 0.).all()
        assert (g.data[:, 5:] == 0.).all()
        assert (g.data[:, :, 6:] == 0.).all()
        assert (g.data[:4, :5, :6] == 1.).all()
Example #13
0
    def test_devicerm(self):
        grid = Grid(shape=(6, 6))

        u = TimeFunction(name='u', grid=grid, space_order=2)
        f = Function(name='f', grid=grid)

        op = Operator(Eq(u.forward, u.dx + f))

        devicerm = self.get_param(op, DeviceRM)
        assert devicerm is not None
        assert devicerm.data == 1  # Always evict, by default
        assert op.arguments(time_M=2)[devicerm.name] == 1
        assert op.arguments(time_M=2, devicerm=0)[devicerm.name] == 0
        assert op.arguments(time_M=2, devicerm=1)[devicerm.name] == 1
        assert op.arguments(time_M=2, devicerm=224)[devicerm.name] == 1
        assert op.arguments(time_M=2, devicerm=True)[devicerm.name] == 1
        assert op.arguments(time_M=2, devicerm=False)[devicerm.name] == 0
Example #14
0
    def test_dynamic_nthreads(self):
        grid = Grid(shape=(16, 16, 16))
        f = TimeFunction(name='f', grid=grid)

        op = Operator(Eq(f.forward, f + 1.), dle='openmp')

        # Check num_threads appears in the generated code
        # Not very elegant, but it does the trick
        assert 'num_threads(nthreads)' in str(op)

        # Check `op` accepts the `nthreads` kwarg
        op.apply(time=0)
        op.apply(time_m=1, time_M=1, nthreads=4)
        assert np.all(f.data[0] == 2.)

        # Check the actual value assumed by `nthreads`
        assert op.arguments(time=0)['nthreads'] == NThreads.default_value()
        assert op.arguments(time=0, nthreads=123)['nthreads'] == 123  # user supplied
Example #15
0
    def test_dynamic_nthreads(self):
        grid = Grid(shape=(16, 16, 16))
        f = TimeFunction(name='f', grid=grid)

        op = Operator(Eq(f.forward, f + 1.), dle='openmp')

        # Check num_threads appears in the generated code
        # Not very elegant, but it does the trick
        assert 'num_threads(nthreads)' in str(op)

        # Check `op` accepts the `nthreads` kwarg
        op.apply(time=0)
        op.apply(time_m=1, time_M=1, nthreads=4)
        assert np.all(f.data[0] == 2.)

        # Check the actual value assumed by `nthreads`
        assert op.arguments(time=0)['nthreads'] == NThreads.default_value()
        assert op.arguments(time=0,
                            nthreads=123)['nthreads'] == 123  # user supplied
Example #16
0
    def test_dimension_size_infer(self, nt=100):
        """Test that the dimension sizes are being inferred correctly"""
        grid = Grid(shape=(3, 5, 7))
        a = Function(name='a', grid=grid)
        b = TimeFunction(name='b', grid=grid, save=nt)
        op = Operator(Eq(b, a))

        time = b.indices[0]
        op_arguments = op.arguments()
        assert(op_arguments[time.min_name] == 0)
        assert(op_arguments[time.max_name] == nt-1)
Example #17
0
    def test_dimension_size_infer(self, nt=100):
        """Test that the dimension sizes are being inferred correctly"""
        i, j, k = dimify('i j k')
        shape = tuple([d.size for d in [i, j, k]])
        a = DenseData(name='a', shape=shape).indexed
        b = TimeData(name='b', shape=shape, save=True, time_dim=nt).indexed
        eqn = Eq(b[time, x, y, z], a[x, y, z])
        op = Operator(eqn)

        _, op_dim_sizes = op.arguments()
        assert(op_dim_sizes[time.name] == nt)
Example #18
0
class Processing(object):

    def setup(self):
        grid = Grid(shape=(5, 5, 5))

        funcs = [Function(name='f%d' % n, grid=grid) for n in range(30)]
        tfuncs = [TimeFunction(name='u%d' % n, grid=grid) for n in range(30)]
        stfuncs = [SparseTimeFunction(name='su%d' % n, grid=grid, npoint=1, nt=100)
                   for n in range(30)]
        v = TimeFunction(name='v', grid=grid, space_order=2)

        eq = Eq(v.forward, v.laplace + sum(funcs) + sum(tfuncs) + sum(stfuncs),
                subdomain=grid.interior)

        self.op = Operator(eq, opt='noop')

        # Allocate data, populate cached properties, etc.
        self.op.arguments(time_M=98)

    def time_processing(self):
        self.op.arguments(time_M=98)
Example #19
0
    def test_npthreads(self):
        nt = 10
        async_degree = 5
        grid = Grid(shape=(300, 300, 300))

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

        eqns = [Eq(u.forward, u + 1), Eq(usave, u.forward)]

        op = Operator(eqns,
                      opt=('buffering', 'tasking', 'orchestrate', {
                          'buf-async-degree': async_degree
                      }))

        npthreads0 = self.get_param(op, NPThreads)
        assert op.arguments(time_M=2)[npthreads0.name] == 1
        assert op.arguments(time_M=2, npthreads0=4)[npthreads0.name] == 4
        # Cannot provide a value larger than the thread pool size
        with pytest.raises(InvalidArgument):
            assert op.arguments(time_M=2, npthreads0=5)
Example #20
0
 def test_dimension_offset_adjust(self, nt=100):
     """Test that the dimension sizes are being inferred correctly"""
     i, j, k = dimify('i j k')
     shape = (10, 10, 10)
     grid = Grid(shape=shape, dimensions=(i, j, k))
     a = Function(name='a', grid=grid).indexed
     b = TimeFunction(name='b', grid=grid, save=nt)
     time = b.indices[0]
     eqn = Eq(b.indexed[time + 1, i, j, k], b.indexed[time - 1, i, j, k]
              + b.indexed[time, i, j, k] + a[i, j, k])
     op = Operator(eqn)
     op_arguments = op.arguments(time=nt-10)
     assert(op_arguments[time.min_name] == 1)
     assert(op_arguments[time.max_name] == nt - 10)
Example #21
0
    def test_argument_derivation_order(self, nt=100):
        """ Ensure the precedence order of arguments is respected
        Defaults < (overriden by) Tensor Arguments < Dimensions < Scalar Arguments
        """
        i, j, k = dimify('i j k')
        shape = (10, 10, 10)
        grid = Grid(shape=shape, dimensions=(i, j, k))
        a = Function(name='a', grid=grid).indexed
        b_function = TimeFunction(name='b', grid=grid, save=True, time_dim=nt)
        b = b_function.indexed
        time = b_function.indices[0]
        b1 = TimeFunction(name='b1', grid=grid, save=True,
                          time_dim=nt + 1).indexed
        eqn = Eq(b[time, i, j, k], a[i, j, k])
        op = Operator(eqn)

        # Simple case, same as that tested above.
        # Repeated here for clarity of further tests.
        op_arguments, _ = op.arguments()
        assert (op_arguments[time.start_name] == 0)
        assert (op_arguments[time.end_name] == nt)

        # Providing a tensor argument should infer the dimension size from its shape
        op_arguments, _ = op.arguments(b=b1)
        assert (op_arguments[time.start_name] == 0)
        assert (op_arguments[time.end_name] == nt + 1)

        # Providing a dimension size explicitly should override the automatically inferred
        op_arguments, _ = op.arguments(b=b1, time=nt - 1)
        assert (op_arguments[time.start_name] == 0)
        assert (op_arguments[time.end_name] == nt - 1)

        # Providing a scalar argument explicitly should override the automatically\
        # inferred
        op_arguments, _ = op.arguments(b=b1, time=nt - 1, time_e=nt - 2)
        assert (op_arguments[time.start_name] == 0)
        assert (op_arguments[time.end_name] == nt - 2)
Example #22
0
 def test_explicit_run(self):
     time_dim = 6
     grid = Grid(shape=(11, 11))
     a = TimeFunction(name='a', grid=grid, time_order=1, save=time_dim)
     eqn = Eq(a.forward, a + 1.)
     op = Operator(eqn)
     assert isinstance(op, OperatorForeign)
     args = OrderedDict(op.arguments())
     assert args['a'] is None
     # Emulate data feeding from outside
     array = np.ndarray(shape=a.shape, dtype=np.float32)
     array.fill(0.0)
     args['a'] = array
     op.cfunction(*list(args.values()))
     assert all(np.allclose(args['a'][i], i) for i in range(time_dim))
Example #23
0
 def test_arg_offset_adjust(self, nt=100):
     """Test that the dimension sizes are being inferred correctly"""
     i, j, k = dimify('i j k')
     shape = (10, 10, 10)
     grid = Grid(shape=shape, dimensions=(i, j, k))
     a = Function(name='a', grid=grid).indexed
     b = TimeFunction(name='b', grid=grid, save=True, time_dim=nt)
     time = b.indices[0]
     eqn = Eq(
         b.indexed[time + 1, i, j, k], b.indexed[time - 1, i, j, k] +
         b.indexed[time, i, j, k] + a[i, j, k])
     op = Operator(eqn)
     args = {time.end_name: nt - 10}
     op_arguments, _ = op.arguments(**args)
     assert (op_arguments[time.start_name] == 0)
     assert (op_arguments[time.end_name] == nt - 8)
Example #24
0
 def test_code(self):
     shape = (11, 11)
     a = TimeData(name='a',
                  shape=shape,
                  time_order=1,
                  time_dim=6,
                  save=True)
     eqn = Eq(a.forward, a + 1.)
     b = TimeData(name='a',
                  shape=shape,
                  time_order=1,
                  time_dim=6,
                  save=True)
     eqn2 = Eq(b.forward, b + 1.)
     op = Operator(eqn)
     op()
     op2 = Operator(eqn2, external=True)
     arg, _ = op.arguments(a=b)
     op2.cfunction(*list(arg.values()))
     assert (np.allclose(a.data[:], b.data[:]))
Example #25
0
    def test_mpi(self):
        # Shape chosen to get a source in multiple ranks
        shape = (91, 91)
        grid = Grid(shape=shape)
        x, y = grid.dimensions
        #  because we interpolate across 2 neighbouring points in each dimension
        r = 2

        nt = 10

        # NOTE: halo on function (space_order//2?) must be at least >= r
        m = TimeFunction(name="m",
                         grid=grid,
                         space_order=4,
                         save=None,
                         time_order=1)

        m.data[:] = 0.0
        m.data[:, 40, 40] = 1.0
        m.data[:, 50, 50] = 1.0

        # only rank 0 is allowed to have points
        if grid.distributor.myrank == 0:
            # A single dipole source - so two rows, one column
            matrix = scipy.sparse.coo_matrix(
                np.array([[1], [-1]], dtype=np.float32))
        else:
            matrix = scipy.sparse.coo_matrix((0, 0), dtype=np.float32)

        sf = MatrixSparseTimeFunction(name="s",
                                      grid=grid,
                                      r=r,
                                      matrix=matrix,
                                      nt=nt)

        if grid.distributor.myrank == 0:
            # First component of the dipole at 40, 40
            sf.gridpoints.data[0, 0] = 40
            sf.gridpoints.data[0, 1] = 40
            sf.interpolation_coefficients[x].data[0, 0] = 1.0
            sf.interpolation_coefficients[x].data[0, 1] = 2.0
            sf.interpolation_coefficients[y].data[0, 0] = 1.0
            sf.interpolation_coefficients[y].data[0, 1] = 2.0
            sf.gridpoints.data[1, 0] = 50
            sf.gridpoints.data[1, 1] = 50
            sf.interpolation_coefficients[x].data[1, 0] = 2.0
            sf.interpolation_coefficients[x].data[1, 1] = 2.0
            sf.interpolation_coefficients[y].data[1, 0] = 2.0
            sf.interpolation_coefficients[y].data[1, 1] = 2.0

        op = Operator(sf.interpolate(m))
        sf.manual_scatter()
        args = op.arguments(time_m=0, time_M=9)
        print("rank %d: %s" % (grid.distributor.myrank, str(args)))
        op.apply(time_m=0, time_M=0)
        sf.manual_gather()

        for i in range(grid.distributor.nprocs):
            print("==== from rank %d" % i)
            if i == grid.distributor.myrank:
                print(repr(sf.data))
            grid.distributor.comm.Barrier()

        if grid.distributor.myrank == 0:
            assert sf.data[0,
                           0] == -3.0  # 1 * (1 * 1) * 1 + (-1) * (2 * 2) * 1