# 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, 10.0, 20) # output times n_nominal = 1000 # total number of nodes lamb = 1.0 mu = 1.0 rho = 1.0 stencil_size = 30 order = 2 basis = rbf.basis.phs5 nodes, idx, normals = min_energy_nodes( n_nominal, vert, smp, boundary_groups={'all': range(len(smp))}, boundary_groups_with_ghosts=['all'], include_vertices=False) n = nodes.shape[0] # create initial and boundary conditions r = np.sqrt((nodes[idx['interior'], 0] - 0.5)**2 + (nodes[idx['interior'], 1] - 0.5)**2) u_init = np.zeros_like(nodes) u_init[idx['interior'], 0] = 1.0 / (1 + (r / 0.2)**4) u_init[idx['interior'], 1] = 1.0 / (1 + (r / 0.2)**4) v_init = np.zeros_like(nodes)
boundary_groups_with_ghosts = ['outside_corner'] # total number of nodes N = 1000 # 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.2 + 0.8 / ((r / 0.3)**4 + 1.0) nodes, groups, normals = min_energy_nodes( N, vert, smp, rho=rho, boundary_groups=boundary_groups, boundary_groups_with_ghosts=boundary_groups_with_ghosts, include_vertices=True) # plot the results fig, ax = plt.subplots(figsize=(6, 6)) # plot the domain for s in smp: ax.plot(vert[s, 0], vert[s, 1], 'k-') # plot the different node groups and their normal vectors for i, (name, idx) in enumerate(groups.items()): ax.plot(nodes[idx, 0], nodes[idx, 1], 'C%s.' % i, label=name, ms=8) ax.quiver(nodes[idx, 0], nodes[idx, 1],
N = 500 # total number of nodes. n = 20 # stencil size. Increase this will generally improve accuracy basis = phs3 # radial basis function used to compute the weights. Odd # order polyharmonic splines (e.g., phs3) have always # performed well for me and they do not require the user # to tune a shape parameter. Use higher order # polyharmonic splines for higher order PDEs. order = 2 # Order of the added polynomials. This should be at least as # large as the order of the PDE being solved (2 in this # case). Larger values may improve accuracy # generate nodes nodes, groups, _ = min_energy_nodes(N, vert, smp) # create the "left hand side" matrix. # create the component which evaluates the PDE A_interior = weight_matrix(nodes[groups['interior']], nodes, diffs=[[2, 0], [0, 2]], n=n, basis=basis, order=order) # create the component for the fixed boundary conditions A_boundary = weight_matrix(nodes[groups['boundary:all']], nodes, diffs=[0, 0]) # Add the components to the corresponding rows of `A` A = csc_matrix((N, N)) A = add_rows(A,A_interior,groups['interior']) A = add_rows(A,A_boundary,groups['boundary:all']) # create "right hand side" vector
''' import numpy as np from rbf.fd import weight_matrix from rbf.nodes import min_energy_nodes from rbf.geometry import contains import matplotlib.pyplot as plt from scipy.integrate import ode from scipy.interpolate import griddata # 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 = 20000 # total number of nodes nodes, idx, _ = min_energy_nodes(N, vert, smp) # generate nodes # create differentiation matrices for the interior and boundary nodes D = weight_matrix(nodes[idx['interior']], nodes, [(2, 0), (0, 2)], n=30) # create initial and boundary conditions r = np.sqrt((nodes[idx['interior'], 0] - 0.5)**2 + (nodes[idx['interior'], 1] - 0.5)**2) u_init = 1.0 / (1 + (r / 0.05)**4) # initial u in the interior dudt_init = np.zeros(len(idx['interior'])) # initial velocity in the interior u_bnd = np.zeros(len(idx['boundary:all'])) # boundary conditions # Make state vector containing the initial displacements and velocities v = np.hstack([u_init, dudt_init]) def f(t, v): ''' Function used for time integration. This calculates the time
# size of RBF-FD stencils n = 20 # lame parameters lamb = 1.0 mu = 1.0 # z component of body force body_force = 1.0 ## Build and solve for displacements and strain ##################################################################### # generate nodes. The nodes are assigned groups based on which simplex # they lay on boundary_groups = {'fixed':[0], 'free':[1, 2, 3]} nodes, groups, normals = min_energy_nodes( N, vert, smp, boundary_groups=boundary_groups, boundary_groups_with_ghosts=['free']) # `nodes` : (N, 2) float array # `groups` : dictionary containing index sets. It has the keys # "interior", "boundary:free", "boundary:fixed", # "ghosts:free". # `normals : (N, 2) float array # update `N` to include ghost nodes N = nodes.shape[0] ## Create the sparse submatrices for the system matrix G_xx = sp.csr_matrix((N, N)) G_xy = sp.csr_matrix((N, N)) G_yx = sp.csr_matrix((N, N)) G_yy = sp.csr_matrix((N, N))
vert = np.vstack((vert_bot, vert_top)) smp = np.array([np.arange(300), np.roll(np.arange(300), -1)]).T # number of nodes excluding ghosts N = 2000 # size of RBF-FD stencils n = 20 # Lame parameters lamb = 1.0 mu = 1.0 ##################################################################### # generate nodes. Note that this may take a while groups = {'roller': [0, 1, 299], 'free': range(2, 299)} nodes, idx, normals = min_energy_nodes( N, vert, smp, boundary_groups=groups, boundary_groups_with_ghosts=['roller', 'free'], rho=node_density) # update `N` to include ghosts N = nodes.shape[0] # allocate the left-hand-side matrix components G_xx = sp.csc_matrix((N, N)) G_xy = sp.csc_matrix((N, N)) G_yx = sp.csc_matrix((N, N)) G_yy = sp.csc_matrix((N, N)) # build the "left hand side" matrices for body force constraints out = elastic2d_body_force(nodes[idx['interior']], nodes,
N = 500 # size of RBF-FD stencils n = 30 # lame parameters lamb = 1.0 mu = 1.0 # z component of body force body_force = 1.0 ## Build and solve for displacements and strain ##################################################################### # generate nodes. Note that this may take a while boundary_groups = {'fix': [0, 1], 'free': range(2, 12)} nodes, idx, normals = min_energy_nodes(N, vert, smp, boundary_groups=boundary_groups, boundary_groups_with_ghosts=['free'], include_vertices=True) N = nodes.shape[0] # The "left hand side" matrices are built with the convenience # functions from *rbf.fdbuild*. Read the documentation for these # functions to better understand this step. G_xx = sp.csr_matrix((N, N)) G_xy = sp.csr_matrix((N, N)) G_xz = sp.csr_matrix((N, N)) G_yx = sp.csr_matrix((N, N)) G_yy = sp.csr_matrix((N, N)) G_yz = sp.csr_matrix((N, N))
# decomposition. If the value is too large then you may get a "Factor # is exactly singular" error. ilu_drop_tol = 0.005 ##################################################################### ## GENERATE THE DOMAIN AND NODES # generate the domain according to `topo_func` vert, smp = topography(topo_func, [-2.0, 2.0], [-2.0, 2.0], 1.0, n=30) # generate the nodes boundary_groups = {'free': range(10, smp.shape[0]), 'roller': range(0, 10)} nodes, idx, normals = min_energy_nodes( N, vert, smp, boundary_groups=boundary_groups, boundary_groups_with_ghosts=['free', 'roller'], rho=density_func, itr=50) # update `N` to now include the ghost nodes N = nodes.shape[0] ## BUILD THE LEFT-HAND-SIDE MATRIX # allocate the left-hand-side matrix components G_xx = sp.csc_matrix((N, N)) G_xy = sp.csc_matrix((N, N)) G_xz = sp.csc_matrix((N, N)) G_yx = sp.csc_matrix((N, N)) G_yy = sp.csc_matrix((N, N)) G_yz = sp.csc_matrix((N, N)) G_zx = sp.csc_matrix((N, N))
''' compute the density of `nodes` and evaluate the density function at `x`. The output is normalize 1.0 ''' out = np.zeros(x.shape[0]) for n in nodes: out += rbf.basis.se(x, n[None, :], eps=0.01)[:, 0] out /= np.max(out) return out vert = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) smp = np.array([[0, 1], [1, 2], [2, 3], [3, 0]]) nodes = min_energy_nodes(10000, vert, smp, rho=desired_rho)[0] # plot the nodes fig, ax = plt.subplots() for s in smp: ax.plot(vert[s, 0], vert[s, 1], 'k-') ax.plot(nodes[:, 0], nodes[:, 1], 'k.', ms=1) ax.set_aspect('equal') ax.set_title('node positions') fig.tight_layout() plt.savefig('../figures/nodes.b.1.png') fig, axs = plt.subplots(1, 2, figsize=(10, 4)) # evaluate and plot the node density
stencil_size = 50 poly_order = 2 basis = phs3 # create the domain boundary as a simplicial complex vert, smp, boundary_groups = topography( topo_func, xbounds, ybounds, depth, n=30) # create the nodes nodes, groups, normals = min_energy_nodes( nominal_node_count, vert, smp, pinned_nodes=pinned_nodes, boundary_groups=boundary_groups, boundary_groups_with_ghosts=['surface', 'sides', 'bottom']) node_count = nodes.shape[0] # allocate the left-hand-side matrix components G_xx = sp.csc_matrix((node_count, node_count)) G_xy = sp.csc_matrix((node_count, node_count)) G_xz = sp.csc_matrix((node_count, node_count)) G_yx = sp.csc_matrix((node_count, node_count)) G_yy = sp.csc_matrix((node_count, node_count)) G_yz = sp.csc_matrix((node_count, node_count)) G_zx = sp.csc_matrix((node_count, node_count))
pinned_node = [5.0, 0.0] groups = {'bottom': [0], 'top': [2, 3, 4, 5], 'sides': [1, 6]} nominal_node_count = 1000 stencil_size = 30 poly_order = 2 # lame parameters lamb = 1.0 mu = 1.0 # create the nodes nodes, idx, normals = min_energy_nodes( nominal_node_count, vert, smp, itr=1000, pinned_nodes=[pinned_node], include_vertices=False, boundary_groups=groups, boundary_groups_with_ghosts=['top', 'bottom', 'sides']) node_count = nodes.shape[0] for k, v in idx.items(): plt.plot(nodes[v, 0], nodes[v, 1], 'o', label=k, ms=5) plt.quiver(nodes[v, 0], nodes[v, 1], normals[v, 0], normals[v, 1]) plt.legend() plt.show() # allocate the left-hand-side matrix components