def _vertex_outward_normals(vert, smp): ''' Get the "normal" vectors for each vertex of the domain. Here the normal vector is the average of the normal vectors for each simplex that the vertex is part of. ''' simplex_normals = simplex_outward_normals(vert, smp) vertex_normals = np.zeros_like(vert) for i, s in enumerate(smp): vertex_normals[s] += simplex_normals[i] vertex_normals /= np.linalg.norm(vertex_normals, axis=1)[:, None] return vertex_normals
def _make_normal_vectors(smpid, vert, smp): ''' Create an (n, d) array of normal vectors for each node. If the node is not a boundary node then the corresponding row contains NaN's. ''' # get the normal vectors for each simplex simplex_normals = simplex_outward_normals(vert, smp) # allocate an array of nans for the node normal vectors normals = np.full((smpid.shape[0], vert.shape[1]), np.nan) # find which nodes are attached to a simplex. set those normal # vectors to be the normal vector for the simplex they are attached # to normals[smpid >= 0] = simplex_normals[smpid[smpid >= 0]] return normals
def make_ghost_nodes(nodes,smpid,idx,vert,smp): # create nodes that are just outside the boundary nodes = np.asarray(nodes) smpid = np.asarray(smpid) sub_nodes = nodes[idx] sub_smpid = smpid[idx] if np.any(sub_smpid == -1): raise ValueError('cannot make a ghost node for an interior node') norms = simplex_outward_normals(vert,smp)[sub_smpid] dummy,dx = rbf.stencil.nearest(sub_nodes,nodes,2,vert=vert,smp=smp) # distance to the nearest neighbors dx = dx[:,[1]] ghosts = sub_nodes + dx*norms return ghosts
def make_ghost_nodes(nodes, smpid, idx, vert, smp): # create nodes that are just outside the boundary nodes = np.asarray(nodes) smpid = np.asarray(smpid) sub_nodes = nodes[idx] sub_smpid = smpid[idx] if np.any(sub_smpid == -1): raise ValueError('cannot make a ghost node for an interior node') norms = simplex_outward_normals(vert, smp)[sub_smpid] dummy, dx = rbf.stencil.nearest(sub_nodes, nodes, 2, vert=vert, smp=smp) # distance to the nearest neighbors dx = dx[:, [1]] ghosts = sub_nodes + dx * norms return ghosts
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 dx = np.min(neighbors(nodes,2)[1][:,1]) nodes = np.vstack((nodes,nodes[free_idx] + dx*free_normals)) nodes = np.vstack((nodes,nodes[roller_idx] + dx*roller_normals)) # build the "left hand side" matrices for body force constraints A_body = elastic3d_body_force(nodes[int_idx+free_idx+roller_idx],nodes,lamb=lamb,mu=mu,n=n) A_body_x,A_body_y,A_body_z = (hstack(i) for i in A_body)
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) # create initial and boundary conditions r = np.sqrt((nodes[interior + boundary, 0] - 0.5)**2 + (nodes[interior + boundary, 1] - 0.5)**2) u_init = 1.0 / (1 + (r / 0.05)**4) # initial u in the interior
bnd_vert = np.array([[0.0,0.0],[5.0,0.0]]) bnd_smp = np.array([[0,1]]) weight_kwargs = {'vert':bnd_vert,'smp':bnd_smp,'n':20,'order':2} # build lhs # enforce laplacian on interior nodes A_interior = weight_matrix(nodes[interior],nodes, [[2,0],[0,2]], **weight_kwargs) A_ghost = weight_matrix(nodes[boundary],nodes, [[2,0],[0,2]], **weight_kwargs) # find boundary normal vectors normals = simplex_outward_normals(vert,smp)[smpid[boundary]] n1 = scipy.sparse.diags(normals[:,0],0) n2 = scipy.sparse.diags(normals[:,1],0) # enforce free surface boundary conditions A_boundary = (n1*weight_matrix(nodes[boundary],nodes,[1,0],**weight_kwargs) + n2*weight_matrix(nodes[boundary],nodes,[0,1],**weight_kwargs)) # These next two matrices are really just identity matrices padded with zeros A_slit_top = weight_matrix(nodes[slit_top],nodes,[0,0],**weight_kwargs) A_slit_bot = weight_matrix(nodes[slit_bot],nodes,[0,0],**weight_kwargs) A = scipy.sparse.vstack((A_interior,A_ghost,A_boundary, A_slit_top,A_slit_bot)) # build the rhs in the same order d_interior = np.zeros(interior.shape[0])