def _rhs(x, s, eps, powers, diff, basis): ''' Returns the differentiated RBF and polynomial terms evaluated at x ''' x = x[None, :] # number of nodes in the stencil and the number of dimensions Ns, Ndim = s.shape # number of monomial terms Np = powers.shape[0] d = np.empty(Ns + Np, dtype=float) d[:Ns] = basis(x, s, eps=eps, diff=diff)[0, :] d[Ns:] = rbf.poly.mvmonos(x, powers, diff=diff)[0, :] return d
def __init__(self, y, d, sigma=None, eps=1.0, basis=rbf.basis.phs3, order=1, extrapolate=True): y = np.asarray(y) assert_shape(y, (None, None), 'y') d = np.asarray(d) assert_shape(d, (y.shape[0], ), 'd') q, dim = y.shape if sigma is None: # if sigma is not specified then it is zeros sigma = np.zeros(q) elif np.isscalar(sigma): # if a float is specified then use it as the uncertainties for # all observations sigma = np.repeat(sigma, q) else: sigma = np.asarray(sigma) assert_shape(sigma, (y.shape[0], ), 'sigma') # form block consisting of the RBF and uncertainties on the # diagonal K = basis(y, y, eps=eps) Cd = scipy.sparse.diags(sigma**2) # form the block consisting of the monomials powers = rbf.poly.powers(order, dim) P = rbf.poly.mvmonos(y, powers) # create zeros vector for the right-hand-side z = np.zeros((powers.shape[0], )) # solve for the RBF and mononomial coefficients basis_coeff, poly_coeff = PartitionedSolver(K + Cd, P).solve(d, z) self._y = y self._basis = basis self._order = order self._eps = eps self._basis_coeff = basis_coeff self._poly_coeff = poly_coeff self._powers = powers self.extrapolate = extrapolate
def _lhs(s, eps, powers, basis): ''' Returns the transposed RBF alternant matrix with added polynomial terms and constraints ''' # number of nodes in the stencil and the number of dimensions Ns, Ndim = s.shape # number of monomial terms Np = powers.shape[0] # deriviative orders diff = np.zeros(Ndim, dtype=int) A = np.zeros((Ns + Np, Ns + Np), dtype=float) A[:Ns, :Ns] = basis(s, s, eps=eps, diff=diff).T Ap = rbf.poly.mvmonos(s, powers, diff=diff) A[Ns:, :Ns] = Ap.T A[:Ns, Ns:] = Ap return A
def _interpolation_matrix(xitp,x,diff,eps,basis,order): ''' returns the matrix that maps the coefficients to the function values at the interpolation points ''' # number of interpolation points and spatial dimensions I,D = xitp.shape # number of observation points N = x.shape[0] # powers for the additional polynomials powers = rbf.poly.powers(order,D) # number of polynomial terms P = powers.shape[0] # allocate array A = np.zeros((I,N+P)) A[:,:N] = basis(xitp,x,eps=eps,diff=diff) A[:,N:] = rbf.poly.mvmonos(xitp,powers,diff=diff) return A
def _coefficient_matrix(x, eps, basis, order): ''' returns the matrix used to compute the radial basis function coefficients ''' # number of observation points and spatial dimensions N, D = x.shape # powers for the additional polynomials powers = rbf.poly.monomial_powers(order, D) # number of polynomial terms P = powers.shape[0] # allocate array A = np.zeros((N + P, N + P)) A[:N, :N] = basis(x, x, eps=eps) Ap = rbf.poly.mvmonos(x, powers) A[N:, :N] = Ap.T A[:N, N:] = Ap return A
def _coefficient_matrix(x,eps,basis,order): ''' returns the matrix used to compute the radial basis function coefficients ''' # number of observation points and spatial dimensions N,D = x.shape # powers for the additional polynomials powers = rbf.poly.powers(order,D) # number of polynomial terms P = powers.shape[0] # allocate array A = np.zeros((N+P,N+P)) A[:N,:N] = basis(x,x,eps=eps) Ap = rbf.poly.mvmonos(x,powers) A[N:,:N] = Ap.T A[:N,N:] = Ap return A
def weights(x, s, diffs, coeffs=None, basis=rbf.basis.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 the function 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. basis : rbf.basis.RBF, 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.]) Notes ----- This function may become unstable with high order polynomials (i.e., `order` is high). This can be somewhat remedied by shifting the coordinate system so that x is zero 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) # stencil size and number of dimensions size, dim = s.shape 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') max_order = _max_poly_order(size, dim) if order is None: 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') # get the powers for the added monomials powers = rbf.poly.powers(order, dim) # evaluate the RBF and monomials at each point in the stencil. This # becomes the left-hand-side A = basis(s, s, eps=eps) P = rbf.poly.mvmonos(s, powers) # Evaluate the RBF and monomials for each term in the differential # operator. This becomes the right-hand-side. a = coeffs[0] * basis(x[None, :], s, eps=eps, diff=diffs[0]) p = coeffs[0] * rbf.poly.mvmonos(x[None, :], powers, diff=diffs[0]) for c, d in zip(coeffs[1:], diffs[1:]): a += c * basis(x[None, :], s, eps=eps, diff=d) p += c * rbf.poly.mvmonos(x[None, :], powers, diff=d) # squeeze `a` and `p` into 1d arrays. `a` is ran through as_array # because it may be sparse. a = rbf.linalg.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, basis, order, s))
forcing = sympy.lambdify((x, y), forcing_sym, 'numpy') # define a circular domain vert, smp = rbf.domain.circle() nodes, smpid = menodes(N, vert, smp) # smpid describes which boundary simplex, if any, the nodes are # attached to. If it is -1, then the node is in the interior boundary, = (smpid >= 0).nonzero() interior, = (smpid == -1).nonzero() # create the left-hand-side matrix which is the Laplacian of the basis # function for interior nodes and the undifferentiated basis functions # for the boundary nodes A = np.zeros((N, N)) A[interior] = basis(nodes[interior], nodes, diff=[2, 0]) A[interior] += basis(nodes[interior], nodes, diff=[0, 2]) A[boundary, :] = basis(nodes[boundary], nodes) # create the right-hand-side vector, consisting of the forcing term # for the interior nodes and zeros for the boundary nodes d = np.zeros(N) d[interior] = forcing(nodes[interior, 0], nodes[interior, 1]) d[boundary] = true_soln(nodes[boundary, 0], nodes[boundary, 1]) # find the RBF coefficients that solve the PDE coeff = np.linalg.solve(A, d) # create a collection of interpolation points to evaluate the # solution. It is easiest to just call menodes again itp, dummy = menodes(10000, vert, smp, itr=0)
forcing = sympy.lambdify((x,y),forcing_sym,'numpy') # define a circular domain vert,smp = rbf.domain.circle() nodes,smpid = menodes(N,vert,smp) # smpid describes which boundary simplex, if any, the nodes are # attached to. If it is -1, then the node is in the interior boundary, = (smpid>=0).nonzero() interior, = (smpid==-1).nonzero() # create the left-hand-side matrix which is the Laplacian of the basis # function for interior nodes and the undifferentiated basis functions # for the boundary nodes A = np.zeros((N,N)) A[interior] = basis(nodes[interior],nodes,diff=[2,0]) A[interior] += basis(nodes[interior],nodes,diff=[0,2]) A[boundary,:] = basis(nodes[boundary],nodes) # create the right-hand-side vector, consisting of the forcing term # for the interior nodes and zeros for the boundary nodes d = np.zeros(N) d[interior] = forcing(nodes[interior,0],nodes[interior,1]) d[boundary] = true_soln(nodes[boundary,0],nodes[boundary,1]) # find the RBF coefficients that solve the PDE coeff = np.linalg.solve(A,d) # create a collection of interpolation points to evaluate the # solution. It is easiest to just call menodes again itp,dummy = menodes(10000,vert,smp,itr=0)