def plot_basis(self, p=2, q=20, plot=True): node = np.array([ (0.0, 0.0), (1.0, 0.0), (0.5, np.sqrt(3)/2)], dtype=np.float) cell = np.array([(0, 1, 2)], dtype=np.int) mesh = TriangleMesh(node, cell) space = LagrangeFiniteElementSpace(mesh, p=p) bc = space.multi_index_matrix[1](q)/q node = mesh.bc_to_point(bc) #( NQ, 1, 2) phi = space.basis(bc) # (NQ, 1, ldof) fig = plt.figure() axes = fig.gca(projection='3d') axes.plot_trisurf( node[..., 0].reshape(-1), node[..., 1].reshape(-1), phi[..., 0].reshape(-1), cmap='rainbow', lw=0.0, antialiased=True) axes.plot_trisurf( node[..., 0].reshape(-1), node[..., 1].reshape(-1), phi[..., 1].reshape(-1), cmap='rainbow', lw=0.0, antialiased=True) plt.show()
class SurfaceTriangleMesh(): def __init__(self, mesh, surface, p=1, scale=None): """ Initial a object of Surface Triangle Mesh. Parameters ---------- self : Surface Triangle Mesh Object mesh : mesh object, represents a triangulation with flat triangle faces. surface : The continuous surface which was represented as a level set function. p : int The degree of the Lagrange space Returns ------- See Also -------- Notes ----- """ self.mesh = mesh self.p = p self.space = LagrangeFiniteElementSpace(mesh, p) self.surface = surface self.ds = mesh.ds self.scale = scale if scale is not None: self.mesh.node *= scale if scale is None: self.node, d = self.surface.project( self.space.interpolation_points()) else: self.node, d = self.surface.project( self.space.interpolation_points() / scale) self.node *= scale self.meshtype = 'stri' self.ftype = mesh.ftype self.itype = mesh.itype self.nodedata = {} self.celldata = {} def vtk_cell_type(self): return 69 def project(self, p): if self.scale is None: return self.surface.project(p) else: p, d = self.surface.project(p / self.scale) return p * self.scale, d * self.scale def integrator(self, k, etype='cell'): return TriangleQuadrature(k) def entity(self, etype=2): if etype in {'cell', 2}: return self.ds.cell elif etype in {'edge', 1}: return self.ds.edge elif etype in {'node', 0}: return self.mesh.node else: raise ValueError("`entitytype` is wrong!") def entity_measure(self, etype=2): p = self.p if etype in {'cell', 2}: return self.area(p + 1) elif etype in {'edge', 'face', 1}: return self.mesh.entity_measure('edge') else: raise ValueError("`entitytype` is wrong!") def entity_barycenter(self, etype=2): p = self.p return self.mesh.entity_barycenter(etype=etype) def number_of_nodes(self): return self.node.shape[0] def number_of_edges(self): return self.mesh.ds.NE def number_of_cells(self): return self.mesh.ds.NC def geo_dimension(self): return self.node.shape[1] def top_dimension(self): return 2 def jacobi_matrix(self, bc, index=np.s_[:]): mesh = self.mesh cell2dof = self.space.dof.cell2dof grad = self.space.grad_basis(bc, index=index) # the tranpose of the jacobi matrix between S_h and K Jh = mesh.jacobi_matrix(index=index) # the tranpose of the jacobi matrix between S_p and S_h Jph = np.einsum('ijm, ...ijk->...imk', self.node[cell2dof[index], :], grad) # the transpose of the jacobi matrix between S_p and K Jp = np.einsum('...ijk, imk->...imj', Jph, Jh) grad = np.einsum('ijk, ...imk->...imj', Jh, grad) return Jp, grad def normal(self, bc, index=None): Js, _, ps = self.surface_jacobi_matrix(bc, index=index) n = np.cross(Js[..., 0, :], Js[..., 1, :], axis=-1) return n, ps def surface_jacobi_matrix(self, bc, index=None): Jp, grad = self.jacobi_matrix(bc, index=index) ps = self.bc_to_point(bc, index=index) Jsp = self.surface.jacobi_matrix(ps) Js = np.einsum('...ijk, ...imk->...imj', Jsp, Jp) return Js, grad, ps def bc_to_point(self, bc, index=None): phi = self.space.basis(bc) cell2dof = self.space.dof.cell2dof if index is None: bcp = np.einsum('...ij, ijk->...ik', phi, self.node[cell2dof, :]) else: bcp = np.einsum('...ij, ijk->...ik', phi, self.node[cell2dof[index], :]) bcp, _ = self.project(bcp) return bcp def area(self, q=3): integrator = self.integrator(q) bcs, ws = integrator.quadpts, integrator.weights Jp, _ = self.jacobi_matrix(bcs) n = np.cross(Jp[..., 0, :], Jp[..., 1, :], axis=-1) l = np.sqrt(np.sum(n**2, axis=-1)) a = np.einsum('i, ij->j', ws, l) / 2.0 return a def add_plot(self, plot, nodecolor='w', edgecolor='k', cellcolor=[0.5, 0.9, 0.45], aspect='equal', linewidths=1, markersize=50, showaxis=False, showcolorbar=False, cmap='rainbow'): if isinstance(plot, ModuleType): fig = plot.figure() fig.set_facecolor('white') axes = fig.gca() else: axes = plot return show_mesh_2d(axes, self.mesh, nodecolor=nodecolor, edgecolor=edgecolor, cellcolor=cellcolor, aspect=aspect, linewidths=linewidths, markersize=markersize, showaxis=showaxis, showcolorbar=showcolorbar, cmap=cmap) def find_node(self, axes, node=None, index=None, showindex=False, color='r', markersize=100, fontsize=24, fontcolor='k'): if node is None: node = self.node if (index is None) and (showindex is True): index = np.array(range(node.shape[0])) find_node(axes, node, index=index, showindex=showindex, color=color, markersize=markersize, fontsize=fontsize, fontcolor=fontcolor) def find_edge(self, axes, index=None, showindex=False, color='g', markersize=150, fontsize=24, fontcolor='k'): find_entity(axes, self.mesh, entity='edge', index=index, showindex=showindex, color=color, markersize=markersize, fontsize=fontsize, fontcolor=fontcolor) def find_cell(self, axes, index=None, showindex=False, color='y', markersize=200, fontsize=24, fontcolor='k'): find_entity(axes, self.mesh, entity='cell', index=index, showindex=showindex, color=color, markersize=markersize, fontsize=fontsize, fontcolor=fontcolor)
import numpy as np import matplotlib.pyplot as plt from fealpy.functionspace import LagrangeFiniteElementSpace from fealpy.pde.poisson_1d import CosData n = 100 p = 4 pde = CosData() mesh = pde.init_mesh(n=0) bc = np.zeros((n, 2), dtype=np.float) bc[:, 1] = np.linspace(0, 1, n) bc[:, 0] = 1 - bc[:, 1] space = LagrangeFiniteElementSpace(mesh, p=p) val = space.basis(bc) print(val.shape) fig = plt.figure() axes = fig.gca() mesh.add_plot(axes) axes.plot(bc[:, 1], val) axes.set_axis_on() plt.show()
class HuZhangFiniteElementSpace(): """ Hu-Zhang Mixed Finite Element Space. """ def __init__(self, mesh, p): self.space = LagrangeFiniteElementSpace(mesh, p) # the scalar space self.mesh = mesh self.p = p self.dof = self.space.dof self.dim = self.space.GD self.init_orth_matrices() self.init_cell_to_dof() def init_orth_matrices(self): """ Initialize the othogonal symetric matrix basis. """ mesh = self.mesh gdim = self.geo_dimension() NE = mesh.number_of_edges() if gdim == 2: idx = np.array([(0, 0), (1, 1), (0, 1)]) self.TE = np.zeros((NE, 3, 3), dtype=np.float) self.T = np.array([[(1, 0), (0, 0)], [(0, 0), (0, 1)], [(0, 1), (1, 0)]]) elif gdim == 3: idx = np.array([(0, 0), (1, 1), (2, 2), (1, 2), (0, 2), (0, 1)]) self.TE = np.zeros((NE, 6, 6), dtype=np.float) self.T = np.array([[(1, 0, 0), (0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 1, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 0), (0, 0, 1)], [(0, 0, 0), (0, 0, 1), (0, 1, 0)], [(0, 0, 1), (0, 0, 0), (1, 0, 0)], [(0, 1, 0), (1, 0, 0), (0, 0, 0)]]) t = mesh.edge_unit_tagent() _, _, frame = np.linalg.svd( t[:, np.newaxis, :]) # get the axis frame on the edge by svd frame[:, 0, :] = t for i, (j, k) in enumerate(idx): self.TE[:, i] = (frame[:, j, idx[:, 0]] * frame[:, k, idx[:, 1]] + frame[:, j, idx[:, 1]] * frame[:, k, idx[:, 0]]) / 2 self.TE[:, gdim:] *= np.sqrt(2) if gdim == 3: NF = mesh.number_of_faces() self.TF = np.zeros((NF, 6, 6), dtype=np.float) n = mesh.face_unit_normal() _, _, frame = np.linalg.svd( n[:, np.newaxis, :]) # get the axis frame on the edge by svd frame[:, 0, :] = n for i, (j, k) in enumerate(idx): self.TF[:, i] = ( frame[:, j, idx[:, 0]] * frame[:, k, idx[:, 1]] + frame[:, j, idx[:, 1]] * frame[:, k, idx[:, 0]]) / 2 self.TF[:, gdim:] *= np.sqrt(2) def __str__(self): return "Hu-Zhang mixed finite element space!" def number_of_global_dofs(self): """ """ p = self.p gdim = self.geo_dimension() tdim = self.tensor_dimension() mesh = self.mesh NC = mesh.number_of_cells() NN = mesh.number_of_nodes() gdof = tdim * NN if p > 1: edof = p - 1 NE = mesh.number_of_edges() gdof += (tdim - 1) * edof * NE # 边内部连续自由度的个数 E = mesh.number_of_edges_of_cells() # 单元边的个数 gdof += NC * E * edof # 边内部不连续自由度的个数 if p > 2: fdof = (p + 1) * (p + 2) // 2 - 3 * p # 面内部自由度的个数 if gdim == 2: gdof += tdim * fdof * NC elif gdim == 3: NF = mesh.number_of_faces() gdof += 3 * fdof * NF # 面内部连续自由度的个数 F = mesh.number_of_faces_of_cells() # 每个单元面的个数 gdof += 3 * F * fdof * NC # 面内部不连续自由度的个数 if (p > 3) and (gdim == 3): ldof = self.dof.number_of_local_dofs() V = mesh.number_of_nodes_of_cells() # 单元顶点的个数 cdof = ldof - E * edof - F * fdof - V gdof += tdim * cdof * NC return gdof def number_of_local_dofs(self): tdim = self.tensor_dimension() ldof = self.dof.number_of_local_dofs() return tdim * ldof def cell_to_dof(self): return self.cell2dof def init_cell_to_dof(self): """ 构建局部自由度到全局自由度的映射矩阵 Returns ------- cell2dof : ndarray with shape (NC, ldof*tdim) NC: 单元个数 ldof: p 次标量空间局部自由度的个数 tdim: 对称张量的维数 """ mesh = self.mesh NN = mesh.number_of_nodes() NE = mesh.number_of_edges() NC = mesh.number_of_cells() gdim = self.geo_dimension() tdim = self.tensor_dimension() # 张量维数 p = self.p dof = self.dof # 标量空间自由度管理对象 c2d = dof.cell2dof[..., np.newaxis] ldof = dof.number_of_local_dofs() # ldof : 标量空间单元上自由度个数 cell2dof = np.zeros((NC, ldof, tdim), dtype=np.int) # 每个标量自由度变成 tdim 个自由度 dofFlags = self.dof_flags_1() # 把不同类型的自由度区分开来 idx, = np.nonzero(dofFlags[0]) # 局部顶点自由度的编号 cell2dof[:, idx, :] = tdim * c2d[:, idx] + np.arange(tdim) base0 = 0 base1 = 0 idx, = np.nonzero(dofFlags[1]) # 边内部自由度的编号 if len(idx) > 0: base0 += NN # 这是标量编号的新起点 base1 += tdim * NN # 这是张量自由度编号的新起点 # 0号局部自由度对应的是切向不连续的自由度, 留到后面重新编号 cell2dof[:, idx, 1:] = base1 + (tdim - 1) * (c2d[:, idx] - base0) + np.arange(tdim - 1) idx, = np.nonzero(dofFlags[2]) if len(idx) > 0: edof = p - 1 base0 += edof * NE base1 += (tdim - 1) * edof * NE if gdim == 2: cell2dof[:, idx, :] = base1 + tdim * (c2d[:, idx] - base0) + np.arange(tdim) elif gdim == 3: # 1, 2, 3 号局部自由度对应切向不连续的张量自由度, 留到后面重新编号 # TODO: check it is right cell2dof[:, idx.reshape(-1, 1), np.array([0, 4, 5])] = base1 + (tdim - 3) * ( c2d[:, idx] - base0) + np.arange(tdim - 3) fdof = (p + 1) * (p + 2) // 2 - 3 * p # 边内部自由度 if gdim == 3: idx, = np.nonzero(dofFlags[3]) if len(idx) > 0: NF = mesh.number_of_faces() base0 += fdof * NF base1 += (tdim - 3) * fdof * NF cell2dof[:, idx, :] = base1 + tdim * (c2d[:, idx] - base0) + np.arange(tdim) cdof = ldof - 4 * fdof - 6 * edof - 4 # 单元内部自由度 else: cdof = fdof idx, = np.nonzero(dofFlags[1]) if len(idx) > 0: base1 += tdim * cdof * NC cell2dof[:, idx, 0] = base1 + np.arange(NC * len(idx)).reshape( NC, len(idx)) if gdim == 3: base1 += NC * len(idx) idx, = np.nonzero(dofFlags[2]) print(idx) if len(idx) > 0: cell2dof[:, idx.reshape(-1, 1), np.array([1, 2, 3])] = base1 + np.arange( NC * len(idx) * 3).reshape(NC, len(idx), 3) self.cell2dof = cell2dof.reshape(NC, -1) def geo_dimension(self): return self.dim def tensor_dimension(self): dim = self.dim return dim * (dim - 1) // 2 + dim def interpolation_points(self): return self.dof.interpolation_points() def dof_flags(self): """ 对标量空间中的自由度进行分类, 分为边内部自由度, 面内部自由度(如果是三维空间的话)及其它自由度 Returns ------- isOtherDof : ndarray, (ldof,) 除了边内部和面内部自由度的其它自由度 isEdgeDof : ndarray, (ldof, 3) or (ldof, 6) 每个边内部的自由度 isFaceDof : ndarray, (ldof, 4) 每个面内部的自由度 ------- """ dim = self.geo_dimension() dof = self.dof isPointDof = dof.is_on_node_local_dof() isEdgeDof = dof.is_on_edge_local_dof() isEdgeDof[isPointDof] = False isEdgeDof0 = np.sum(isEdgeDof, axis=-1) > 0 # isOtherDof = (~isEdgeDof0) # 除了边内部自由度之外的其它自由度 # dim = 2: 包括点和面内部自由度 # dim = 3: 包括点, 面内部和体内部自由度 if dim == 2: return isOtherDof, isEdgeDof elif dim == 3: isFaceDof = dof.is_on_face_local_dof() isFaceDof[isPointDof, :] = False isFaceDof[isEdgeDof0, :] = False isFaceDof0 = np.sum(isFaceDof, axis=-1) > 0 isOtherDof = isOtherDof & (~isFaceDof0) # 三维情形下, 从其它自由度中除去面内部自由度 return isOtherDof, isEdgeDof, isFaceDof else: raise ValueError('`dim` should be 2 or 3!') def dof_flags_1(self): """ 对标量空间中的自由度进行分类, 分为: 点上的自由由度 边内部的自由度 面内部的自由度 体内部的自由度 Returns ------- """ gdim = self.geo_dimension() # the geometry space dimension dof = self.dof isPointDof = dof.is_on_node_local_dof() isEdgeDof = dof.is_on_edge_local_dof() isEdgeDof[isPointDof] = False isEdgeDof0 = np.sum(isEdgeDof, axis=-1) > 0 if gdim == 2: return isPointDof, isEdgeDof0, ~(isPointDof | isEdgeDof0) elif gdim == 3: isFaceDof = dof.is_on_face_local_dof() isFaceDof[isPointDof, :] = False isFaceDof[isEdgeDof0, :] = False isFaceDof0 = np.sum(isFaceDof, axis=-1) > 0 return isPointDof, isEdgeDof0, isFaceDof0, ~( isPointDof | isEdgeDof0 | isFaceDof0) else: raise ValueError('`dim` should be 2 or 3!') def basis(self, bc, cellidx=None): """ Parameters ---------- bc : ndarray with shape (NQ, dim+1) bc[i, :] is i-th quad point cellidx : ndarray 有时我我们只需要计算部分单元上的基函数 Returns ------- phi : ndarray with shape (NQ, NC, ldof*tdim, 3 or 6) NQ: 积分点个数 NC: 单元个数 ldof: 标量空间的单元自由度个数 tdim: 对称张量的维数 """ mesh = self.mesh gdim = self.geo_dimension() tdim = self.tensor_dimension() if cellidx is None: NC = mesh.number_of_cells() cell2edge = mesh.ds.cell_to_edge() else: NC = len(cellidx) cell2edge = mesh.ds.cell_to_edge()[cellidx] phi0 = self.space.basis(bc) # the shape of phi0 is (NQ, ldof) shape = list(phi0.shape) shape.insert(-1, NC) shape += [tdim, tdim] # The shape of `phi` is (NQ, NC, ldof, tdim, tdim), where # NQ : the number of quadrature points # NC : the number of cells # ldof : the number of dofs in each cell # tdim : the dimension of symmetric tensor matrix phi = np.zeros(shape, dtype=np.float) dofFlag = self.dof_flags() # the dof on the vertex and the interior of the cell isOtherDof = dofFlag[0] idx, = np.nonzero(isOtherDof) if len(idx) > 0: phi[..., idx[..., np.newaxis], range(tdim), range(tdim)] = phi0[..., np.newaxis, idx, np.newaxis] isEdgeDof = dofFlag[1] for i, isDof in enumerate(isEdgeDof.T): phi[..., isDof, :, :] = np.einsum('...j, imn->...ijmn', phi0[..., isDof], self.TE[cell2edge[:, i]]) if gdim == 3: if cellidx is None: cell2face = mesh.ds.cell_to_face() else: cell2face = mesh.ds.cell_to_face()[cellidx] isFaceDof = dofFlag[2] for i, isDof in enumerate(isFaceDof.T): phi[..., isDof, :, :] = np.einsum('...j, imn->...ijmn', phi0[..., isDof], self.TF[cell2face[:, i]]) # The shape of `phi` should be (NQ, NC, ldof*tdim, tdim)? shape = phi.shape[:-3] + (-1, tdim) return phi.reshape(shape) def div_basis(self, bc, cellidx=None): mesh = self.mesh gdim = self.geo_dimension() tdim = self.tensor_dimension() # the shape of `gphi` is (NQ, NC, ldof, gdim) gphi = self.space.grad_basis(bc, cellidx=cellidx) shape = list(gphi.shape) shape.insert(-1, tdim) # the shape of `dphi` is (NQ, NC, ldof, tdim, gdim) dphi = np.zeros(shape, dtype=np.float) dofFlag = self.dof_flags() # the dof on the vertex and the interior of the cell isOtherDof = dofFlag[0] dphi[..., isOtherDof, :, :] = np.einsum('...ijm, kmn->...ijkn', gphi[..., isOtherDof, :], self.T) if cellidx is None: cell2edge = mesh.ds.cell_to_edge() else: cell2edge = mesh.ds.cell_to_edge()[cellidx] isEdgeDof = dofFlag[1] for i, isDof in enumerate(isEdgeDof.T): VAL = np.einsum('ijk, kmn->ijmn', self.TE[cell2edge[:, i]], self.T) dphi[..., isDof, :, :] = np.einsum('...ikm, ijmn->...ikjn', gphi[..., isDof, :], VAL) if gdim == 3: if cellidx is None: cell2face = mesh.ds.cell_to_face() else: cell2face = mesh.ds.cell_to_face()[cellidx] isFaceDof = dofFlag[2] for i, isDof in enumerate(isFaceDof.T): VAL = np.einsum('ijk, kmn->ijmn', self.TF[cell2face[:, i]], self.T) dphi[..., isDof, :, :] = np.einsum('...ikm, ijmn->...ikjn', gphi[..., isDof, :], VAL) # The new shape of `dphi` is `(NQ, NC, ldof*tdim, gdim)`, where shape = dphi.shape[:-3] + (-1, gdim) return dphi.reshape(shape) def value(self, uh, bc, cellidx=None): phi = self.basis(bc, cellidx=cellidx) cell2dof = self.cell_to_dof() tdim = self.tensor_dimension() if cellidx is None: uh = uh[cell2dof] else: uh = uh[cell2dof[cellidx]] phi = np.einsum('...jk, kmn->...jmn', phi, self.T) val = np.einsum('...ijmn, ij->...imn', phi, uh) return val def div_value(self, uh, bc, cellidx=None): dphi = self.div_basis(bc, cellidx=cellidx) cell2dof = self.cell_to_dof() tdim = self.tensor_dimension() if cellidx is None: uh = uh[cell2dof] else: uh = uh[cell2dof[cellidx]] val = np.einsum('...ijm, ij->...im', dphi, uh) return val def interpolation(self, u): mesh = self.mesh gdim = self.geo_dimension() tdim = self.tensor_dimension() if gdim == 2: idx = np.array([(0, 0), (1, 1), (0, 1)]) elif gdim == 3: idx = np.array([(0, 0), (1, 1), (2, 2), (1, 2), (0, 2), (0, 1)]) ipoint = self.dof.interpolation_points() c2d = self.dof.cell2dof val = u(ipoint)[c2d] ldof = self.dof.number_of_local_dofs() cell2dof = self.cell2dof.reshape(-1, ldof, tdim) uI = Function(self) dofFlag = self.dof_flags() isOtherDof = dofFlag[0] idx0, = np.nonzero(isOtherDof) uI[cell2dof[:, idx0, :]] = val[:, idx0][..., idx[:, 0], idx[:, 1]] isEdgeDof = dofFlag[1] cell2edge = self.mesh.ds.cell_to_edge() for i, isDof in enumerate(isEdgeDof.T): TE = np.einsum('ijk, kmn->ijmn', self.TE[cell2edge[:, i]], self.T) uI[cell2dof[:, isDof, :]] = np.einsum('ikmn, ijmn->ikj', val[:, isDof, :, :], TE) if gdim == 3: cell2face = mesh.ds.cell_to_face() isFaceDof = dofFlag[2] for i, isDof in enumerate(isFaceDof.T): TF = np.einsum('ijk, kmn->ijmn', self.TF[cell2face[:, i]], self.T) uI[cell2dof[:, isDof, :]] = np.einsum('ikmn, ijmn->ikj', val[..., isDof, :, :], TF) return uI def function(self, dim=None): f = Function(self) return f def array(self, dim=None): gdof = self.number_of_global_dofs() return np.zeros(gdof, dtype=np.float)
from mpl_toolkits.mplot3d import Axes3D from fealpy.mesh import MeshFactory as MF from fealpy.mesh import TriangleMesh from fealpy.mesh.core import multi_index_matrix2d from fealpy.functionspace import LagrangeFiniteElementSpace mesh = MF.one_triangle_mesh(meshtype='equ') space = LagrangeFiniteElementSpace(mesh, p=5) n = 50 bcs = multi_index_matrix2d(n) / n # ldof = (n+1)(n+2)/2 ps = mesh.bc_to_point(bcs).reshape(-1, 2) val = space.basis(bcs) # (NQ, 1, ldof) val = space.grad_basis(bcs) # (NQ, NC, ldof, GD) val = val[:, 0, 0] fig = plt.figure() axes = fig.gca() mesh.add_plot(axes) mesh.find_node(axes, node=ps, markersize=20) fig = plt.figure() axes = fig.add_subplot(projection='3d') axes.plot_trisurf(ps[:, 0], ps[:, 1], val,
NC = mesh.number_of_cells() print("网格中节点、边和单元的个数分别为:", NN, NE, NC) print('创建拉格朗日有限元空间...') space = LagrangeFiniteElementSpace(mesh, p=p) ldof = space.number_of_local_dofs() gdof = space.number_of_global_dofs() print('拉格朗日空间的次数为:', p) print('每个单元上的局部自由度个数:', ldof) print('每个单元上的全局自由度个数', gdof) print('计算空间基函数在每个单元重心坐标点处的值...') bc = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=np.float64) # (3, 3) ps = mesh.bc_to_point(bc) # (NQ, NC, 2) phi = space.basis(bc) gphi = space.grad_basis(bc) print('重心坐标数组:', bc) print('bc.shape:', bc.shape) print('phi.shape:', phi.shape) print('gphi.shape:', gphi.shape) fig = plt.figure() axes = fig.gca() mesh.add_plot(axes) mesh.find_node(axes, showindex=True) mesh.find_cell(axes, showindex=True) plt.show()