@exporter.export
@xarray_derivative_wrap
def second_derivative(f, **kwargs):
    'Calculate the second derivative of a grid of values.\n\n    Works for both regularly-spaced data and grids with varying spacing.\n\n    Either `x` or `delta` must be specified, or `f` must be given as an `xarray.DataArray` with\n    attached coordinate and projection information. If `f` is an `xarray.DataArray`, and `x` or\n    `delta` are given, `f` will be converted to a `pint.Quantity` and the derivative returned\n    as a `pint.Quantity`, otherwise, if neither `x` nor `delta` are given, the attached\n    coordinate information belonging to `axis` will be used and the derivative will be returned\n    as an `xarray.DataArray`.\n\n    This uses 3 points to calculate the derivative, using forward or backward at the edges of\n    the grid as appropriate, and centered elsewhere. The irregular spacing is handled\n    explicitly, using the formulation as specified by [Bowen2005]_.\n\n    Parameters\n    ----------\n    f : array-like\n        Array of values of which to calculate the derivative\n    axis : int or str, optional\n        The array axis along which to take the derivative. If `f` is ndarray-like, must be an\n        integer. If `f` is a `DataArray`, can be a string (referring to either the coordinate\n        dimension name or the axis type) or integer (referring to axis number), unless using\n        implicit conversion to `pint.Quantity`, in which case it must be an integer. Defaults\n        to 0.\n    x : array-like, optional\n        The coordinate values corresponding to the grid points in `f`.\n    delta : array-like, optional\n        Spacing between the grid points in `f`. There should be one item less than the size\n        of `f` along `axis`.\n\n    Returns\n    -------\n    array-like\n        The second derivative calculated along the selected axis.\n\n    See Also\n    --------\n    first_derivative\n\n    '
    (n, axis, delta) = _process_deriv_args(f, kwargs)
    slice0 = ([slice(None)] * n)
    slice1 = ([slice(None)] * n)
    slice2 = ([slice(None)] * n)
    delta_slice0 = ([slice(None)] * n)
    delta_slice1 = ([slice(None)] * n)
    slice0[axis] = slice(None, (- 2))
    slice1[axis] = slice(1, (- 1))
    slice2[axis] = slice(2, None)
    delta_slice0[axis] = slice(None, (- 1))
    delta_slice1[axis] = slice(1, None)
    combined_delta = (delta[tuple(delta_slice0)] + delta[tuple(delta_slice1)])
    center = (2 * (((f[tuple(slice0)] / (combined_delta * delta[tuple(delta_slice0)])) - (f[tuple(slice1)] / (delta[tuple(delta_slice0)] * delta[tuple(delta_slice1)]))) + (f[tuple(slice2)] / (combined_delta * delta[tuple(delta_slice1)]))))
    slice0[axis] = slice(None, 1)
    slice1[axis] = slice(1, 2)
    slice2[axis] = slice(2, 3)
    delta_slice0[axis] = slice(None, 1)
    delta_slice1[axis] = slice(1, 2)
    combined_delta = (delta[tuple(delta_slice0)] + delta[tuple(delta_slice1)])
    left = (2 * (((f[tuple(slice0)] / (combined_delta * delta[tuple(delta_slice0)])) - (f[tuple(slice1)] / (delta[tuple(delta_slice0)] * delta[tuple(delta_slice1)]))) + (f[tuple(slice2)] / (combined_delta * delta[tuple(delta_slice1)]))))
    slice0[axis] = slice((- 3), (- 2))
    slice1[axis] = slice((- 2), (- 1))
    slice2[axis] = slice((- 1), None)
    delta_slice0[axis] = slice((- 2), (- 1))
    delta_slice1[axis] = slice((- 1), None)
    combined_delta = (delta[tuple(delta_slice0)] + delta[tuple(delta_slice1)])
    right = (2 * (((f[tuple(slice0)] / (combined_delta * delta[tuple(delta_slice0)])) - (f[tuple(slice1)] / (delta[tuple(delta_slice0)] * delta[tuple(delta_slice1)]))) + (f[tuple(slice2)] / (combined_delta * delta[tuple(delta_slice1)]))))
    return concatenate((left, center, right), axis=axis)
Calculate the second derivative of a grid of values.

Works for both regularly-spaced data and grids with varying spacing.

Either `x` or `delta` must be specified, or `f` must be given as an `xarray.DataArray` with
attached coordinate and projection information. If `f` is an `xarray.DataArray`, and `x` or
`delta` are given, `f` will be converted to a `pint.Quantity` and the derivative returned
as a `pint.Quantity`, otherwise, if neither `x` nor `delta` are given, the attached
coordinate information belonging to `axis` will be used and the derivative will be returned
as an `xarray.DataArray`.

This uses 3 points to calculate the derivative, using forward or backward at the edges of
the grid as appropriate, and centered elsewhere. The irregular spacing is handled
explicitly, using the formulation as specified by [Bowen2005]_.

Parameters
----------
f : array-like
    Array of values of which to calculate the derivative
axis : int or str, optional
    The array axis along which to take the derivative. If `f` is ndarray-like, must be an
    integer. If `f` is a `DataArray`, can be a string (referring to either the coordinate
    dimension name or the axis type) or integer (referring to axis number), unless using
    implicit conversion to `pint.Quantity`, in which case it must be an integer. Defaults
    to 0.
x : array-like, optional
    The coordinate values corresponding to the grid points in `f`.
delta : array-like, optional
    Spacing between the grid points in `f`. There should be one item less than the size
    of `f` along `axis`.

Returns
-------
array-like
    The second derivative calculated along the selected axis.

See Also
--------
first_derivative
@exporter.export
@xarray_derivative_wrap
def second_derivative(f, **kwargs):
    'Calculate the second derivative of a grid of values.\n\n    Works for both regularly-spaced data and grids with varying spacing.\n\n    Either `x` or `delta` must be specified, or `f` must be given as an `xarray.DataArray` with\n    attached coordinate and projection information. If `f` is an `xarray.DataArray`, and `x` or\n    `delta` are given, `f` will be converted to a `pint.Quantity` and the derivative returned\n    as a `pint.Quantity`, otherwise, if neither `x` nor `delta` are given, the attached\n    coordinate information belonging to `axis` will be used and the derivative will be returned\n    as an `xarray.DataArray`.\n\n    This uses 3 points to calculate the derivative, using forward or backward at the edges of\n    the grid as appropriate, and centered elsewhere. The irregular spacing is handled\n    explicitly, using the formulation as specified by [Bowen2005]_.\n\n    Parameters\n    ----------\n    f : array-like\n        Array of values of which to calculate the derivative\n    axis : int or str, optional\n        The array axis along which to take the derivative. If `f` is ndarray-like, must be an\n        integer. If `f` is a `DataArray`, can be a string (referring to either the coordinate\n        dimension name or the axis type) or integer (referring to axis number), unless using\n        implicit conversion to `pint.Quantity`, in which case it must be an integer. Defaults\n        to 0.\n    x : array-like, optional\n        The coordinate values corresponding to the grid points in `f`.\n    delta : array-like, optional\n        Spacing between the grid points in `f`. There should be one item less than the size\n        of `f` along `axis`.\n\n    Returns\n    -------\n    array-like\n        The second derivative calculated along the selected axis.\n\n    See Also\n    --------\n    first_derivative\n\n    '
    (n, axis, delta) = _process_deriv_args(f, kwargs)
    slice0 = ([slice(None)] * n)
    slice1 = ([slice(None)] * n)
    slice2 = ([slice(None)] * n)
    delta_slice0 = ([slice(None)] * n)
    delta_slice1 = ([slice(None)] * n)
    slice0[axis] = slice(None, (- 2))
    slice1[axis] = slice(1, (- 1))
    slice2[axis] = slice(2, None)
    delta_slice0[axis] = slice(None, (- 1))
    delta_slice1[axis] = slice(1, None)
    combined_delta = (delta[tuple(delta_slice0)] + delta[tuple(delta_slice1)])
    center = (2 * (((f[tuple(slice0)] / (combined_delta * delta[tuple(delta_slice0)])) - (f[tuple(slice1)] / (delta[tuple(delta_slice0)] * delta[tuple(delta_slice1)]))) + (f[tuple(slice2)] / (combined_delta * delta[tuple(delta_slice1)]))))
    slice0[axis] = slice(None, 1)
    slice1[axis] = slice(1, 2)
    slice2[axis] = slice(2, 3)
    delta_slice0[axis] = slice(None, 1)
    delta_slice1[axis] = slice(1, 2)
    combined_delta = (delta[tuple(delta_slice0)] + delta[tuple(delta_slice1)])
    left = (2 * (((f[tuple(slice0)] / (combined_delta * delta[tuple(delta_slice0)])) - (f[tuple(slice1)] / (delta[tuple(delta_slice0)] * delta[tuple(delta_slice1)]))) + (f[tuple(slice2)] / (combined_delta * delta[tuple(delta_slice1)]))))
    slice0[axis] = slice((- 3), (- 2))
    slice1[axis] = slice((- 2), (- 1))
    slice2[axis] = slice((- 1), None)
    delta_slice0[axis] = slice((- 2), (- 1))
    delta_slice1[axis] = slice((- 1), None)
    combined_delta = (delta[tuple(delta_slice0)] + delta[tuple(delta_slice1)])
    right = (2 * (((f[tuple(slice0)] / (combined_delta * delta[tuple(delta_slice0)])) - (f[tuple(slice1)] / (delta[tuple(delta_slice0)] * delta[tuple(delta_slice1)]))) + (f[tuple(slice2)] / (combined_delta * delta[tuple(delta_slice1)]))))
    return concatenate((left, center, right), axis=axis)