out2 : (N,2) float array Array of normalized vectors that are orthogonal to the corresponding vectors in *n* and in *out1* ''' out1 = np.empty_like(n,dtype=float) out1[:,0] = -np.sum(n,axis=1) + n[:,0] out1[:,1:] = n[:,[0]] out1 /= np.linalg.norm(out1,axis=1)[:,None] # normalize length out2 = np.cross(n,out1) # find vectors that are orthogonal to *n* and *out1* out2 /= np.linalg.norm(out2,axis=1)[:,None] # normalize length return out1,out2 # generate nodes. Note that this may take a while nodes,smpid = menodes(N,vert,smp,rho=density_func,itr=50) # find which nodes are attached to each simplex int_idx = np.nonzero(smpid == -1)[0].tolist() roller_idx = np.nonzero((smpid >= 0) & (smpid <= 9))[0].tolist() free_idx = np.nonzero(smpid > 9)[0].tolist() # find normal vectors to each free surface node simplex_normals = simplex_outward_normals(vert,smp) free_normals = simplex_normals[smpid[free_idx]] # find the normal vectors to each roller node roller_normals = simplex_normals[smpid[roller_idx]] # find two orthogonal vectors that are parallel to the surface at each # roller node. This is used to determine the directions along which # traction forces will be constrained. Note that any two orthogonal # vectors that are parallel to the surface would do. roller_parallels1,roller_parallels2 = find_orthogonals(roller_normals) # add ghost nodes next to free and roller nodes
# symbolic definition of the solution x, y = sympy.symbols('x,y') r = sympy.sqrt(x**2 + y**2) true_soln_sym = (1 - r) * sympy.sin(x) * sympy.cos(y) # numerical solution true_soln = sympy.lambdify((x, y), true_soln_sym, 'numpy') # symbolic forcing term forcing_sym = true_soln_sym.diff(x, x) + true_soln_sym.diff(y, y) # numerical forcing term 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. The third argument to weight_matrix # describes the derivates order for each spatial dimension A = scipy.sparse.lil_matrix((N, N)) A[interior, :] = weight_matrix(nodes[interior], nodes, [[2, 0], [0, 2]]) A[boundary, :] = weight_matrix(nodes[boundary], nodes, [0, 0]) # convert A to a csr matrix for efficient solving A = A.tocsr()
from rbf.nodes import menodes import matplotlib.pyplot as plt # define the problem domain vert = np.array([[0.762,0.057],[0.492,0.247],[0.225,0.06 ],[0.206,0.056], [0.204,0.075],[0.292,0.398],[0.043,0.609],[0.036,0.624], [0.052,0.629],[0.373,0.63 ],[0.479,0.953],[0.49 ,0.966], [0.503,0.952],[0.611,0.629],[0.934,0.628],[0.95 ,0.622], [0.941,0.607],[0.692,0.397],[0.781,0.072],[0.779,0.055]]) smp = np.array([[0,1],[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8],[8,9], [9,10],[10,11],[11,12],[12,13],[13,14],[14,15],[15,16], [16,17],[17,18],[18,19],[19,0]]) N = 500 # total number of nodes nodes,smpid = menodes(N,vert,smp) # generate nodes boundary, = (smpid>=0).nonzero() # identify boundary nodes interior, = (smpid==-1).nonzero() # identify interior nodes # create left-hand-side matrix and right-hand-side vector A = np.empty((N,N)) A[interior] = phs3(nodes[interior],nodes,diff=[2,0]) A[interior] += phs3(nodes[interior],nodes,diff=[0,2]) A[boundary,:] = phs3(nodes[boundary],nodes) d = np.empty(N) d[interior] = -100.0 d[boundary] = 0.0 # Solve the PDE coeff = np.linalg.solve(A,d) # solve for the RBF coefficients itp = menodes(10000,vert,smp)[0] # interpolation points
import numpy as np from rbf.fd import weight_matrix from rbf.nodes import menodes, neighbors from rbf.geometry import contains, simplex_outward_normals import matplotlib.pyplot as plt from scipy.integrate import ode from scipy.interpolate import griddata from scipy.sparse.linalg import spsolve # define the problem domain vert = np.array([[0.0, 0.0], [2.0, 0.0], [2.0, 1.0], [1.0, 1.0], [1.0, 2.0], [0.0, 2.0]]) smp = np.array([[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]]) times = np.linspace(0.0, 2.0, 5) # output times N = 50000 # total number of nodes nodes, smpid = menodes(N, vert, smp) # generate nodes interior = np.nonzero(smpid == -1)[0].tolist() # identify boundary nodes boundary = np.nonzero(smpid >= 0)[0].tolist() # identify boundary nodes # calculate surface normal vector for each boundary node normals = simplex_outward_normals(vert, smp)[smpid[boundary]] # dx is the shortest distance between any two nodes dx = np.min(neighbors(nodes, 2)[1][:, 1]) # add ghost nodes to greatly improve accuracy at the free surface nodes = np.vstack((nodes, nodes[boundary] + 0.5 * dx * normals)) ghost = range(N, N + len(boundary)) # ghost node indices # create differentiation matrices for the interior and boundary nodes D = weight_matrix(nodes[interior + boundary], nodes, [(2, 0), (0, 2)], n=30) dD = weight_matrix(nodes[boundary], nodes, [(1, 0), (0, 1)], coeffs=normals.T, n=30)
# symbolic definition of the solution x,y = sympy.symbols('x,y') r = sympy.sqrt(x**2 + y**2) true_soln_sym = (1-r)*sympy.sin(x)*sympy.cos(y) # numerical solution true_soln = sympy.lambdify((x,y),true_soln_sym,'numpy') # symbolic forcing term forcing_sym = true_soln_sym.diff(x,x) + true_soln_sym.diff(y,y) # numerical forcing term 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. The third argument to weight_matrix # describes the derivates order for each spatial dimension A = scipy.sparse.lil_matrix((N,N)) A[interior,:] = weight_matrix(nodes[interior],nodes,[[2,0],[0,2]]) A[boundary,:] = weight_matrix(nodes[boundary],nodes,[0,0]) # convert A to a csr matrix for efficient solving A = A.tocsr()
# number of nodes N = 200 # define the vertices and simplices for cut annulus t = np.linspace(gap,2*np.pi,100) vert_outer = np.array([2*np.cos(t),2*np.sin(t)]).T vert_inner = np.array([np.cos(t[::-1]),np.sin(t[::-1])]).T vert = np.vstack((vert_outer,vert_inner)) smp = np.array([np.arange(200),np.roll(np.arange(200),-1)]).T # setting bound_force=True ensures that the edges where the annulus is # cut will have an appropriate number of boundary nodes. This also # makes the function considerably slower nodes,smpid = menodes(N,vert,smp,bound_force=True) # identify nodes associated with the different boundary types slit_top, = (smpid==199).nonzero() slit_bot, = (smpid==99).nonzero() # edges of the annulus minus the cut boundary, = ((smpid>=0) & (smpid!=199) & (smpid!=99)).nonzero() interior, = (smpid==-1).nonzero() # add ghost nodes ghost_nodes = make_ghost_nodes(nodes,smpid,boundary,vert,smp) nodes = np.vstack((nodes,ghost_nodes)) # ghost node indices ghost = N + np.arange(len(boundary)) # do not build stencils which cross this line
# symbolic definition of the solution x, y = sympy.symbols('x,y') r = sympy.sqrt(x**2 + y**2) true_soln_sym = (1 - r) * sympy.sin(x) * sympy.cos(y) # numerical solution true_soln = sympy.lambdify((x, y), true_soln_sym, 'numpy') # symbolic forcing term forcing_sym = true_soln_sym.diff(x, x) + true_soln_sym.diff(y, y) # numerical forcing term 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
# number of nodes N = 200 # define the vertices and simplices for cut annulus t = np.linspace(gap, 2 * np.pi, 100) vert_outer = np.array([2 * np.cos(t), 2 * np.sin(t)]).T vert_inner = np.array([np.cos(t[::-1]), np.sin(t[::-1])]).T vert = np.vstack((vert_outer, vert_inner)) smp = np.array([np.arange(200), np.roll(np.arange(200), -1)]).T # setting bound_force=True ensures that the edges where the annulus is # cut will have an appropriate number of boundary nodes. This also # makes the function considerably slower nodes, smpid = menodes(N, vert, smp, bound_force=True) # identify nodes associated with the different boundary types slit_top, = (smpid == 199).nonzero() slit_bot, = (smpid == 99).nonzero() # edges of the annulus minus the cut boundary, = ((smpid >= 0) & (smpid != 199) & (smpid != 99)).nonzero() interior, = (smpid == -1).nonzero() # do not build stencils which cross this line bnd_vert = np.array([[0.0, 0.0], [10 * np.cos(gap / 2.0), 10 * np.sin(gap / 2.0)]]) bnd_smp = np.array([[0, 1]]) weight_kwargs = {'vert': bnd_vert, 'smp': bnd_smp, 'n': 20} # build lhs # enforce laplacian on interior nodes
# symbolic definition of the solution x,y,z = sympy.symbols('x,y,z') r = sympy.sqrt(x**2 + y**2 + z**2) true_soln_sym = (1-r)*sympy.sin(x)*sympy.cos(y)*sympy.cos(z) # numerical solution true_soln = sympy.lambdify((x,y,z),true_soln_sym,'numpy') # symbolic forcing term forcing_sym = true_soln_sym.diff(x,x) + true_soln_sym.diff(y,y) + true_soln_sym.diff(z,z) # numerical forcing term forcing = sympy.lambdify((x,y,z),forcing_sym,'numpy') # smpid describes which boundary simplex, if any, the nodes are # attached to. If it is -1, then the node is in the interior vert,smp = rbf.domain.sphere() nodes,smpid = menodes(N,vert,smp,itr=10) interior, = np.nonzero(smpid==-1) boundary, = np.nonzero(smpid>=0) # 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. The third argument to weight_matrix # describes the derivates order for each spatial dimension A = scipy.sparse.lil_matrix((N,N)) A[interior,:] = weight_matrix(nodes[interior],nodes,[[2,0,0],[0,2,0],[0,0,2]]) A[boundary,:] = weight_matrix(nodes[boundary],nodes,[0,0,0]) # convert A to a csr matrix for efficient solving A = A.tocsr() # create the right-hand-side vector, consisting of the forcing term # for the interior nodes and zeros for the boundary nodes
from rbf.nodes import menodes # Define the problem domain with line segments. vert = np.array([[0.0,0.0],[2.0,0.0],[2.0,1.0], [1.0,1.0],[1.0,2.0],[0.0,2.0]]) smp = np.array([[0,1],[1,2],[2,3],[3,4],[4,5],[5,0]]) # define a node density function. It takes an (N,D) array of positions # and returns an (N,) array of normalized densities between 0 and 1 def rho(x): r = np.sqrt((x[:,0] - 1.0)**2 + (x[:,1] - 1.0)**2) return 0.1 + 0.9/((r/0.25)**4 + 1.0) N = 1000 # total number of nodes nodes,smpid = menodes(N,vert,smp,rho=rho) # identify boundary and interior nodes interior = smpid == -1 boundary = smpid > -1 fig,ax = plt.subplots(figsize=(6,6)) # plot the domain for s in smp: ax.plot(vert[s,0],vert[s,1],'k-') ax.plot(nodes[interior,0],nodes[interior,1],'k.') ax.plot(nodes[boundary,0],nodes[boundary,1],'b.') ax.set_aspect('equal') fig.tight_layout() plt.savefig('../figures/nodes.a.png') plt.show()
# symbolic definition of the solution x,y = sympy.symbols('x,y') r = sympy.sqrt(x**2 + y**2) true_soln_sym = (1-r)*sympy.sin(x)*sympy.cos(y) # numerical solution true_soln = sympy.lambdify((x,y),true_soln_sym,'numpy') # symbolic forcing term forcing_sym = true_soln_sym.diff(x,x) + true_soln_sym.diff(y,y) # numerical forcing term 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
from rbf.fd import weight_matrix from rbf.nodes import menodes import matplotlib.pyplot as plt # define the problem domain vert = np.array([[0.762,0.057],[0.492,0.247],[0.225,0.06 ],[0.206,0.056], [0.204,0.075],[0.292,0.398],[0.043,0.609],[0.036,0.624], [0.052,0.629],[0.373,0.63 ],[0.479,0.953],[0.49 ,0.966], [0.503,0.952],[0.611,0.629],[0.934,0.628],[0.95 ,0.622], [0.941,0.607],[0.692,0.397],[0.781,0.072],[0.779,0.055]]) smp = np.array([[0,1],[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8],[8,9], [9,10],[10,11],[11,12],[12,13],[13,14],[14,15],[15,16], [16,17],[17,18],[18,19],[19,0]]) dt = 0.000025 # time step size N = 100000 # total number of nodes nodes,smpid = menodes(N,vert,smp) # generate nodes boundary, = (smpid>=0).nonzero() # identify boundary nodes interior, = (smpid==-1).nonzero() # identify interior nodes D = weight_matrix(nodes[interior],nodes,[[2,0],[0,2]],n=30) r = np.linalg.norm(nodes-np.array([0.49,0.46]),axis=1) u_prev = 1.0/(1 + (r/0.01)**4) # create initial conditions u_curr = 1.0/(1 + (r/0.01)**4) fig,axs = plt.subplots(2,2,figsize=(7,7)) axs = [axs[0][0],axs[0][1],axs[1][0],axs[1][1]] for i in range(15001): u_next = dt**2*D.dot(u_curr) + 2*u_curr[interior] - u_prev[interior] u_prev[:] = u_curr u_curr[interior] = u_next if i in [0,5000,10000,15000]: ax = axs[[0,5000,10000,15000].index(i)] p = ax.scatter(nodes[:,0],nodes[:,1],s=3,c=np.array(u_curr,copy=True),
Returns ------- out : (N,2) float array Array of normalized vectors that are orthogonal to the corresponding vector in *n*. ''' out = np.empty_like(n,dtype=float) out[:,0] = -np.sum(n,axis=1) + n[:,0] out[:,1:] = n[:,[0]] out /= np.linalg.norm(out,axis=1)[:,None] return out # generate nodes. Note that this may take a while nodes,smpid = menodes(N,vert,smp,rho=node_density) # roller nodes are on the bottom and sides of the domain roller_idx = np.nonzero((smpid == 0) | (smpid == 1) | (smpid == 299))[0].tolist() # free nodes are on the top of the domain free_idx = np.nonzero(~((smpid == -1) | (smpid == 0) | (smpid == 1) | (smpid == 299)))[0].tolist() # interior nodes int_idx = np.nonzero(smpid == -1)[0].tolist() # find normal vectors for each simplex simplex_normals = simplex_outward_normals(vert,smp) # find normal vectors for each boundary node roller_normals = simplex_normals[smpid[roller_idx]]
# make gray scale image img = Image.open('Lenna.png') imga = np.array(img,dtype=float)/256.0 gray = np.linalg.norm(imga,axis=-1) # normalize so that the max value is 1 gray = gray/gray.max() # define the node density function def rho(p): # x and y are mapped to integers between 0 and 512 p = p*512 p = np.array(p,dtype=int) return 1.0001 - gray[511-p[:,1],p[:,0]] nodes,smpid = menodes(N,vert,smp,rho=rho) interior, = np.nonzero(smpid==-1) boundary, = np.nonzero(smpid>=0) ### plot results ##################################################################### fig,ax = plt.subplots() # plot interior nodes ax.plot(nodes[interior,0],nodes[interior,1],'k.',markersize=2) # plot boundary nodes ax.plot(nodes[boundary,0],nodes[boundary,1],'b.',markersize=2) ax.set_aspect('equal') fig.tight_layout() plt.savefig('figures/lenna.png') plt.show()