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)
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)
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 = numeric_weights(1, ind, dim) return indices_weights_to_fd(expr, dim, ind, c, matvec=matvec.val)