def elastic3d_displacement(x, p, n, **kwargs): ''' Returns weight matrices that map displacements at `p` to the displacements at `x`. Parameters ---------- x: (N, 3) array target points. p: (M, 3) array observation points. n : int stencil size **kwargs: additional arguments passed to `weight_matrix` Returns ------- dict keys are the components and the values are the corresponding weight matrices. ''' D_xx = weight_matrix(x, p, n, (0, 0, 0), **kwargs) D_yy = weight_matrix(x, p, n, (0, 0, 0), **kwargs) D_zz = weight_matrix(x, p, n, (0, 0, 0), **kwargs) return {'xx': D_xx, 'yy': D_yy, 'zz': D_zz}
def elastic2d_surface_force(x, nrm, p, n, lamb=1.0, mu=1.0, **kwargs): ''' Returns weight matrices that map displacements at `p` to the surface traction force at `x` with normals `nrm` in a two-dimensional (plane strain) homogeneous elastic medium. Parameters ---------- x: (N, 2) array target points which reside on a surface. nrm: (N, 2) array surface normal vectors at each point in `x`. p: (M, 2) array observation points. n : int stencil size lamb, mu: float Lame parameters **kwargs: additional arguments passed to `weight_matrix` Returns ------- dict keys are the components and the values are the corresponding weight matrices. ''' # x component of traction force resulting from x displacement coeffs_xx = [nrm[:, 0] * (lamb + 2 * mu), nrm[:, 1] * mu] diffs_xx = [(1, 0), (0, 1)] # x component of traction force resulting from y displacement coeffs_xy = [nrm[:, 0] * lamb, nrm[:, 1] * mu] diffs_xy = [(0, 1), (1, 0)] # y component of traction force resulting from x displacement coeffs_yx = [nrm[:, 0] * mu, nrm[:, 1] * lamb] diffs_yx = [(0, 1), (1, 0)] # y component of force resulting from displacement in the y direction coeffs_yy = [nrm[:, 0] * mu, nrm[:, 1] * (lamb + 2 * mu)] diffs_yy = [(1, 0), (0, 1)] # make the differentiation matrices that enforce the free surface boundary # conditions. D_xx = weight_matrix(x, p, n, diffs_xx, coeffs=coeffs_xx, **kwargs) D_xy = weight_matrix(x, p, n, diffs_xy, coeffs=coeffs_xy, **kwargs) D_yx = weight_matrix(x, p, n, diffs_yx, coeffs=coeffs_yx, **kwargs) D_yy = weight_matrix(x, p, n, diffs_yy, coeffs=coeffs_yy, **kwargs) return {'xx': D_xx, 'xy': D_xy, 'yx': D_yx, 'yy': D_yy}
def elastic2d_body_force(x, p, n, lamb=1.0, mu=1.0, **kwargs): ''' Returns weight matrices that map displacements at `p` to the body force at `x` in a two-dimensional (plane strain) homogeneous elastic medium Parameters ---------- x : (N, 2) array Target points. p : (M, 2) array Observation points. n : int stencil size lamb, mu : float, optional Lame parameters **kwargs : additional arguments passed to `weight_matrix` Returns ------- dict keys are the components and the values are the corresponding weight matrices. ''' # x component of force resulting from displacement in the x direction. coeffs_xx = [lamb + 2 * mu, mu] diffs_xx = [(2, 0), (0, 2)] # x component of force resulting from displacement in the y direction. coeffs_xy = [lamb + mu] diffs_xy = [(1, 1)] # y component of force resulting from displacement in the x direction. coeffs_yx = [lamb + mu] diffs_yx = [(1, 1)] # y component of force resulting from displacement in the y direction. coeffs_yy = [lamb + 2 * mu, mu] diffs_yy = [(0, 2), (2, 0)] # make the differentiation matrices that enforce the PDE on the # interior nodes. D_xx = weight_matrix(x, p, n, diffs_xx, coeffs=coeffs_xx, **kwargs) D_xy = weight_matrix(x, p, n, diffs_xy, coeffs=coeffs_xy, **kwargs) D_yx = weight_matrix(x, p, n, diffs_yx, coeffs=coeffs_yx, **kwargs) D_yy = weight_matrix(x, p, n, diffs_yy, coeffs=coeffs_yy, **kwargs) return {'xx': D_xx, 'xy': D_xy, 'yx': D_yx, 'yy': D_yy}
groups['interior+boundary:all'] = np.hstack( (groups['interior'], groups['boundary:all'])) # create the initial displacements at the interior and boundary, the velocities # are zero r = np.sqrt((nodes[groups['interior+boundary:all'], 0] - 0.5)**2 + (nodes[groups['interior+boundary:all'], 1] - 0.5)**2) u_init = np.zeros((n, )) u_init[groups['interior+boundary:all']] = 1.0 / (1 + (r / 0.1)**4) v_init = np.zeros((n, )) z_init = np.hstack((u_init, v_init)) # construct a matrix that maps the displacements at `nodes` to `u` B_disp = weight_matrix(x=nodes[groups['interior+boundary:all']], p=nodes, n=1, diffs=(0, 0)) B_free = weight_matrix(x=nodes[groups['boundary:all']], p=nodes, n=stencil_size, diffs=[(1, 0), (0, 1)], coeffs=[ normals[groups['boundary:all'], 0], normals[groups['boundary:all'], 1] ], phi=phi, order=order) B = expand_rows(B_disp, groups['interior+boundary:all'], n) B += expand_rows(B_free, groups['ghosts:all'], n) B = B.tocsc() Bsolver = splu(B)
## Enforce the PDE on interior nodes AND the free surface nodes # x component of force resulting from displacement in the x direction. coeffs_xx = [lamb+2*mu, mu] diffs_xx = [(2, 0), (0, 2)] # x component of force resulting from displacement in the y direction. coeffs_xy = [lamb, mu] diffs_xy = [(1, 1), (1, 1)] # y component of force resulting from displacement in the x direction. coeffs_yx = [mu, lamb] diffs_yx = [(1, 1), (1, 1)] # y component of force resulting from displacement in the y direction. coeffs_yy = [lamb+2*mu, mu] diffs_yy = [(0, 2), (2, 0)] # make the differentiation matrices that enforce the PDE on the # interior nodes. D_xx = weight_matrix(nodes[groups['interior']], nodes, n, diffs_xx, coeffs=coeffs_xx) D_xy = weight_matrix(nodes[groups['interior']], nodes, n, diffs_xy, coeffs=coeffs_xy) D_yx = weight_matrix(nodes[groups['interior']], nodes, n, diffs_yx, coeffs=coeffs_yx) D_yy = weight_matrix(nodes[groups['interior']], nodes, n, diffs_yy, coeffs=coeffs_yy) G_xx = add_rows(G_xx, D_xx, groups['interior']) G_xy = add_rows(G_xy, D_xy, groups['interior']) G_yx = add_rows(G_yx, D_yx, groups['interior']) G_yy = add_rows(G_yy, D_yy, groups['interior']) # use the ghost nodes to enforce the PDE on the boundary D_xx = weight_matrix(nodes[groups['boundary:free']], nodes, n, diffs_xx, coeffs=coeffs_xx) D_xy = weight_matrix(nodes[groups['boundary:free']], nodes, n, diffs_xy, coeffs=coeffs_xy) D_yx = weight_matrix(nodes[groups['boundary:free']], nodes, n, diffs_yx, coeffs=coeffs_yx) D_yy = weight_matrix(nodes[groups['boundary:free']], nodes, n, diffs_yy, coeffs=coeffs_yy) G_xx = add_rows(G_xx, D_xx, groups['ghosts:free']) G_xy = add_rows(G_xy, D_xy, groups['ghosts:free'])
# 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, _ = poisson_disc_nodes(spacing, (vert, smp)) N = nodes.shape[0] # create the "left hand side" matrix. # create the component which evaluates the PDE A_interior = weight_matrix(nodes[groups['interior']], nodes, n, diffs=[[2, 0], [0, 2]], phi=phi, order=order) # create the component for the fixed boundary conditions A_boundary = weight_matrix(nodes[groups['boundary:all']], nodes, 1, diffs=[0, 0]) # Add the components to the corresponding rows of `A` A = coo_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 d = np.zeros((N, )) d[groups['interior']] = -1.0
def elastic3d_surface_force(x, nrm, p, n, lamb=1.0, mu=1.0, **kwargs): ''' Returns weight matrices that map displacements at `p` to the surface traction force at `x` with normals `nrm` in a three-dimensional homogeneous elastic medium. Parameters ---------- x: (N, 3) array target points which reside on a surface. nrm: (N, 3) array surface normal vectors at each point in `x`. p: (M, 3) array observation points. n : int stencil size lamb, mu: float Lame parameters **kwargs: additional arguments passed to `weight_matrix` Returns ------- dict keys are the components and the values are the corresponding weight matrices. ''' coeffs_xx = [nrm[:, 0] * (lamb + 2 * mu), nrm[:, 1] * mu, nrm[:, 2] * mu] diffs_xx = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] coeffs_xy = [nrm[:, 0] * lamb, nrm[:, 1] * mu] diffs_xy = [(0, 1, 0), (1, 0, 0)] coeffs_xz = [nrm[:, 0] * lamb, nrm[:, 2] * mu] diffs_xz = [(0, 0, 1), (1, 0, 0)] coeffs_yx = [nrm[:, 0] * mu, nrm[:, 1] * lamb] diffs_yx = [(0, 1, 0), (1, 0, 0)] coeffs_yy = [nrm[:, 0] * mu, nrm[:, 1] * (lamb + 2 * mu), nrm[:, 2] * mu] diffs_yy = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] coeffs_yz = [nrm[:, 1] * lamb, nrm[:, 2] * mu] diffs_yz = [(0, 0, 1), (0, 1, 0)] coeffs_zx = [nrm[:, 0] * mu, nrm[:, 2] * lamb] diffs_zx = [(0, 0, 1), (1, 0, 0)] coeffs_zy = [nrm[:, 1] * mu, nrm[:, 2] * lamb] diffs_zy = [(0, 0, 1), (0, 1, 0)] coeffs_zz = [nrm[:, 0] * mu, nrm[:, 1] * mu, nrm[:, 2] * (lamb + 2 * mu)] diffs_zz = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] D_xx = weight_matrix(x, p, n, diffs_xx, coeffs=coeffs_xx, **kwargs) D_xy = weight_matrix(x, p, n, diffs_xy, coeffs=coeffs_xy, **kwargs) D_xz = weight_matrix(x, p, n, diffs_xz, coeffs=coeffs_xz, **kwargs) D_yx = weight_matrix(x, p, n, diffs_yx, coeffs=coeffs_yx, **kwargs) D_yy = weight_matrix(x, p, n, diffs_yy, coeffs=coeffs_yy, **kwargs) D_yz = weight_matrix(x, p, n, diffs_yz, coeffs=coeffs_yz, **kwargs) D_zx = weight_matrix(x, p, n, diffs_zx, coeffs=coeffs_zx, **kwargs) D_zy = weight_matrix(x, p, n, diffs_zy, coeffs=coeffs_zy, **kwargs) D_zz = weight_matrix(x, p, n, diffs_zz, coeffs=coeffs_zz, **kwargs) return { 'xx': D_xx, 'xy': D_xy, 'xz': D_xz, 'yx': D_yx, 'yy': D_yy, 'yz': D_yz, 'zx': D_zx, 'zy': D_zy, 'zz': D_zz }
def elastic3d_body_force(x, p, n, lamb=1.0, mu=1.0, **kwargs): ''' Returns weight matrices that map displacements at `p` to the body force at `x` in a three-dimensional homogeneous elastic medium. Parameters ---------- x: (N, 3) array target points. p: (M, 3) array observation points. n : int stencil size lamb, mu: float first Lame parameter **kwargs: additional arguments passed to `weight_matrix` Returns ------- dict keys are the components and the values are the corresponding weight matrices. ''' coeffs_xx = [lamb + 2 * mu, mu, mu] diffs_xx = [(2, 0, 0), (0, 2, 0), (0, 0, 2)] coeffs_xy = [lamb + mu] diffs_xy = [(1, 1, 0)] coeffs_xz = [lamb + mu] diffs_xz = [(1, 0, 1)] coeffs_yx = [lamb + mu] diffs_yx = [(1, 1, 0)] coeffs_yy = [mu, lamb + 2 * mu, mu] diffs_yy = [(2, 0, 0), (0, 2, 0), (0, 0, 2)] coeffs_yz = [lamb + mu] diffs_yz = [(0, 1, 1)] coeffs_zx = [lamb + mu] diffs_zx = [(1, 0, 1)] coeffs_zy = [lamb + mu] diffs_zy = [(0, 1, 1)] coeffs_zz = [mu, mu, lamb + 2 * mu] diffs_zz = [(2, 0, 0), (0, 2, 0), (0, 0, 2)] D_xx = weight_matrix(x, p, n, diffs_xx, coeffs=coeffs_xx, **kwargs) D_xy = weight_matrix(x, p, n, diffs_xy, coeffs=coeffs_xy, **kwargs) D_xz = weight_matrix(x, p, n, diffs_xz, coeffs=coeffs_xz, **kwargs) D_yx = weight_matrix(x, p, n, diffs_yx, coeffs=coeffs_yx, **kwargs) D_yy = weight_matrix(x, p, n, diffs_yy, coeffs=coeffs_yy, **kwargs) D_yz = weight_matrix(x, p, n, diffs_yz, coeffs=coeffs_yz, **kwargs) D_zx = weight_matrix(x, p, n, diffs_zx, coeffs=coeffs_zx, **kwargs) D_zy = weight_matrix(x, p, n, diffs_zy, coeffs=coeffs_zy, **kwargs) D_zz = weight_matrix(x, p, n, diffs_zz, coeffs=coeffs_zz, **kwargs) return { 'xx': D_xx, 'xy': D_xy, 'xz': D_xz, 'yx': D_yx, 'yy': D_yy, 'yz': D_yz, 'zx': D_zx, 'zy': D_zy, 'zz': D_zz }
# 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, _ = poisson_disc_nodes(spacing, (vert, smp)) N = nodes.shape[0] # create the components for the "left hand side" matrix. A_interior = weight_matrix(x=nodes[groups['interior']], p=nodes, n=n, diffs=[[2, 0], [0, 2]], phi=phi, order=order) A_boundary = weight_matrix(x=nodes[groups['boundary:all']], p=nodes, n=1, diffs=[0, 0]) # Expand and add the components together A = expand_rows(A_interior, groups['interior'], N) A += expand_rows(A_boundary, groups['boundary:all'], N) # create "right hand side" vector d = np.zeros((N, )) d[groups['interior']] = -1.0 d[groups['boundary:all']] = 0.0
mu=mu, n=stencil_size, order=order, basis=basis) D_xx = add_rows(D_xx, components['xx'], idx['boundary:all']) D_xy = add_rows(D_xy, components['xy'], idx['boundary:all']) D_yx = add_rows(D_xy, components['yx'], idx['boundary:all']) D_yy = add_rows(D_yy, components['yy'], idx['boundary:all']) # the ghost node components are left as zero D = sp.vstack((sp.hstack((D_xx, D_xy)), sp.hstack((D_yx, D_yy)))).tocsc() L = weight_matrix(nodes, nodes, diffs=[[6, 0], [0, 6]], n=stencil_size, basis=rbf.basis.phs7, order=6) L = sp.block_diag((L, L)) I = sp.eye(L.shape[0]) R = np.linalg.inv((I + 1e-14 * L.T.dot(L)).A) def f(t, z): ''' Function used for time integration. This calculates the time derivative of the current state vector. ''' u, v = z.reshape((2, -1)) dudt = v h = Binv.dot(u)
} # Generate the nodes. `groups` identifies which group each node belongs to. # `normals` contains the normal vectors corresponding to each node (nan if the # node is not on a boundary). We are giving the circle boundary nodes ghost # nodes to improve the accuracy of the free boundary constraint. nodes, groups, normals = poisson_disc_nodes( node_spacing, (vertices, simplices), boundary_groups=boundary_groups, boundary_groups_with_ghosts=['circle']) # Create the LHS and RHS enforcing that the Lapacian is -5 at the interior # nodes A_interior = weight_matrix(nodes[groups['interior']], nodes, stencil_size, [[2, 0], [0, 2]], phi=radial_basis_function, order=polynomial_order) b_interior = np.full(len(groups['interior']), -5.0) # Enforce that the solution is x at the box boundary nodes. A_boundary_box = weight_matrix(nodes[groups['boundary:box']], nodes, stencil_size, [0, 0], phi=radial_basis_function, order=polynomial_order) b_boundary_box = nodes[groups['boundary:box'], 0] # Enforce a free boundary at the circle boundary nodes.
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, normals = poisson_disc_nodes( spacing, (vert, smp), boundary_groups=boundary_groups, boundary_groups_with_ghosts=['free']) # create the "left hand side" matrix. # create the component which evaluates the PDE A_interior = weight_matrix(nodes[groups['interior']], nodes, n, diffs=[[2, 0], [0, 2]], phi=phi, order=order) # use the ghost nodes to evaluate the PDE at the free boundary nodes A_ghost = weight_matrix(nodes[groups['boundary:free']], nodes, n, diffs=[[2, 0], [0, 2]], phi=phi, order=order) # create the component for the fixed boundary conditions. This is # essentially an identity operation and so we only need a stencil size # of 1 A_fixed = weight_matrix(nodes[groups['boundary:fixed']],
from scipy.interpolate import griddata import matplotlib.pyplot as plt from rbf.pde.fd import weight_matrix from rbf.pde.nodes import min_energy_nodes from rbf.pde.geometry import contains # 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, 50, [(2, 0), (0, 2)]) # 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 derivative of the current state vector. '''
D_xy = expand_rows(components['xy'], groups['interior+boundary:all'], n) D_yx = expand_rows(components['yx'], groups['interior+boundary:all'], n) D_yy = expand_rows(components['yy'], groups['interior+boundary:all'], n) # The boundary conditions are fixed, so there is no acceleration at # `groups['ghosts:all']` D = sp.vstack((sp.hstack((D_xx, D_xy)), sp.hstack((D_yx, D_yy)))).tocsc() # construct a matrix that maps `v` to the acceleration of `u` due to # hyperviscosity. The boundary conditions do not change due to hyperviscosity # nor do they influence the effect of hyperviscosity on other nodes. H = weight_matrix( nodes[groups['interior+boundary:all']], nodes[groups['interior+boundary:all']], stencil_size, diffs=[(4, 0), (0, 4)], coeffs=[-1.0, -1.0], phi='phs5', order=4) H = expand_rows(H, groups['interior+boundary:all'], n) H = expand_cols(H, groups['interior+boundary:all'], n) H = sp.block_diag((H, H)).tocsc() def state_derivative(t, z): u, v = z.reshape((2, -1)) return np.hstack([v, rho*D.dot(Bsolver.solve(u)) + nu*H.dot(v)]) if plot_eigs: L = LinearOperator((4*n, 4*n), matvec=lambda x:state_derivative(0.0, x)) print('computing eigenvectors')