def __init__(self, y, d, sigma=None, eps=1.0, phi=phs3, order=1, extrapolate=True): y = np.asarray(y) assert_shape(y, (None, None), 'y') nobs, dim = y.shape d = np.asarray(d) assert_shape(d, (nobs, ), 'd') if sigma is None: # if sigma is not specified then it is zeros sigma = np.zeros(nobs) elif np.isscalar(sigma): # if a float is specified then use it as the uncertainties for # all observations sigma = np.repeat(sigma, nobs) else: sigma = np.asarray(sigma) assert_shape(sigma, (nobs, ), 'sigma') phi = get_rbf(phi) # form block consisting of the RBF and uncertainties on the # diagonal K = phi(y, y, eps=eps) Cd = scipy.sparse.diags(sigma**2) # form the block consisting of the monomials pwr = powers(order, dim) P = mvmonos(y, pwr) # create zeros vector for the right-hand-side z = np.zeros((pwr.shape[0], )) # solve for the RBF and mononomial coefficients phi_coeff, poly_coeff = PartitionedSolver(K + Cd, P).solve(d, z) self._y = y self._phi = phi self._order = order self._eps = eps self._phi_coeff = phi_coeff self._poly_coeff = poly_coeff self._pwr = pwr self.extrapolate = extrapolate
return out def clear_rbf_caches(): ''' Clear the caches of numerical functions for all the RBF instances ''' for inst in RBF._INSTANCES: if inst() is not None: inst().clear_cache() ## Instantiate some common RBFs ##################################################################### _phs8_limits = {} _phs8_limits.update((tuple(i), 0) for i in powers(7, 1)) _phs8_limits.update((tuple(i), 0) for i in powers(7, 2)) _phs8_limits.update((tuple(i), 0) for i in powers(7, 3)) phs8 = RBF((_EPS * _R)**8 * sympy.log(_EPS * _R), tol=1e-10, limits=_phs8_limits) _phs7_limits = {} _phs7_limits.update((tuple(i), 0) for i in powers(6, 1)) _phs7_limits.update((tuple(i), 0) for i in powers(6, 2)) _phs7_limits.update((tuple(i), 0) for i in powers(6, 3)) phs7 = RBF((_EPS * _R)**7, tol=1e-10, limits=_phs7_limits) _phs6_limits = {} _phs6_limits.update((tuple(i), 0) for i in powers(5, 1)) _phs6_limits.update((tuple(i), 0) for i in powers(5, 2))
def weights(x, s, diffs, coeffs=None, phi=phs3, order=None, eps=1.0): ''' Returns the weights which map a functions values at `s` to an approximation of that functions derivative at `x`. The weights are computed using the RBF-FD method described in [1]. In this function `x` is a single point in D-dimensional space. Use `weight_matrix` to compute the weights for multiple point. Parameters ---------- x : (D,) array Target point. The weights will approximate the derivative at this point. s : (N, D) array Stencil points. The derivative will be approximated with a weighted sum of values at this point. diffs : (D,) int array or (K, D) int array Derivative orders for each spatial dimension. For example `[2, 0]` indicates that the weights should approximate the second derivative with respect to the first spatial dimension in two-dimensional space. diffs can also be a (K, D) array, where each (D,) sub-array is a term in a differential operator. For example the two-dimensional Laplacian can be represented as `[[2, 0], [0, 2]]`. coeffs : (K,) array, optional Coefficients for each term in the differential operator specified with `diffs`. Defaults to an array of ones. If `diffs` was specified as a (D,) array then `coeffs` should be a length 1 array. phi : rbf.basis.RBF instance or str, optional Type of RBF. Select from those available in `rbf.basis` or create your own. order : int, optional Order of the added polynomial. This defaults to the highest derivative order. For example, if `diffs` is `[[2, 0], [0, 1]]`, then order is set to 2. eps : float or (N,) array, optional Shape parameter for each RBF, which have centers `s`. This only makes a difference when using RBFs that are not scale invariant. All the predefined RBFs except for the odd order polyharmonic splines are not scale invariant. Returns ------- out : (N,) array RBF-FD weights Examples -------- Calculate the weights for a one-dimensional second order derivative. >>> x = np.array([1.0]) >>> s = np.array([[0.0], [1.0], [2.0]]) >>> diff = (2,) >>> weights(x, s, diff) array([ 1., -2., 1.]) Calculate the weights for estimating an x derivative from three points in a two-dimensional plane >>> x = np.array([0.25, 0.25]) >>> s = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]) >>> diff = (1, 0) >>> weights(x, s, diff) array([ -1., 1., 0.]) References ---------- [1] Fornberg, B. and N. Flyer. A Primer on Radial Basis Functions with Applications to the Geosciences. SIAM, 2015. ''' x = np.asarray(x, dtype=float) assert_shape(x, (None, ), 'x') s = np.asarray(s, dtype=float) assert_shape(s, (None, x.shape[0]), 's') diffs = np.asarray(diffs, dtype=int) diffs = _reshape_diffs(diffs) if coeffs is None: coeffs = np.ones(diffs.shape[0], dtype=float) else: coeffs = np.asarray(coeffs, dtype=float) assert_shape(coeffs, (diffs.shape[0], ), 'coeffs') phi = get_rbf(phi) # stencil size and number of dimensions size, dim = s.shape # get the maximum polynomial order allowed for this stencil size max_order = _max_poly_order(size, dim) if order is None: # If the polynomial order is not specified, make it equal to the derivative # order, provided that the stencil size is large enough. order = _default_poly_order(diffs) order = min(order, max_order) if order > max_order: raise ValueError('Polynomial order is too high for the stencil size') # center the stencil on `x` for improved numerical stability s = s - x x = np.zeros_like(x) # get the powers for the added monomials pwr = powers(order, dim) # evaluate the RBF and monomials at each point in the stencil. This becomes # the left-hand-side A = phi(s, s, eps=eps) P = mvmonos(s, pwr) # Evaluate the RBF and monomials for each term in the differential operator. # This becomes the right-hand-side. a = np.zeros((1, size), dtype=float) p = np.zeros((1, pwr.shape[0]), dtype=float) for c, d in zip(coeffs, diffs): a += c * phi(x[None, :], s, eps=eps, diff=d) p += c * mvmonos(x[None, :], pwr, diff=d) # squeeze `a` and `p` into 1d arrays. `a` is ran through as_array because it # may be sparse. a = as_array(a)[0] p = p[0] # attempt to compute the RBF-FD weights try: w = PartitionedSolver(A, P).solve(a, p)[0] return w except np.linalg.LinAlgError: raise np.linalg.LinAlgError( 'An error was raised while computing the RBF-FD weights at point %s ' 'with the RBF %s and the polynomial order %s. This may be due to a ' 'stencil with duplicate or collinear points. The stencil contains the ' 'following points:\n%s' % (x, phi, order, s))