Ejemplo n.º 1
0
def fwi_gradient(m_in):
    # Create symbols to hold the gradient and residual
    grad = Function(name="grad", grid=model.grid)
    residual = Receiver(name='rec',
                        grid=model.grid,
                        time_range=geometry.time_axis,
                        coordinates=geometry.rec_positions)
    objective = 0.

    # Creat forward wavefield to reuse to avoid memory overload
    u0 = TimeFunction(name='u',
                      grid=model.grid,
                      time_order=2,
                      space_order=4,
                      save=geometry.nt)
    for i in range(nshots):
        # Important: We force previous wavefields to be destroyed,
        # so that we may reuse the memory.
        clear_cache()

        # Update source location
        geometry.src_positions[0, :] = source_locations[i, :]

        # Generate synthetic data from true model
        true_d, _, _ = solver.forward(m=model.m)

        # Compute smooth data and full forward wavefield u0
        u0.data.fill(0.)
        smooth_d, _, _ = solver.forward(m=m_in, save=True, u=u0)

        # Compute gradient from data residual and update objective function
        residual.data[:] = smooth_d.data[:] - true_d.data[:]

        objective += .5 * np.linalg.norm(residual.data.flatten())**2
        solver.gradient(rec=residual, u=u0, m=m_in, grad=grad)

    return objective, grad.data
Ejemplo n.º 2
0
    def test_special_symbols(self):
        """
        This test checks the singletonization, through the caching infrastructure,
        of the special symbols that an Operator may generate (e.g., `nthreads`).
        """
        grid = Grid(shape=(4, 4, 4))
        f = TimeFunction(name='f', grid=grid)
        sf = SparseTimeFunction(name='sf', grid=grid, npoint=1, nt=10)

        eqns = [Eq(f.forward, f + 1.)] + sf.inject(field=f.forward, expr=sf)

        opt = ('advanced', {'par-nested': 0, 'openmp': True})
        op0 = Operator(eqns, opt=opt)
        op1 = Operator(eqns, opt=opt)

        nthreads0, nthreads_nested0, nthreads_nonaffine0 =\
            [i for i in op0.input if isinstance(i, NThreadsBase)]
        nthreads1, nthreads_nested1, nthreads_nonaffine1 =\
            [i for i in op1.input if isinstance(i, NThreadsBase)]

        assert nthreads0 is nthreads1
        assert nthreads_nested0 is nthreads_nested1
        assert nthreads_nonaffine0 is nthreads_nonaffine1

        tid0 = ThreadID(op0.nthreads)
        tid1 = ThreadID(op0.nthreads)
        assert tid0 is tid1

        did0 = DeviceID()
        did1 = DeviceID()
        assert did0 is did1

        npt0 = NPThreads(name='npt', size=3)
        npt1 = NPThreads(name='npt', size=3)
        npt2 = NPThreads(name='npt', size=4)
        assert npt0 is npt1
        assert npt0 is not npt2
Ejemplo n.º 3
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))
Ejemplo n.º 4
0
    def test_multiple_subnests_v0(self):
        grid = Grid(shape=(3, 3, 3))
        x, y, z = grid.dimensions
        t = grid.stepping_dim

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

        eqn = Eq(
            u.forward,
            _R(
                _R(u[t, x, y, z] + u[t, x + 1, y + 1, z + 1]) * 3. * f +
                _R(u[t, x + 2, y + 2, z + 2] + u[t, x + 3, y + 3, z + 3]) *
                3. * f) + 1.)
        op = Operator(eqn,
                      opt=('advanced', {
                          'openmp': True,
                          'cire-mingain': 0,
                          'par-nested': 0,
                          'par-collapse-ncores': 1,
                          'par-dynamic-work': 0
                      }))

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

        trees = retrieve_iteration_tree(bns['x0_blk0'])
        assert len(trees) == 2

        assert trees[0][0] is trees[1][0]
        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)')
        assert trees[1][2].pragmas[0].value == ('omp parallel for collapse(2) '
                                                'schedule(dynamic,1) '
                                                'num_threads(nthreads_nested)')
Ejemplo n.º 5
0
    def adjoint(self, rec, srca=None, v=None, m=None, **kwargs):
        """
        Adjoint modelling function that creates the necessary
        data objects for running an adjoint modelling operator.

        :param rec: Symbol with stored receiver data. Please note that
                    these act as the source term in the adjoint run.
        :param srca: Symbol to store the resulting data for the
                     interpolated at the original source location.
        :param v: (Optional) Symbol to store the computed wavefield
        :param m: (Optional) Symbol for the time-constant square slowness

        :returns: Adjoint source, wavefield and performance summary
        """
        # Create a new adjoint source and receiver symbol
        srca = srca or PointSource(name='srca',
                                   grid=self.model.grid,
                                   time_range=self.geometry.time_axis,
                                   coordinates=self.geometry.src_positions)

        # Create the adjoint wavefield if not provided
        v = v or TimeFunction(name='v',
                              grid=self.model.grid,
                              time_order=2,
                              space_order=self.space_order)

        # Pick m from model unless explicitly provided
        m = m or self.model.m

        # Execute operator and return wavefield and receiver data
        summary = self.op_adj().apply(srca=srca,
                                      rec=rec,
                                      v=v,
                                      m=m,
                                      dt=kwargs.pop('dt', self.dt),
                                      **kwargs)
        return srca, v, summary
Ejemplo n.º 6
0
def random_walks1D_vec(x0, N, p, num_walks=1, num_times=1, random=random):
    """Vectorized version of random_walks1D."""
    position = np.zeros(N + 1)  # Accumulated positions
    position2 = np.zeros(N + 1)  # Accumulated positions**2
    # Histogram at num_times selected time points
    pos_hist = np.zeros((num_walks, num_times))
    pos_hist_times = [(N // num_times) * i for i in range(num_times)]

    # Create and initialise our TimeFunction r
    x_d = Dimension(name='x_d')
    t_d = Dimension(name='t_d')
    r = TimeFunction(name='r', dimensions=(x_d, t_d), shape=(N + 1, num_walks))

    # Setting each walk's first element to x0
    r.data[0, :] = x0

    steps = Function(name='steps',
                     dimensions=(x_d, t_d),
                     shape=(N + 1, num_walks))

    # Populating steps with -1 if value in rs <= p at that point and 1 otherwise
    rs = random.uniform(0, 1, size=N * num_walks).reshape(num_walks, N)
    for n in range(num_walks):
        steps.data[:N, n] = np.where(rs[n] <= p, -1, 1)

    # Creating and applying operator that contains equation for adding steps
    eq = Eq(r.forward, r + steps)
    op = Operator(eq)
    op.apply()

    # Summing over data to give positions
    position = np.sum(r.data, axis=1)  # Accumulated positions
    position2 = np.sum(r.data**2, axis=1)  # Accumulated positions**2

    pos_hist[:, :] = np.transpose(r.data[pos_hist_times, :])

    return position, position2, pos_hist, np.array(pos_hist_times)
Ejemplo n.º 7
0
    def gradient(self, rec, u, v=None, grad=None, m=None, **kwargs):
        """
        Gradient modelling function for computing the adjoint of the
        Linearized Born modelling function, ie. the action of the
        Jacobian adjoint on an input data.

        :param recin: Receiver data as a numpy array
        :param u: Symbol for full wavefield `u` (created with save=True)
        :param v: (Optional) Symbol to store the computed wavefield
        :param grad: (Optional) Symbol to store the gradient field

        :returns: Gradient field and performance summary
        """

        # Gradient symbol
        if grad is None:
            grad = Function(name='grad', grid=self.model.grid)

        # Create the forward wavefield
        if v is None:
            v = TimeFunction(name='v',
                             grid=self.model.grid,
                             time_order=2,
                             space_order=self.space_order)

        # Pick m from model unless explicitly provided
        if m is None:
            m = m or self.model.m

        summary = self.op_grad().apply(rec=rec,
                                       grad=grad,
                                       v=v,
                                       u=u,
                                       m=m,
                                       dt=kwargs.pop('dt', self.dt),
                                       **kwargs)
        return grad, summary
Ejemplo n.º 8
0
def test_precomputed_interpolation_time():
    """ Test interpolation with PrecomputedSparseFunction which accepts
        precomputed values for interpolation coefficients, but this time
        with a TimeFunction
    """
    shape = (101, 101)
    points = [(.05, .9), (.01, .8), (0.07, 0.84)]
    origin = (0, 0)

    grid = Grid(shape=shape, origin=origin)
    r = 2  # Constant for linear interpolation
    #  because we interpolate across 2 neighbouring points in each dimension

    u = TimeFunction(name='u', grid=grid, space_order=0, save=5)
    for it in range(5):
        u.data[it, :] = it

    gridpoints, interpolation_coeffs = precompute_linear_interpolation(
        points, grid, origin)

    sf = PrecomputedSparseTimeFunction(
        name='s',
        grid=grid,
        r=r,
        npoint=len(points),
        nt=5,
        gridpoints=gridpoints,
        interpolation_coeffs=interpolation_coeffs)

    assert sf.data.shape == (5, 3)

    eqn = sf.interpolate(u)
    op = Operator(eqn)
    op(time_m=0, time_M=4)

    for it in range(5):
        assert np.allclose(sf.data[it, :], it)
Ejemplo n.º 9
0
    def test_contracted_shape_after_blocking(self):
        """
        Like `test_full_alias_shape_after_blocking`, but a different
        Operator is used, leading to contracted Arrays (2D instead of 3D).
        """
        grid = Grid(shape=(3, 3, 3))
        x, y, z = grid.dimensions  # noqa
        t = grid.stepping_dim

        f = Function(name='f', grid=grid)
        f.data_with_halo[:] = 1.
        u = TimeFunction(name='u', grid=grid, space_order=3)
        u.data_with_halo[:] = 0.

        # Leads to 2D aliases
        eqn = Eq(u.forward, ((u[t, x, y, z] + u[t, x, y+1, z+1])*3*f +
                             (u[t, x, y+2, z+2] + u[t, x, y+3, z+3])*3*f + 1))
        op0 = Operator(eqn, dse='noop', dle=('advanced', {'openmp': True}))
        op1 = Operator(eqn, dse='aggressive', dle=('advanced', {'openmp': True}))

        y0_blk_size = op1.parameters[-2]
        z_size = op1.parameters[3]

        arrays = [i for i in FindSymbols().visit(op1._func_table['bf0'].root)
                  if i.is_Array]
        assert len(arrays) == 1
        a = arrays[0]
        assert len(a.dimensions) == 2
        assert a.halo == ((1, 1), (1, 1))
        assert Add(*a.symbolic_shape[0].args) == y0_blk_size + 2
        assert Add(*a.symbolic_shape[1].args) == z_size + 2
        # Check numerical output
        op0(time_M=1)
        exp = np.copy(u.data[:])
        u.data_with_halo[:] = 0.
        op1(time_M=1)
        assert np.all(u.data == exp)
Ejemplo n.º 10
0
def wavefield_subsampled(model, u, nt, t_sub, space_order=8):
    """
    Create a subsampled wavefield

    Parameters
    ----------

    model : Model
        Physical model
    u : TimeFunction
        Forward wavefield for modeling
    nt : int
        Number of time steps on original time axis
    t_sub : int
        Factor for time-subsampling
    space_order: int
        Spatial discretization order
    """
    wf_s = []
    eq_save = []
    if t_sub > 1:
        time_subsampled = ConditionalDimension(name='t_sub',
                                               parent=model.grid.time_dim,
                                               factor=t_sub)
        nsave = (nt - 1) // t_sub + 2
    else:
        return None, []
    for wf in as_tuple(u):
        usave = TimeFunction(name='us_%s' % wf.name,
                             grid=model.grid,
                             time_order=2,
                             space_order=space_order,
                             time_dim=time_subsampled,
                             save=nsave)
        wf_s.append(usave)
        eq_save.append(Eq(usave, wf))
    return wf_s, eq_save
Ejemplo n.º 11
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 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 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
Ejemplo n.º 12
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, dle='openmp')

        parregions = FindNodes(ParallelRegion).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'] == NThreads.default_value()
        assert op.arguments(time=0)['nthreads_nonaffine'] == \
            NThreadsNonaffine.default_value()
        # Again, but with user-supplied values
        assert op.arguments(time=0, nthreads=123)['nthreads'] == 123
        assert op.arguments(
            time=0, nthreads_nonaffine=100)['nthreads_nonaffine'] == 100
        # Again, but with the aliases
        assert op.arguments(time=0, nthreads0=123)['nthreads'] == 123
        assert op.arguments(time=0, nthreads2=123)['nthreads_nonaffine'] == 123
Ejemplo n.º 13
0
def adjoint_wf_src(model, u, src_coords, space_order=8):
    """
    Adjoint/backward modeling of a full wavefield (full wavefield as adjoint source)
    Ps*F^T*u.

    Parameters
    ----------
    model: Model
        Physical model
    u: Array or TimeFunction
        Time-space dependent source
    src_coords: Array
        Source coordinates
    space_order: Int (optional)
        Spatial discretization order, defaults to 8

    Returns
    ----------
    Array
        Shot record (sampled at source position(s))
    """
    wf_src = TimeFunction(name='wf_src',
                          grid=model.grid,
                          time_order=2,
                          space_order=space_order,
                          save=u.shape[0])
    if isinstance(u, TimeFunction):
        wf_src._data = u._data
    else:
        wf_src.data[:] = u[:]
    rec, _, _ = adjoint(model,
                        None,
                        src_coords,
                        None,
                        space_order=space_order,
                        q=wf_src)
    return rec.data
Ejemplo n.º 14
0
def adjoint_wf_src_norec(model, u, space_order=8):
    """
    Adjoint/backward modeling of a full wavefield (full wavefield as adjoint source)
    F^T*u.

    Parameters
    ----------
    model: Model
        Physical model
    u: Array or TimeFunction
        Time-space dependent source
    space_order: Int (optional)
        Spatial discretization order, defaults to 8

    Returns
    ----------
    Array
        Adjoint wavefield
    """
    wf_src = TimeFunction(name='wf_src',
                          grid=model.grid,
                          time_order=2,
                          space_order=space_order,
                          save=u.shape[0])
    if isinstance(u, TimeFunction):
        wf_src._data = u._data
    else:
        wf_src.data[:] = u[:]
    _, v, _ = adjoint(model,
                      None,
                      None,
                      None,
                      space_order=space_order,
                      save=True,
                      q=wf_src)
    return v.data
Ejemplo n.º 15
0
    def test_multiple_subnests(self):
        grid = Grid(shape=(3, 3, 3))
        x, y, z = grid.dimensions
        t = grid.stepping_dim

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

        eqn = Eq(u.forward, ((u[t, x, y, z] + u[t, x+1, y+1, z+1])*3*f +
                             (u[t, x+2, y+2, z+2] + u[t, x+3, y+3, z+3])*3*f + 1))
        op = Operator(eqn, dse='aggressive', dle=('advanced', {'openmp': True}))

        trees = retrieve_iteration_tree(op._func_table['bf0'].root)
        assert len(trees) == 2

        assert trees[0][0] is trees[1][0]
        assert trees[0][0].pragmas[0].value ==\
            'omp for collapse(1) schedule(dynamic,1)'
        assert trees[0][2].pragmas[0].value == ('omp parallel for collapse(1) '
                                                'schedule(dynamic,1) '
                                                'num_threads(nthreads_nested)')
        assert trees[1][2].pragmas[0].value == ('omp parallel for collapse(1) '
                                                'schedule(dynamic,1) '
                                                'num_threads(nthreads_nested)')
Ejemplo n.º 16
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.)
Ejemplo n.º 17
0
    def test_no_index(self):
        """Test behaviour when the ConditionalDimension is used as a symbol in
        an expression."""
        nt = 19
        grid = Grid(shape=(11, 11))
        time = grid.time_dim

        u = TimeFunction(name='u', grid=grid)
        assert(grid.stepping_dim in u.indices)

        v = Function(name='v', grid=grid)

        factor = 4
        time_subsampled = ConditionalDimension('t_sub', parent=time, factor=factor)

        eqns = [Eq(u.forward, u + 1), Eq(v, v + u*u*time_subsampled)]
        op = Operator(eqns)
        op.apply(t_M=nt-2)
        assert np.all(np.allclose(u.data[(nt-1) % 3], nt-1))
        # expected result is 1024
        # v = u[0]**2 * 0 + u[4]**2 * 1 + u[8]**2 * 2 + u[12]**2 * 3 + u[16]**2 * 4
        # with u[t] = t
        # v = 16 * 1 + 64 * 2 + 144 * 3 + 256 * 4 = 1600
        assert np.all(np.allclose(v.data, 1600))
Ejemplo n.º 18
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=0, time_M=0)

        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)
def ForwardOperator(model, source, receiver, space_order=4,
                    save=False, kernel='OT2', **kwargs):
    """
    Constructor method for the forward modelling operator in an acoustic media

    :param model: :class:`Model` object containing the physical parameters
    :param source: :class:`PointData` object containing the source geometry
    :param receiver: :class:`PointData` object containing the acquisition geometry
    :param time_order: Time discretization order
    :param space_order: Space discretization order
    :param save: Saving flag, True saves all time steps, False only the three
    """
    m, damp = model.m, model.damp

    # Create symbols for forward wavefield, source and receivers
    u = TimeFunction(name='u', grid=model.grid,
                     save=source.nt if save else None,
                     time_order=2, space_order=space_order)
    src = PointSource(name='src', grid=model.grid, ntime=source.nt,
                      npoint=source.npoint)
    rec = Receiver(name='rec', grid=model.grid, ntime=receiver.nt,
                   npoint=receiver.npoint)

    s = model.grid.stepping_dim.spacing
    eqn = iso_stencil(u, m, s, damp, kernel)

    # Construct expression to inject source values
    src_term = src.inject(field=u.forward, expr=src * s**2 / m,
                          offset=model.nbpml)

    # Create interpolation expression for receivers
    rec_term = rec.interpolate(expr=u, offset=model.nbpml)

    # Substitute spacing terms to reduce flops
    return Operator(eqn + src_term + rec_term, subs=model.spacing_map,
                    name='Forward', **kwargs)
Ejemplo n.º 20
0
    def test_array_rw(self):
        grid = Grid(shape=(3, 3, 3))

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

        eqn = Eq(u.forward, u * cos(f * 2))

        op = Operator(eqn)

        assert len(op.body[1].header) == 7
        assert str(op.body[1].header[0]) == 'float (*r1)[y_size][z_size];'
        assert op.body[1].header[1].text ==\
            'posix_memalign((void**)&r1, 64, sizeof(float[x_size][y_size][z_size]))'
        assert op.body[1].header[2].value ==\
            ('omp target enter data map(alloc: r1[0:x_size][0:y_size][0:z_size])'
             '')

        assert len(op.body[1].footer) == 6
        assert str(op.body[1].footer[0]) == ''
        assert op.body[1].footer[1].value ==\
            ('omp target exit data map(delete: r1[0:x_size][0:y_size][0:z_size])'
             ' if((x_size != 0) && (y_size != 0) && (z_size != 0))')
        assert op.body[1].footer[2].text == 'free(r1)'
Ejemplo n.º 21
0
    def test_iteration_parallelism_3d(self, exprs, atomic, parallel):
        """Tests detection of PARALLEL_* properties."""
        grid = Grid(shape=(10, 10, 10))
        time = grid.time_dim  # noqa
        t = grid.stepping_dim  # noqa
        x, y, z = grid.dimensions  # noqa

        p = Dimension(name='p')
        d = Dimension(name='d')
        rx = Dimension(name='rx')
        ry = Dimension(name='ry')
        rz = Dimension(name='rz')

        u = Function(name='u', grid=grid)  # noqa
        v = TimeFunction(name='v', grid=grid, save=None)  # noqa

        cx = Function(name='coeff_x', dimensions=(p, rx), shape=(1, 2))  # noqa
        cy = Function(name='coeff_y', dimensions=(p, ry), shape=(1, 2))  # noqa
        cz = Function(name='coeff_z', dimensions=(p, rz), shape=(1, 2))  # noqa

        gp = Function(name='gridpoints', dimensions=(p, d), shape=(1, 3))  # noqa
        src = Function(name='src', dimensions=(p,), shape=(1,))  # noqa
        rcv = Function(name='rcv', dimensions=(time, p), shape=(100, 1), space_order=0)  # noqa

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

        # Use 'openmp' here instead of 'advanced' to disable loop blocking
        op = Operator(exprs, dle='openmp')

        iters = FindNodes(Iteration).visit(op)
        assert all([i.is_ParallelAtomic for i in iters if i.dim.name in atomic])
        assert all([not i.is_ParallelAtomic for i in iters if i.dim.name not in atomic])
        assert all([i.is_Parallel for i in iters if i.dim.name in parallel])
        assert all([not i.is_Parallel for i in iters if i.dim.name not in parallel])
Ejemplo n.º 22
0
def forward_wf_src(model, u, rec_coords, space_order=8):
    """
    Forward modeling of a full wavefield source Pr*F*u.

    Parameters
    ----------
    model: Model
        Physical model
    u: TimeFunction or Array
        Time-space dependent wavefield
    rec_coords: Array
        Coordiantes of the receiver(s)
    space_order: Int (optional)
        Spatial discretization order, defaults to 8

    Returns
    ----------
    Array
        Shot record
    """
    wf_src = TimeFunction(name='wf_src',
                          grid=model.grid,
                          time_order=2,
                          space_order=space_order,
                          save=u.shape[0])
    if isinstance(u, TimeFunction):
        wf_src._data = u._data
    else:
        wf_src.data[:] = u[:]
    rec, _, _ = forward(model,
                        None,
                        rec_coords,
                        None,
                        space_order=space_order,
                        q=wf_src)
    return rec.data
Ejemplo n.º 23
0
    def forward(self, src=None, rec=None, u=None, m=None, save=None, **kwargs):
        """
        Forward modelling function that creates the necessary
        data objects for running a forward modelling operator.

        :param src: Symbol with time series data for the injected source term
        :param rec: Symbol to store interpolated receiver data
        :param u: (Optional) Symbol to store the computed wavefield
        :param m: (Optional) Symbol for the time-constant square slowness
        :param save: Option to store the entire (unrolled) wavefield

        :returns: Receiver, wavefield and performance summary
        """
        # Source term is read-only, so re-use the default
        if src is None:
            src = self.source
        # Create a new receiver object to store the result
        if rec is None:
            rec = Receiver(name='rec', grid=self.model.grid,
                           ntime=self.receiver.nt,
                           coordinates=self.receiver.coordinates.data)

        # Create the forward wavefield if not provided
        if u is None:
            u = TimeFunction(name='u', grid=self.model.grid,
                             save=self.source.nt if save else None,
                             time_order=2, space_order=self.space_order)

        # Pick m from model unless explicitly provided
        if m is None:
            m = m or self.model.m

        # Execute operator and return wavefield and receiver data
        summary = self.op_fwd(save).apply(src=src, rec=rec, u=u, m=m,
                                          dt=kwargs.pop('dt', self.dt), **kwargs)
        return rec, u, summary
Ejemplo n.º 24
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)
Ejemplo n.º 25
0
    def test_empty_arrays(self):
        """
        MFE for issue #1641.
        """
        grid = Grid(shape=(4, 4), extent=(3.0, 3.0))

        f = TimeFunction(name='f', grid=grid, space_order=0)
        f.data[:] = 1.
        sf1 = SparseTimeFunction(name='sf1', grid=grid, npoint=0, nt=10)
        sf2 = SparseTimeFunction(name='sf2', grid=grid, npoint=0, nt=10)
        assert sf1.size == 0
        assert sf2.size == 0

        eqns = sf1.inject(field=f, expr=sf1 + sf2 + 1.)

        op = Operator(eqns)
        op.apply()
        assert np.all(f.data == 1.)

        # Again, but with a MatrixSparseTimeFunction
        mat = scipy.sparse.coo_matrix((0, 0), dtype=np.float32)
        sf = MatrixSparseTimeFunction(name="s",
                                      grid=grid,
                                      r=2,
                                      matrix=mat,
                                      nt=10)
        assert sf.size == 0

        eqns = sf.interpolate(f)

        op = Operator(eqns)

        sf.manual_scatter()
        op(time_m=0, time_M=9)
        sf.manual_gather()
        assert np.all(f.data == 1.)
Ejemplo n.º 26
0
    def test_injection_wodup_wtime(self):
        """
        Just like ``test_injection_wodup``, but using a SparseTimeFunction
        instead of a SparseFunction. Hence, the data scattering/gathering now
        has to correctly pack/unpack multidimensional arrays.
        """
        grid = Grid(shape=(4, 4), extent=(3.0, 3.0))

        save = 3
        f = TimeFunction(name='f', grid=grid, save=save, space_order=0)
        f.data[:] = 0.
        coords = np.array([(0.5, 0.5), (0.5, 2.5), (2.5, 0.5), (2.5, 2.5)])
        sf = SparseTimeFunction(name='sf', grid=grid, nt=save,
                                npoint=len(coords), coordinates=coords)
        sf.data[0, :] = 4.
        sf.data[1, :] = 8.
        sf.data[2, :] = 12.

        op = Operator(sf.inject(field=f, expr=sf + 1))
        op.apply()

        assert np.all(f.data[0] == 1.25)
        assert np.all(f.data[1] == 2.25)
        assert np.all(f.data[2] == 3.25)
Ejemplo n.º 27
0
    def test_function_wo(self):
        grid = Grid(shape=(3, 3, 3))
        i = Dimension(name='i')

        f = Function(name='f', shape=(1, ), dimensions=(i, ), grid=grid)
        u = TimeFunction(name='u', grid=grid)

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

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

        assert len(op.body[1].header) == 2
        assert len(op.body[1].footer) == 2
        assert op.body[1].header[0].value ==\
            ('omp target enter data map(to: u[0:u_vec->size[0]]'
             '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])')
        assert str(op.body[1].header[1]) == ''
        assert str(op.body[1].footer[0]) == ''
        assert op.body[1].footer[1].contents[0].value ==\
            ('omp target update from(u[0:u_vec->size[0]]'
             '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])')
        assert op.body[1].footer[1].contents[1].value ==\
            ('omp target exit data map(release: u[0:u_vec->size[0]]'
             '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])')
Ejemplo n.º 28
0
def test_codegen_quality1():
    grid = Grid(shape=(4, 4, 4))

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

    eqn = Eq(u.forward, u.dy.dy + 1.)

    op = Operator(eqn,
                  opt=('advanced', {
                      'linearize': True,
                      'cire-mingain': 0
                  }))

    assert 'uL0' in str(op)

    # 11 expressions in total are expected, 8 of which are for the linearized accesses
    exprs = FindNodes(Expression).visit(op)
    assert len(exprs) == 11
    assert all('const long' in str(i) for i in exprs[:-3])
    assert all('const long' not in str(i) for i in exprs[-3:])

    # Only two access macros necessary, namely `uL0` and `r1L0` (the other five
    # obviously are _POSIX_C_SOURCE, MIN, MAX, START_TIMER, STOP_TIMER)
    assert len(op._headers) == 7
Ejemplo n.º 29
0
    def test_equations_mixed_densedata_timedata(self, shape, dimensions):
        """
        Test that equations using a mixture of Function and TimeFunction objects
        are embedded within the same time loop.
        """
        grid = Grid(shape=shape, dimensions=dimensions, time_dimension=time)
        a = TimeFunction(name='a', grid=grid, time_order=2, space_order=2)
        p_aux = Dimension(name='p_aux')
        b = Function(name='b', shape=shape + (10,), dimensions=dimensions + (p_aux,),
                     space_order=2)
        b.data_allocated[:] = 1.0
        b2 = Function(name='b2', shape=(10,) + shape, dimensions=(p_aux,) + dimensions,
                      space_order=2)
        b2.data_allocated[:] = 1.0
        eqns = [Eq(a.forward, a.laplace + 1.),
                Eq(b, time*b*a + b)]
        eqns2 = [Eq(a.forward, a.laplace + 1.),
                 Eq(b2, time*b2*a + b2)]
        subs = {x.spacing: 2.5, y.spacing: 1.5, z.spacing: 2.0}
        op = Operator(eqns, subs=subs, dle='noop')
        trees = retrieve_iteration_tree(op)
        assert len(trees) == 2
        assert all(trees[0][i] is trees[1][i] for i in range(3))

        op2 = Operator(eqns2, subs=subs, dle='noop')
        trees = retrieve_iteration_tree(op2)
        assert len(trees) == 2

        # Verify both operators produce the same result
        op(time=10)
        a.data_allocated[:] = 0.
        op2(time=10)

        for i in range(10):
            assert(np.allclose(b2.data[i, ...].reshape(-1) -
                               b.data[..., i].reshape(-1), 0.))
Ejemplo n.º 30
0
    def test_basic(self):
        grid = Grid(shape=(3, 3, 3))

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

        op = Operator(Eq(u.forward, u + 1),
                      platform='nvidiaX',
                      language='openacc')

        trees = retrieve_iteration_tree(op)
        assert len(trees) == 1

        assert trees[0][1].pragmas[0].value ==\
            'acc parallel loop collapse(3) present(u)'
        assert op.body[2].header[0].value ==\
            ('acc enter data copyin(u[0:u_vec->size[0]]'
             '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])')
        assert str(op.body[2].footer[0]) == ''
        assert op.body[2].footer[1].contents[0].value ==\
            ('acc exit data copyout(u[0:u_vec->size[0]]'
             '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])')
        assert op.body[2].footer[1].contents[1].value ==\
            ('acc exit data delete(u[0:u_vec->size[0]]'
             '[0:u_vec->size[1]][0:u_vec->size[2]][0:u_vec->size[3]])')