Exemple #1
0
def make_derivative(expr, dim, fd_order, deriv_order, side, matvec, x0, symbolic):
    # The stencil positions
    indices, x0 = generate_indices(expr, dim, fd_order, side=side, x0=x0)

    # Finite difference weights from Taylor approximation with these positions
    if symbolic:
        weights = symbolic_weights(expr, deriv_order, indices, x0)
    else:
        weights = numeric_weights(deriv_order, indices, x0)

    return indices_weights_to_fd(expr, dim, indices, weights, matvec=matvec.val)
Exemple #2
0
def generic_derivative(expr,
                       dim,
                       fd_order,
                       deriv_order,
                       stagger=None,
                       symbolic=False,
                       matvec=direct):
    """
    Arbitrary-order derivative of a given expression.

    Parameters
    ----------
    expr : expr-like
        Expression for which the derivative is produced.
    dim : Dimension
        The Dimension w.r.t. which to differentiate.
    fd_order : int
        Coefficient discretization order. Note: this impacts the width of
        the resulting stencil.
    deriv_order : int
        Derivative order, e.g. 2 for a second-order derivative.
    stagger : Side, optional
        Shift of the finite-difference approximation.

    Returns
    -------
    expr-like
        ``deriv-order`` derivative of ``expr``.
    """
    diff = dim.spacing
    adjoint_val = matvec.val**deriv_order

    # Stencil positions
    indices, x0 = generate_indices(expr, dim, diff, fd_order, stagger=stagger)

    # Finite difference weights from Taylor approximation with these positions
    if symbolic:
        c = symbolic_weights(expr, deriv_order, indices, x0)
    else:
        c = finite_diff_weights(deriv_order, indices, x0)[-1][-1]

    # Loop through positions
    deriv = 0
    all_dims = tuple(
        set((dim, ) + tuple(i for i in expr.indices if i.root == dim)))
    for i in range(len(indices)):
        subs = dict((d, indices[i].subs({dim: d})) for d in all_dims)
        deriv += expr.subs(subs) * c[i]

    # Evaluate up to _PRECISION digits
    deriv = (adjoint_val * deriv).evalf(_PRECISION)

    return deriv
Exemple #3
0
def generic_derivative(expr,
                       dim,
                       fd_order,
                       deriv_order,
                       symbolic=False,
                       matvec=direct,
                       x0=None):
    """
    Arbitrary-order derivative of a given expression.

    Parameters
    ----------
    expr : expr-like
        Expression for which the derivative is produced.
    dim : Dimension
        The Dimension w.r.t. which to differentiate.
    fd_order : int
        Coefficient discretization order. Note: this impacts the width of
        the resulting stencil.
    deriv_order : int
        Derivative order, e.g. 2 for a second-order derivative.
    stagger : Side, optional
        Shift of the finite-difference approximation.
    x0 : dict, optional
        Origin of the finite-difference scheme as a map dim: origin_dim.

    Returns
    -------
    expr-like
        ``deriv-order`` derivative of ``expr``.
    """
    # First order derivative with 2nd order FD is highly non-recommended so taking
    # first order fd that is a lot better
    if deriv_order == 1 and fd_order == 2 and not symbolic:
        fd_order = 1
    # Stencil positions
    indices, x0 = generate_indices(expr, dim, fd_order, x0=x0)

    # Finite difference weights from Taylor approximation with these positions
    if symbolic:
        c = symbolic_weights(expr, deriv_order, indices, x0)
    else:
        c = numeric_weights(deriv_order, indices, x0)

    return indices_weights_to_fd(expr, dim, indices, c, matvec=matvec.val)
Exemple #4
0
def first_derivative(expr,
                     dim,
                     fd_order=None,
                     side=centered,
                     matvec=direct,
                     symbolic=False):
    """
    First-order derivative of a given expression.

    Parameters
    ----------
    expr : expr-like
        Expression for which the first-order derivative is produced.
    dim : Dimension
        The Dimension w.r.t. which to differentiate.
    fd_order : int, optional
        Coefficient discretization order. Note: this impacts the width of
        the resulting stencil. Defaults to ``expr.space_order``
    side : Side, optional
        Side of the finite difference location, centered (at x), left (at x - 1)
        or right (at x +1). Defaults to ``centered``.
    matvec : Transpose, optional
        Forward (matvec=direct) or transpose (matvec=transpose) mode of the
        finite difference. Defaults to ``direct``.

    Returns
    -------
    expr-like
        First-order derivative of ``expr``.

    Examples
    --------
    >>> from devito import Function, Grid, first_derivative, transpose
    >>> grid = Grid(shape=(4, 4))
    >>> x, _ = grid.dimensions
    >>> f = Function(name='f', grid=grid)
    >>> g = Function(name='g', grid=grid)
    >>> first_derivative(f*g, dim=x)
    -f(x, y)*g(x, y)/h_x + f(x + h_x, y)*g(x + h_x, y)/h_x

    Semantically, this is equivalent to

    >>> (f*g).dx
    Derivative(f(x, y)*g(x, y), x)

    The only difference is that in the latter case derivatives remain unevaluated.
    The expanded form is obtained via ``evaluate``

    >>> (f*g).dx.evaluate
    -f(x, y)*g(x, y)/h_x + f(x + h_x, y)*g(x + h_x, y)/h_x

    For the adjoint mode of the first derivative, pass ``matvec=transpose``

    >>> g = Function(name='g', grid=grid)
    >>> first_derivative(f*g, dim=x, matvec=transpose)
    f(x, y)*g(x, y)/h_x - f(x + h_x, y)*g(x + h_x, y)/h_x

    This is also accessible via the .T shortcut

    >>> (f*g).dx.T.evaluate
    f(x, y)*g(x, y)/h_x - f(x + h_x, y)*g(x + h_x, y)/h_x
    """
    side = side.adjoint(matvec)
    diff = dim.spacing
    adjoint_val = matvec.val
    order = fd_order or expr.space_order

    # Stencil positions for non-symmetric cross-derivatives with symmetric averaging
    ind = generate_indices(expr, dim, diff, order, side=side)[0]

    # Finite difference weights from Taylor approximation with these positions
    if symbolic:
        c = symbolic_weights(expr, 1, ind, dim)
    else:
        c = finite_diff_weights(1, ind, dim)[-1][-1]

    # Loop through positions
    deriv = 0
    all_dims = tuple(
        set((dim, ) + tuple([i for i in expr.indices if i.root == dim])))
    for i in range(len(ind)):
        subs = dict([(d, ind[i].subs({dim: d})) for d in all_dims])
        deriv += expr.subs(subs) * c[i]

    # Evaluate up to _PRECISION digits
    deriv = (adjoint_val * deriv).evalf(_PRECISION)

    return deriv
def first_derivative(expr, dim, fd_order=None, side=centered, matvec=direct,
                     symbolic=False, x0=None):
    """
    First-order derivative of a given expression.

    Parameters
    ----------
    expr : expr-like
        Expression for which the first-order derivative is produced.
    dim : Dimension
        The Dimension w.r.t. which to differentiate.
    fd_order : int, optional
        Coefficient discretization order. Note: this impacts the width of
        the resulting stencil. Defaults to ``expr.space_order``
    side : Side, optional
        Side of the finite difference location, centered (at x), left (at x - 1)
        or right (at x +1). Defaults to ``centered``.
    matvec : Transpose, optional
        Forward (matvec=direct) or transpose (matvec=transpose) mode of the
        finite difference. Defaults to ``direct``.
    x0 : dict, optional
        Origin of the finite-difference scheme as a map dim: origin_dim.

    Returns
    -------
    expr-like
        First-order derivative of ``expr``.

    Examples
    --------
    >>> from devito import Function, Grid, first_derivative, transpose
    >>> grid = Grid(shape=(4, 4))
    >>> x, _ = grid.dimensions
    >>> f = Function(name='f', grid=grid)
    >>> g = Function(name='g', grid=grid)
    >>> first_derivative(f*g, dim=x)
    -f(x, y)*g(x, y)/h_x + f(x + h_x, y)*g(x + h_x, y)/h_x

    Semantically, this is equivalent to

    >>> (f*g).dx
    Derivative(f(x, y)*g(x, y), x)

    The only difference is that in the latter case derivatives remain unevaluated.
    The expanded form is obtained via ``evaluate``

    >>> (f*g).dx.evaluate
    -f(x, y)*g(x, y)/h_x + f(x + h_x, y)*g(x + h_x, y)/h_x

    For the adjoint mode of the first derivative, pass ``matvec=transpose``

    >>> g = Function(name='g', grid=grid)
    >>> first_derivative(f*g, dim=x, matvec=transpose)
    -f(x, y)*g(x, y)/h_x + f(x - h_x, y)*g(x - h_x, y)/h_x

    This is also accessible via the .T shortcut

    >>> (f*g).dx.T.evaluate
    -f(x, y)*g(x, y)/h_x + f(x - h_x, y)*g(x - h_x, y)/h_x

    Finally the x0 argument allows to choose the origin of the finite-difference

    >>> first_derivative(f, dim=x, x0={x: 1})
    -f(1, y)/h_x + f(h_x + 1, y)/h_x
    """
    side = side
    order = fd_order or expr.space_order

    # Stencil positions for non-symmetric cross-derivatives with symmetric averaging
    ind = generate_indices(expr, dim, order, side=side, x0=x0)[0]

    # Finite difference weights from Taylor approximation with these positions
    if symbolic:
        c = symbolic_weights(expr, 1, ind, dim)
    else:
        c = finite_diff_weights(1, ind, dim)[-1][-1]

    return indices_weights_to_fd(expr, dim, ind, c, matvec=matvec.val)