def apply(self, fd, u): for axis, order in self.derivs.items(): if self.spac: u = fd.diff(u, self.spac[axis], order, axis, coefficients(order, fd.acc)) else: coefs = [] for i in range(len(self.coords[axis])): coefs.append(coefficients_non_uni(order, fd.acc, self.coords[axis], i)) u = fd.diff_non_uni(u, self.coords[axis], axis, coefs) return u
def diff(self, y, h, acc): """The core function to take a partial derivative on a uniform grid. Central coefficients will be used whenever possible. Backward or forward coefficients will be used if not enough points are available on either side, i.e. forward coefficients for the low index boundary and backward coefficients for the high index boundary. """ dim = self.axis coefs = coefficients(self.order, acc) deriv = self.order try: npts = y.shape[dim] except AttributeError as err: raise ValueError( "FinDiff objects can only be applied to arrays or evaluated(!) functions returning arrays") from err scheme = "center" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] num_bndry_points = len(weights) // 2 ref_slice = slice(num_bndry_points, npts - num_bndry_points, 1) off_slices = [self._shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets))] yd = np.zeros_like(y) self._apply_to_array(yd, y, weights, off_slices, ref_slice, dim) scheme = "forward" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] ref_slice = slice(0, num_bndry_points, 1) off_slices = [self._shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets))] self._apply_to_array(yd, y, weights, off_slices, ref_slice, dim) scheme = "backward" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] ref_slice = slice(npts - num_bndry_points, npts, 1) off_slices = [self._shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets))] self._apply_to_array(yd, y, weights, off_slices, ref_slice, dim) h_inv = 1. / h ** deriv return yd * h_inv
def matrix(self, shape, h=None, acc=None): if isinstance(h, dict): h = h[self.axis] acc = self._properties(self.acc, acc, 2) ndims = len(shape) siz = np.prod(shape) long_indices_nd = long_indices_as_ndarray(shape) axis, order = self.axis, self.order mat = sparse.lil_matrix((siz, siz)) coeff_dict = coefficients(order, acc) for scheme in ['center', 'forward', 'backward']: offsets_1d = coeff_dict[scheme]['offsets'] coeffs = coeff_dict[scheme]['coefficients'] # translate offsets of given scheme to long format offsets_long = [] for o_1d in offsets_1d: o_nd = np.zeros(ndims) o_nd[axis] = o_1d o_long = to_long_index(o_nd, shape) offsets_long.append(o_long) # determine points where to evaluate current scheme in long format if scheme == 'center': multi_slice = [slice(None, None)] * ndims multi_slice[axis] = slice(1, -1) Is = long_indices_nd[tuple(multi_slice)].reshape(-1) elif scheme == 'forward': multi_slice = [slice(None, None)] * ndims multi_slice[axis] = 0 Is = long_indices_nd[tuple(multi_slice)].reshape(-1) else: multi_slice = [slice(None, None)] * ndims multi_slice[axis] = -1 Is = long_indices_nd[tuple(multi_slice)].reshape(-1) for o, c in zip(offsets_long, coeffs): v = c / h**order mat[Is, Is + o] = v mat = sparse.csr_matrix(mat) return mat
def _diff_general(y, h, deriv, dim, acc, coefs=None): if coefs is None: coefs = coefficients(deriv, acc) npts = y.shape[dim] scheme = "center" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] nbndry = len(weights) // 2 ref_slice = slice(nbndry, npts - nbndry, 1) off_slices = [ _shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets)) ] yd = np.zeros_like(y) _apply_to_array(yd, y, weights, off_slices, ref_slice, dim) scheme = "forward" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] ref_slice = slice(0, nbndry, 1) off_slices = [ _shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets)) ] _apply_to_array(yd, y, weights, off_slices, ref_slice, dim) scheme = "backward" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] ref_slice = slice(npts - nbndry, npts, 1) off_slices = [ _shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets)) ] _apply_to_array(yd, y, weights, off_slices, ref_slice, dim) h_inv = 1. / h**deriv return yd * h_inv[dim]
def __init__(self, h=[1.], dims=[0], acc=2): """Constructor for Finite Difference operator Parameters: ---------- h array-like The grid spacing along each axis. dims array-like The axes along which to take the derivatives. Multiple values mean higher derivative. acc int The accuracy order of the finite difference scheme. Example: -------- Suppose f is a four-dimensional array. The second partial derivative with respect to the second axis, \frac{\partial^2 f}{\partial z^2}, on a grid with equidistant spacing h=[0.1, 0.1, 0.1, 0.1] is given by FinDiff(h=[0.1, 0.1, 0.1, 0.1], dims=[1, 1]) """ if not hasattr(h, "__len__"): self._h = np.array([h]) else: self._h = np.array(h) if not hasattr(dims, "__len__"): self._dims = np.array([dims]) else: self._dims = np.array(dims) self._acc = acc ndims = len(self._h) self._derivs = [np.sum(self._dims == i) for i in range(ndims)] self._coefs = [] for i in range(ndims): self._coefs.append(coefficients(self._derivs[i], acc))
def _diff(self, y, h, deriv, dim, acc, coefs=None): """The core function to take a partial derivative on a uniform grid. """ if coefs is None: coefs = coefficients(deriv, acc) npts = y.shape[dim] scheme = "center" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] nbndry = len(weights) // 2 ref_slice = slice(nbndry, npts - nbndry, 1) off_slices = [self._shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets))] yd = np.zeros_like(y) self._apply_to_array(yd, y, weights, off_slices, ref_slice, dim) scheme = "forward" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] ref_slice = slice(0, nbndry, 1) off_slices = [self._shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets))] self._apply_to_array(yd, y, weights, off_slices, ref_slice, dim) scheme = "backward" weights = coefs[scheme]["coefficients"] offsets = coefs[scheme]["offsets"] ref_slice = slice(npts - nbndry, npts, 1) off_slices = [self._shift_slice(ref_slice, offsets[k], npts) for k in range(len(offsets))] self._apply_to_array(yd, y, weights, off_slices, ref_slice, dim) h_inv = 1./h**deriv return yd * h_inv
def _determine_coefs(self): """Calculates the finite difference coefficients for the requested partial derivatives""" for axis, partial in self.derivs.items(): coefs = coefficients(partial["order"], self.acc) self.derivs[axis]["coefs"] = coefs