zoukankan      html  css  js  c++  java
  • scikit-FEM-mesh

    # -*- coding: utf-8 -*-
    """
    “Mesh”模块包含了有限元网格的不同类型.
    
    See the following implementations:
    
        * MeshTri,三角剖分网格
        * MeshTet, 四面体剖分网格
        * MeshQuad, 矩形剖分网格
        * MeshHex, 六面体剖分网格
    """
    import matplotlib.pyplot as plt
    import matplotlib.tri as mtri
    import numpy as np
    import warnings
    from scipy.sparse import coo_matrix
    
    import skfem.mapping
    import skfem.element

    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    class Mesh():
        """A finite element mesh.
        
        This is an abstract superclass. See the following implementations:
    
            * MeshTri, triangular mesh
            * MeshTet, tetrahedral mesh
            * MeshQuad, quadrilateral mesh
            * MeshHex, hexahedral mesh
        """
    
        refdom = "none"  
        brefdom = "none" 
        meshio_type = "none"
    
        p = np.array([]) 
        t = np.array([]) 

    1.1

        def __init__(self):
            """Check that p and t are C_CONTIGUOUS as this leads
            to better performance."""
            if self.p is not None:
                if self.p.flags['F_CONTIGUOUS']:
                    if self.p.shape[1]>1000:
                        warnings.warn("Mesh.__init__(): Transforming " +
                                "over 100 vertices to C_CONTIGUOUS.")
                    self.p = np.ascontiguousarray(self.p)
            if self.t is not None:
                if self.t.flags['F_CONTIGUOUS']:
                    if self.t.shape[1]>1000:
                        warnings.warn("Mesh.__init__(): Transforming " +
                                "over 100 elements to C_CONTIGUOUS.")
                    self.t = np.ascontiguousarray(self.t)

    1.2

        def __str__(self):
            return self.__repr__()

    1.3

        def __repr__(self):
            return "Mesh of type '" + str(type(self)) + "' "
                   "with " + str(self.p.shape) + " vertices " 
                   "and " + str(self.t.shape) + " elements."

    1.4

        def show(self):
            """A wrapper包装纸 for matplotlib.pyplot.show()."""
            plt.show()

    1.5

        def dim(self):
            """Return the spatial空间的 dimension of the mesh."""
            return int(self.p.shape[0])

    1.6

        def mapping(self):
            """Default不履行 local-to-global mapping for the mesh."""
            raise NotImplementedError("Default mapping not implemented!")

    1.7

        def _uniform_refine(self):
            """Perform执行 a single uniform mesh refinement一致网格加密."""
            raise NotImplementedError("Single refine not implemented " +
                                      "for this mesh type!")

    1.8

        def refine(self, N=None):
            """Refine the mesh.
            
            Parameters
            ----------
            N : int (optional)
                Perform N refinements.
            """
            if N is None:
                return self._uniform_refine()
            else:
                for itr in range(N):
                    self._uniform_refine()

    1.9

        def remove_elements(self, element_indices):
            """Construct new mesh with elements removed
            based on their indices.
    
            Parameters
            ----------
            element_indices : numpy array
                List of element indices to remove.
    
            Returns
            -------
            skfem.Mesh
                A new mesh object with elements removed as per requested.
            """
            keep = np.setdiff1d(np.arange(self.t.shape[1]), element_indices)
            newt = self.t[:, keep]
            ptix = np.unique(newt)
            reverse = np.zeros(self.p.shape[1])
            reverse[ptix] = np.arange(len(ptix))
            newt = reverse[newt]
            newp = self.p[:, ptix]
            meshclass = type(self)
            return meshclass(newp, newt.astype(np.intp))

    1.10

        def scale(self, scale):
            """Scale the mesh.
    
            Parameters
            ----------
            scale : float OR tuple of size dim
                Scale each dimension by a factor. If a floating
                point number is provided, same scale is used
                for each dimension.
            """
            for itr in range(int(self.dim())):
                if isinstance(scale, tuple):
                    self.p[itr, :] *= scale[itr]
                else:
                    self.p[itr, :] *= scale

    1.11

        def translate(self, vec):
            """Translate the mesh.
    
            Parameters
            ----------
            vec : tuple of size dim
                Translate the mesh by a vector.
            """
            for itr in range(int(self.dim())):
                self.p[itr, :] += vec[itr]

    1.12

        def _validate(self):
            """Perform mesh validity checks."""
            # check that element connectivity contains integers
            # NOTE: this is neccessary for some plotting functionality
            if not np.issubdtype(self.t[0, 0], np.signedinteger):
                msg = ("Mesh._validate(): Element connectivity "
                       "must consist of integers.")
                raise Exception(msg)
            # check that vertex matrix has "correct" size
            if self.p.shape[0] > 3:
                msg = ("Mesh._validate(): We do not allow meshes "
                       "embedded into larger than 3-dimensional "
                       "Euclidean space! Please check that "
                       "the given vertex matrix is of size Ndim x Nvertices.")
                raise Exception(msg)
            # check that element connectivity matrix has correct size
            nvertices = {'line': 2, 'tri': 3, 'quad': 4, 'tet': 4, 'hex': 8}
            if self.t.shape[0] != nvertices[self.refdom]:
                msg = ("Mesh._validate(): The given connectivity "
                       "matrix has wrong shape!")
                raise Exception(msg)
            # check that there are no duplicate points
            tmp = np.ascontiguousarray(self.p.T)
            if self.p.shape[1] != np.unique(tmp.view([('', tmp.dtype)]
                                                     * tmp.shape[1])).shape[0]:
                msg = "Mesh._validate(): Mesh contains duplicate vertices."
                warnings.warn(msg)
            # check that all points are at least in some element
            if len(np.setdiff1d(np.arange(self.p.shape[1]), np.unique(self.t))):
                msg = ("Mesh._validate(): Mesh contains a vertex "
                       "not belonging to any element.")
                raise Exception(msg)

    1.13

        def save(self, filename, pointData=None, cellData=None):
            """Export the mesh and fields using meshio.
    
            Parameters
            ----------
            filename : string
                The filename for vtk-file.
            pointData : (optional) numpy array for one output or dict for multiple
            cellData : (optional) numpy array for one output or dict for multiple
            """
            import meshio
    
            if pointData is not None:
                if type(pointData) != dict:
                    pointData = {'0':pointData}
    
            if cellData is not None:
                if type(cellData) != dict:
                    cellData = {'0':cellData}
    
            cells = { self.meshio_type : self.t.T }
            mesh = meshio.Mesh(self.p.T, cells, pointData, cellData)
            meshio.write(filename, mesh)

    1.14

        @classmethod
        def load(cls, filename):
            """Load a mesh from file using meshio.
            
            Parameters
            ----------
            filename : string
                The filename for the mesh.
            """
            import meshio
            mesh = meshio.read(filename)
            if issubclass(cls, Mesh2D):
                return cls(mesh.points[:, :2].T, mesh.cells[cls.meshio_type].T)
            else:
                return cls(mesh.points.T, mesh.cells[cls.meshio_type].T)

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    三维网格

    class Mesh3D(Mesh):
        """Three dimensional meshes, common methods.
    
        See the following implementations:
    
            * MeshTet, tetrahedral mesh
            * MeshHex, hexahedral mesh
        """

    2.1

        def nodes_satisfying(self, test):
            """Return nodes that satisfy some condition.
    
            Parameters
            ----------
            test : lambda function (3 params)
                Evaluates to 1 or True for nodes belonging
                to the output set.
            """
            return np.nonzero(test(self.p[0, :], self.p[1, :], self.p[2, :]))[0]

    2.2

        def facets_satisfying(self, test):
            """Return facets whose midpoints satisfy some condition.
            
            Parameters
            ----------
            test : lambda functions (3 params)
                Evaluates to 1 or True for facet midpoints
                of the facets belonging to the output set.
            """
            mx = np.sum(self.p[0, self.facets], axis=0)/self.facets.shape[0]
            my = np.sum(self.p[1, self.facets], axis=0)/self.facets.shape[0]
            mz = np.sum(self.p[2, self.facets], axis=0)/self.facets.shape[0]
            return np.nonzero(test(mx, my, mz))[0]

    2.3

        def edges_satisfying(self, test):
            """Return edges whose midpoints satisfy some condition.
    
            Parameters
            ----------
            test : lambda functions (3 params)
                Evaluates to 1 or True for edge midpoints
                of the edges belonging to the output set.
            """
            mx = 0.5*(self.p[0, self.edges[0, :]] + self.p[0, self.edges[1, :]])
            my = 0.5*(self.p[1, self.edges[0, :]] + self.p[1, self.edges[1, :]])
            mz = 0.5*(self.p[2, self.edges[0, :]] + self.p[2, self.edges[1, :]])
            return np.nonzero(test(mx, my, mz))[0]

    2.4

        def boundary_nodes(self):
            """Return an array of boundary node indices."""
            return np.unique(self.facets[:, self.boundary_facets()])

    2.5

        def boundary_facets(self):
            """Return an array of boundary facet indices."""
            return np.nonzero(self.f2t[1, :] == -1)[0]

    2.6

        def interior_facets(self):
            """Return an array of interior facet indices."""
            return np.nonzero(self.f2t[1, :] >= 0)[0]

    2.7

        def boundary_edges(self):
            """Return an array of boundary edge indices."""
            bnodes = self.boundary_nodes()[:, None]
            return np.nonzero(np.sum(self.edges[0, :] == bnodes, axis=0) *
                              np.sum(self.edges[1, :] == bnodes, axis=0))[0]

    2.8

        def interior_nodes(self):
            """Return an array of interior node indices."""
            return np.setdiff1d(np.arange(0, self.p.shape[1]), self.boundary_nodes())

    2.9

        def draw_vertices(self):
            """Draw all vertices using mplot3d."""
            fig = plt.figure()
            ax = fig.add_subplot(111, projection='3d')
            ax.scatter(self.p[0, :], self.p[1, :], self.p[2, :])
            return fig

    2.10

        def param(self):
            """Return (maximum) mesh parameter."""
            return np.max(np.sqrt(np.sum((self.p[:, self.edges[0, :]] -
                                          self.p[:, self.edges[1, :]])**2, axis=0)))

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    二维网格

    class Mesh2D(Mesh):
        """Two dimensional meshes, common methods.
        
        See the following implementations:
    
            * MeshTri, triangular mesh
            * MeshQuad, quadrilateral mesh
        """

    3.1

        def jiggle(self, z=0.2):
            """Jiggle the interior nodes of the mesh.
    
            Parameters
            ----------
            z : (optional, default=0.2) float
                Mesh parameter is multiplied by this number. The resulting number
                corresponds to the standard deviation of the jiggle.
            """
            y = z*self.param()
            I = self.interior_nodes()
            self.p[0, I] = self.p[0, I] + y*np.random.rand(len(I))
            self.p[1, I] = self.p[1, I] + y*np.random.rand(len(I))

    3.2

        def boundary_nodes(self):
            """Return an array of boundary node indices."""
            return np.unique(self.facets[:, self.boundary_facets()])

    3.3

        def nodes_satisfying(self, test):
            """Return nodes that satisfy some condition.
    
            Parameters
            ----------
            test : lambda
                An anonymous function with two parameters (x and y) and which returns True for the set of nodes
                that are to be included in the return set.
    
            Returns
            -------
            numpy array
                An array of node indices.
            """
            return np.nonzero(test(self.p[0, :], self.p[1, :]))[0]

    3.4

        def draw_nodes(self, nodes, mark='bo'):
            """Highlight some nodes.
    
            Parameters
            ----------
            nodes : numpy array
                The indices of the nodes to highlight.
            mark : (optional, default='bo') string
                A standard matplotlib string to define the highlight style.
            """
            plt.plot(self.p[0, nodes], self.p[1, nodes], mark)

    3.5

        def param(self):
            """Return mesh parameter."""
            return np.max(np.sqrt(np.sum((self.p[:, self.facets[0, :]] -
                                          self.p[:, self.facets[1, :]])**2, axis=0)))

    3.6

        def interior_nodes(self):
            """Return an array of interior node indices."""
            return np.setdiff1d(np.arange(0, self.p.shape[1]), self.boundary_nodes())

    3.7

        def facets_satisfying(self, test):
            """Return facets whose midpoints satisfy some condition.
    
            Parameters
            ----------
            test : lambda function (2 params)
                An anonymous function with two parameters (x and y) and which
                returns True for the midpoints of the set of facets that are
                to be included in the return set.
            """
            mx = 0.5*(self.p[0, self.facets[0, :]] + self.p[0, self.facets[1, :]])
            my = 0.5*(self.p[1, self.facets[0, :]] + self.p[1, self.facets[1, :]])
            return np.nonzero(test(mx, my))[0]

    3.8

        def elements_satisfying(self, test):
            """Return elements whose midpoints satisfy some condition.
    
            Parameters
            ----------
            test : lambda function (2 params)
                An anonymous function with two parameters (x and y) and which
                returns True for the midpoints of the set of elements that
                are to be included in the return set.
            """
            mx = np.sum(self.p[0, self.t], axis=0)/self.t.shape[0]
            my = np.sum(self.p[1, self.t], axis=0)/self.t.shape[0]
            return np.nonzero(test(mx, my))[0]

    3.9

        def interior_facets(self):
            """Return an array of interior facet indices."""
            return np.nonzero(self.f2t[1, :] >= 0)[0]

    3.10

        def boundary_facets(self):
            """Return an array of boundary facet indices."""
            return np.nonzero(self.f2t[1, :] == -1)[0]

    3.11

        def draw(self, ax=None, node_numbering=False, facet_numbering=False, element_numbering=False):
            """Draw the mesh.
    
            Parameters
            ----------
            ax : (optional, default=None) matplotlib axis
                Use a predefined axis for plotting.
            node_numbering : (optional, default=False)
                Draw node numbering.
            facet_numbering: (optional, default=False)
                Draw facet numbering.
            element_numbering : (optional, default=False)
                Draw element numbering.
            """
            if ax is None:
                # create new figure
                fig = plt.figure()
                ax = fig.add_subplot(111)
            # visualize the mesh faster plotting is achieved through
            # None insertion trick.
            xs = []
            ys = []
            for s, t, u, v in zip(self.p[0, self.facets[0, :]],
                                  self.p[1, self.facets[0, :]],
                                  self.p[0, self.facets[1, :]],
                                  self.p[1, self.facets[1, :]]):
                xs.append(s)
                xs.append(u)
                xs.append(None)
                ys.append(t)
                ys.append(v)
                ys.append(None)
            ax.plot(xs, ys, 'k', linewidth='0.5')
    
            if node_numbering:
                for itr in range(self.p.shape[1]):
                    ax.text(self.p[0, itr], self.p[1, itr], str(itr))
    
            if facet_numbering:
                mx = .5*(self.p[0, self.facets[0, :]] + self.p[0, self.facets[1, :]])
                my = .5*(self.p[1, self.facets[0, :]] + self.p[1, self.facets[1, :]])
                for itr in range(self.facets.shape[1]):
                    ax.text(mx[itr], my[itr], str(itr))
    
            if element_numbering:
                mx = np.sum(self.p[0, self.t], axis=0)/self.t.shape[0]
                my = np.sum(self.p[1, self.t], axis=0)/self.t.shape[0]
                for itr in range(self.t.shape[1]):
                    ax.text(mx[itr], my[itr], str(itr))
    
            return ax

    3.12

        def mirror_mesh(self, a, b, c):
            """Mirror a mesh by the line ax + by + c = 0.
            
            Parameters
            ----------
            a : float
            b : float
            c : float
            """
            tmp = -2.0*(a*self.p[0, :] + b*self.p[1, :] + c)/(a**2 + b**2)
            newx = a*tmp + self.p[0, :]
            newy = b*tmp + self.p[1, :]
            newpoints = np.vstack((newx, newy))
            points = np.hstack((self.p, newpoints))
            tris = np.hstack((self.t, self.t + self.p.shape[1]))
    
            # remove duplicates
            tmp = np.ascontiguousarray(points.T)
            tmp, ixa, ixb = np.unique(tmp.view([('', tmp.dtype)]*tmp.shape[1]), return_index=True, return_inverse=True)
            points = points[:, ixa]
            tris = ixb[tris]
    
            meshclass = type(self)
    
            return meshclass(points, tris)

    3.13

        def save(self, filename, pointData=None, cellData=None):
            """Export the mesh and fields using meshio. (2D version.)
    
            Parameters
            ----------
            filename : string
                The filename for vtk-file.
            pointData : (optional) numpy array for one output or dict for multiple
            cellData : (optional) numpy array for one output or dict for multiple
            """
            import meshio
    
            # a row of zeros required in 2D
            p = np.vstack((np.zeros(self.p.shape[1]), self.p))
    
            if pointData is not None:
                if type(pointData) != dict:
                    pointData = {'0':pointData}
    
            if cellData is not None:
                if type(cellData) != dict:
                    cellData = {'0':cellData}
    
            cells = { self.meshio_type : self.t.T }
            mesh = meshio.Mesh(p.T, cells, pointData, cellData)
            meshio.write(filename, mesh)

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    class InterfaceMesh1D(Mesh):
        """An interface mesh for mortar methods."""

    4.1

        def __init__(self, mesh1, mesh2, rule, param, debug_plot=False):
            self.brefdom = mesh1.brefdom
    
            p1_ix = mesh1.nodes_satisfying(rule)
            p2_ix = mesh2.nodes_satisfying(rule)
    
            p1 = mesh1.p[:, p1_ix]
            p2 = mesh2.p[:, p2_ix]
            _, ix = np.unique(np.concatenate((param(p1[0, :], p1[1, :]), param(p2[0, :], p2[1, :]))), return_index=True)
    
            np1 = mesh1.p.shape[1]
            nt1 = mesh1.t.shape[1]
            ixorig = np.concatenate((p1_ix, p2_ix + np1))[ix]
    
            self.p = np.hstack((mesh1.p, mesh2.p))
            self.t = np.hstack((mesh1.t, mesh2.t + np1))
            self.facets = np.array([ixorig[:-1], ixorig[1:]])
            self.t2f = -1 + 0*np.hstack((mesh1.t2f, mesh2.t2f))
    
            # construct normals
            tangent_x = self.p[0, self.facets[0, :]] - self.p[0, self.facets[1, :]]
            tangent_y = self.p[1, self.facets[0, :]] - self.p[1, self.facets[1, :]]
            tangent_lengths = np.sqrt(tangent_x**2 + tangent_y**2)
    
            self.normals = np.array([-tangent_y/tangent_lengths, tangent_x/tangent_lengths])
    
            if debug_plot:
                ax = mesh1.draw()
                mesh2.draw(ax=ax)
                xs = np.array([self.p[0, self.facets[0, :]], self.p[0, self.facets[1, :]]])
                midx = np.sum(xs, axis=0)/2.0
                ys = np.array([self.p[1, self.facets[0, :]], self.p[1, self.facets[1, :]]])
                midy = np.sum(ys, axis=0)/2.0
                xs = 0.9*(xs - midx) + midx
                ys = 0.9*(ys - midy) + midy
                ax.plot(xs, ys, 'x-')
    
            # mappings from facets to the original triangles
            # TODO vectorize
            self.f2t = self.facets*0-1
            for itr in range(self.facets.shape[1]):
                mx = .5*(self.p[0, self.facets[0, itr]] + self.p[0, self.facets[1, itr]])
                my = .5*(self.p[1, self.facets[0, itr]] + self.p[1, self.facets[1, itr]])
                val = param(mx, my)
                for jtr in mesh1.boundary_facets():
                    fix1 = mesh1.facets[0, jtr]
                    x1 = mesh1.p[0, fix1]
                    y1 = mesh1.p[1, fix1]
                    fix2 = mesh1.facets[1, jtr]
                    x2 = mesh1.p[0, fix2]
                    y2 = mesh1.p[1, fix2]
                    if rule(x1, y1) > 0 or rule(x2, y2) > 0:
                        if val > param(x1, y1) and val < param(x2, y2):
                            # OK
                            self.f2t[0, itr] = mesh1.f2t[0, jtr]
                            break
                        elif val < param(x1, y1) and val > param(x2, y2): # ye olde
                            # OK
                            self.f2t[0, itr] = mesh1.f2t[0, jtr]
                            break
                        elif val >= param(x1, y1) and val < param(x2, y2):
                            # OK
                            self.f2t[0, itr] = mesh1.f2t[0, jtr]
                            break
                        elif val > param(x1, y1) and val <= param(x2, y2):
                            # OK
                            self.f2t[0, itr] = mesh1.f2t[0, jtr]
                            break
                        elif val <= param(x1, y1) and val > param(x2, y2):
                            # OK
                            self.f2t[0, itr] = mesh1.f2t[0, jtr]
                            break
                        elif val < param(x1, y1) and val >= param(x2, y2):
                            # OK
                            self.f2t[0, itr] = mesh1.f2t[0, jtr]
                            break
                for jtr in mesh2.boundary_facets():
                    fix1 = mesh2.facets[0, jtr]
                    x1 = mesh2.p[0, fix1]
                    y1 = mesh2.p[1, fix1]
                    fix2 = mesh2.facets[1, jtr]
                    x2 = mesh2.p[0, fix2]
                    y2 = mesh2.p[1, fix2]
                    if rule(x1, y1) > 0 or rule(x2, y2) > 0:
                        if val > param(x1, y1) and val < param(x2, y2):
                            # OK
                            self.f2t[1, itr] = mesh2.f2t[0, jtr] + nt1
                            break
                        elif val < param(x1, y1) and val > param(x2, y2):
                            # OK
                            self.f2t[1, itr] = mesh2.f2t[0, jtr] + nt1
                            break
                        elif val >= param(x1, y1) and val < param(x2, y2):
                            # OK
                            self.f2t[1, itr] = mesh2.f2t[0, jtr] + nt1
                            break
                        elif val > param(x1, y1) and val <= param(x2, y2):
                            # OK
                            self.f2t[1, itr] = mesh2.f2t[0, jtr] + nt1
                            break
                        elif val <= param(x1, y1) and val > param(x2, y2):
                            # OK
                            self.f2t[1, itr] = mesh2.f2t[0, jtr] + nt1
                            break
                        elif val < param(x1, y1) and val >= param(x2, y2):
                            # OK
                            self.f2t[1, itr] = mesh2.f2t[0, jtr] + nt1
                            break
            if (self.f2t>-1).all():
                self.f2t[0, :]
                return
            else:
                print(self.f2t)
                raise Exception("All mesh facets corresponding to mortar facets not found!")

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    一维网格

    class MeshLine(Mesh):
        """One-dimensional mesh."""
    
        refdom = "line"
        brefdom = "point"
    
        p = np.array([])
        t = np.array([])

    5.1

        def __init__(self, p=None, t=None, validate=True):
            if p is None and t is None:
                p = np.array([[0, 1]])
                t = np.array([[0], [1]])
            elif p is not None and t is None:
                t = np.array([np.arange(np.max(p.shape)-1), np.arange(np.max(p.shape)-1)+1])
            if len(p.shape)==1:
                p = np.array([p]) 
            self.p = p
            self.t = t
            if validate:
                self._validate()
            super(MeshLine, self).__init__()

    5.2

        @classmethod
        def init_refdom(cls):
            """Initialize a mesh constisting of the reference interval [0,1]."""
            return cls()

    5.3

        def adaptive_refine(self, marked):
            """Perform an adaptive refine which splits each marked element into two."""
            t = self.t
            p = self.p
    
            mid = range(len(marked)) + np.max(t) + 1
    
            nonmarked = np.setdiff1d(np.arange(t.shape[1]), marked)
    
            newp = np.hstack((p, 0.5*(p[:, self.t[0, marked]] + p[:, self.t[1, marked]])))
            newt = np.vstack((t[0, marked], mid))
            newt = np.hstack((t[:, nonmarked], newt, np.vstack((mid, t[1, marked]))))
            # update fields
            self.p = newp
            self.t = newt

    5.4

        def _uniform_refine(self):
            """Perform a single mesh refine that halves 'h'."""
            # rename variables
            t = self.t
            p = self.p
    
            mid = range(self.t.shape[1]) + np.max(t) + 1
            # new vertices and elements
            newp = np.hstack((p, 0.5*(p[:, self.t[0, :]] + p[:, self.t[1, :]])))
            newt = np.vstack((t[0, :], mid))
            newt = np.hstack((newt, np.vstack((mid, t[1, :]))))
            # update fields
            self.p = newp
            self.t = newt

    5.5

            # TODO implement prolongation
    
        def boundary_nodes(self):
            """Find the boundary nodes of the mesh."""
            _, counts = np.unique(self.t.flatten(), return_counts=True)
            return np.nonzero(counts == 1)[0]

    5.6

        def interior_nodes(self):
            """Find the interior nodes of the mesh."""
            _, counts = np.unique(self.t.flatten(), return_counts=True)
            return np.nonzero(counts == 2)[0]

    5.7

        def plot(self, u, ax=None, color='ko-'):
            """Plot a function defined at the nodes of the mesh."""
            if ax is None:
                # create new figure
                fig = plt.figure()
                ax = fig.add_subplot(111)
            xs = []
            ys = []
            for y1, y2, s, t in zip(u[self.t[0, :]],
                                    u[self.t[1, :]],
                                    self.p[0, self.t[0, :]],
                                    self.p[0, self.t[1, :]]):
                xs.append(s)
                xs.append(t)
                xs.append(None)
                ys.append(y1)
                ys.append(y2)
                ys.append(None)
            ax.plot(xs, ys, color)
    
            return ax

    5.8

        def __mul__(self, other):
            """Tensor product mesh."""
            return MeshQuad.init_tensor(self.p[0, :], other.p[0, :])

    5.9

        def param(self):
            return np.max(np.abs(self.p[0, self.t[1, :]] - self.p[0, self.t[0, :]]))

    5.10

        def mapping(self):
            return skfem.mapping.MappingAffine(self)

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    矩形剖分

    class MeshQuad(Mesh2D):
        """A mesh consisting of quadrilateral elements.
        
        Attributes
        ----------
        p : numpy array of size 2 x Nvertices
            The vertices of the mesh
        t : numpy array of size 4 x Nelements
            The element connectivity
        facets : numpy array of size 2 x Nfacets
            Each column contains a pair of indices to p.
        f2t : numpy array of size 2 x Nfacets
            Each column contains a pair of indices to t
            or -1 on the second row if the facet is on
            the boundary.
        t2f : numpy array of size 4 x Nelements
            Each column contains four indices to facets.
        """
    
        refdom = "quad"
        brefdom = "line"
        meshio_type = "quad"
    
        p = np.array([])
        t = np.array([])
        facets = np.array([])
        f2t = np.array([])
        t2f = np.array([])

    6.1

        def __init__(self, p=None, t=None, validate=True):
            """Initialize a quadrilateral mesh.
    
            Parameters
            ----------
            p : (optional) numpy array of size 2 x Nvertices
                The points of the mesh.
            t : (optional) numpy array of size 4 x Nelements
                The element connectivity, i.e. indices to p.
                These should be in counter-clockwise order.
            """
            if p is None and t is None:
                p = np.array([[0., 0.], [1., 0.], [1., 1.], [0., 1.]]).T
                t = np.array([[0, 1, 2, 3]]).T
            elif p is None or t is None:
                raise Exception("Must provide p AND t or neither")
            self.p = p
            self.t = t
            if validate:
                self._validate()
            self._build_mappings()
            super(MeshQuad, self).__init__()

    6.2

        @classmethod
        def init_tensor(cls, x, y):
            """Initialize a tensor product mesh.
    
            Parameters
            ----------
            x : numpy array (1d)
                The nodal coordinates in dimension x
            y : numpy array (1d)
                The nodal coordinates in dimension y
            """
            npx = len(x)
            npy = len(y)
            X, Y = np.meshgrid(np.sort(x), np.sort(y))   
            p = np.vstack((X.flatten('F'), Y.flatten('F')))
            ix = np.arange(npx*npy)
            ne = (npx-1)*(npy-1)
            t = np.zeros((4, ne))
            ix = ix.reshape(npy, npx, order='F').copy()
            t[0, :] = ix[0:(npy-1), 0:(npx-1)].reshape(ne, 1, order='F').copy().flatten()
            t[1, :] = ix[1:npy, 0:(npx-1)].reshape(ne, 1, order='F').copy().flatten()
            t[2, :] = ix[1:npy, 1:npx].reshape(ne, 1, order='F').copy().flatten()
            t[3, :] = ix[0:(npy-1), 1:npx].reshape(ne, 1, order='F').copy().flatten()
            return cls(p, t.astype(np.int64))

    6.3

        @classmethod
        def init_refdom(cls):
            """Initialize a mesh that includes only the reference quad.
            
            The mesh topology is as follows:
             (-1,1) *-------------* (1,1)
                    |             |
                    |             |
                    |             |
                    |             | 
                    |             | 
                    |             |
                    |             |  
            (-1,-1) *-------------* (1,-1)
            """
            p = np.array([[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]]).T
            t = np.array([[0, 1, 2, 3]]).T
            return cls(p, t)

    6.4

        def _build_mappings(self):
            # do not sort since order defines counterclockwise order
            # self.t=np.sort(self.t,axis=0)
    
            # define facets: in the order (0,1) (1,2) (2,3) (0,3)
            self.facets = np.sort(np.vstack((self.t[0, :], self.t[1, :])), axis=0)
            self.facets = np.hstack((self.facets,
                                     np.sort(np.vstack((self.t[1, :],
                                                        self.t[2, :])), axis=0)))
            self.facets = np.hstack((self.facets,
                                     np.sort(np.vstack((self.t[2, :],
                                                        self.t[3, :])), axis=0)))
            self.facets = np.hstack((self.facets,
                                     np.sort(np.vstack((self.t[0, :],
                                                        self.t[3, :])), axis=0)))
    
            # get unique facets and build quad-to-facet mapping: 4 (edges) x Nquads
            tmp = np.ascontiguousarray(self.facets.T)
            tmp, ixa, ixb = np.unique(tmp.view([('', tmp.dtype)]*tmp.shape[1]),
                                      return_index=True, return_inverse=True)
            self.facets = self.facets[:, ixa]
            self.t2f = ixb.reshape((4, self.t.shape[1]))
    
            # build facet-to-quadrilateral mapping: 2 (quads) x Nedges
            e_tmp = np.hstack((self.t2f[0, :],
                               self.t2f[1, :],
                               self.t2f[2, :],
                               self.t2f[3, :]))
            t_tmp = np.tile(np.arange(self.t.shape[1]), (1, 4))[0]
    
            e_first, ix_first = np.unique(e_tmp, return_index=True)
            # this emulates matlab unique(e_tmp,'last')
            e_last, ix_last = np.unique(e_tmp[::-1], return_index=True)
            ix_last = e_tmp.shape[0] - ix_last - 1
    
            self.f2t = np.zeros((2, self.facets.shape[1]), dtype=np.int64)
            self.f2t[0, e_first] = t_tmp[ix_first]
            self.f2t[1, e_last] = t_tmp[ix_last]
    
            # second row to -1 if repeated (i.e., on boundary)
            self.f2t[1, np.nonzero(self.f2t[0, :] == self.f2t[1, :])[0]] = -1

    6.5

        def _uniform_refine(self):
            """Perform a single mesh refine that halves 'h'.
    
            Each quadrilateral is split into four.
            """
            # rename variables
            t = self.t
            p = self.p
            e = self.facets
            sz = p.shape[1]
            t2f = self.t2f + sz
            # quadrilateral middle point
            mid = range(self.t.shape[1]) + np.max(t2f) + 1
            # new vertices are the midpoints of edges ...
            newp1 = 0.5*np.vstack((p[0, e[0, :]] + p[0, e[1, :]],
                                   p[1, e[0, :]] + p[1, e[1, :]]))
            # ... and element middle points
            newp2 = 0.25*np.vstack((p[0, t[0, :]] + p[0, t[1, :]] +
                                    p[0, t[2, :]] + p[0, t[3, :]],
                                    p[1, t[0, :]] + p[1, t[1, :]] +
                                    p[1, t[2, :]] + p[1, t[3, :]]))
            newp = np.hstack((p, newp1, newp2))
            # build new quadrilateral definitions
            newt = np.vstack((t[0, :],
                              t2f[0, :],
                              mid,
                              t2f[3, :]))
            newt = np.hstack((newt, np.vstack((t2f[0, :],
                                               t[1, :],
                                               t2f[1, :],
                                               mid))))
            newt = np.hstack((newt, np.vstack((mid,
                                               t2f[1, :],
                                               t[2, :],
                                               t2f[2, :]))))
            newt = np.hstack((newt, np.vstack((t2f[3, :],
                                               mid,
                                               t2f[2, :],
                                               t[3, :]))))
            # update fields
            self.p = newp
            self.t = newt
    
            self._build_mappings()

    6.6

            # TODO implement prolongation
    
        def _splitquads(self, x=None):
            """Split each quad into two triangles and return MeshTri."""
            t = self.t[[0, 1, 3], :]
            t = np.hstack((t, self.t[[1, 2, 3]]))
    
            if x is not None:
                if len(x) == self.t.shape[1]:
                    # preserve elemental constant functions
                    X = np.concatenate((x, x))
                else:
                    raise Exception("The parameter x must have one value per element.")
                return MeshTri(self.p, t, validate=False), X
            else:
                return MeshTri(self.p, t, validate=False)

    6.7

        def _splitquads_symmetric(self):
            """Split quads into four triangles."""
            t = np.vstack((self.t, np.arange(self.t.shape[1]) + self.p.shape[1]))
            newt = t[[0, 1, 4], :]
            newt = np.hstack((newt, t[[1, 2, 4], :]))
            newt = np.hstack((newt, t[[2, 3, 4], :]))
            newt = np.hstack((newt, t[[3, 0, 4], :]))
            mx = np.sum(self.p[0, self.t], axis=0)/self.t.shape[0]
            my = np.sum(self.p[1, self.t], axis=0)/self.t.shape[0]
            return MeshTri(np.hstack((self.p, np.vstack((mx, my)))), newt, validate=False)

    6.8

        def plot(self, z, smooth=False, edgecolors=None, ax=None, zlim=None):
            """Visualize nodal or elemental function (2d).
    
            The quadrilateral mesh is split into triangular mesh (MeshTri) and
            the respective plotting function for the triangular mesh is used.
            """
            m, z = self._splitquads(z)
            return m.plot(z, smooth, ax=ax, zlim=zlim, edgecolors=edgecolors)

    6.9

        def plot3(self, z, smooth=False, ax=None):
            """Visualize nodal function (3d i.e. three axes).
    
            The quadrilateral mesh is split into triangular mesh (MeshTri) and
            the respective plotting function for the triangular mesh is used.
            """
            m, z = self._splitquads(z)
            return m.plot3(z, smooth, ax=ax)

    6.10

        def mapping(self):
            return skfem.mapping.MappingIsoparametric(self, skfem.element.ElementQ1())

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    六面体剖分

    class MeshHex(Mesh3D):
        """A mesh consisting of hexahedral elements.
    
        Attributes
        ----------
        p : numpy array of size 3 x Nvertices
            The vertices of the mesh. Each column corresponds to a point.
        t : numpy array of size 8 x Nelements
            The element connectivity. Each column corresponds to a element
            and contains eight column indices to MeshHex.p.
        facets : numpy array of size 4 x Nfacets
            Each column contains four column indices to MeshHex.p.
        f2t : numpy array of size 2 x Nfacets
            Each column contains a pair of column indices to MeshHex.t
            or -1 on the second row if the corresponding
            facet is located on the boundary.
        t2f : numpy array of size 6 x Nelements
            Each column contains four indices to MeshHex.facets.
        edges : numpy array of size 2 x Nedges
            Each column corresponds to an edge and contains two indices to
            MeshHex.p.
        t2e : numpy array of size 12 x Nelements
            Each column contains twelve column indices of MeshHex.edges.
        """
    
        refdom = "hex"
        brefdom = "quad"
        meshio_type = "hexahedron"

    7.1

        def __init__(self, p=None, t=None, validate=True):
            if p is None and t is None:
                p = np.array([[0., 0., 0.], [0., 0., 1.], [0., 1., 0.], [1., 0., 0.],
                              [0., 1., 1.], [1., 0., 1.], [1., 1., 0.], [1., 1., 1.]]).T
                t = np.array([[0, 1, 2, 3, 4, 5, 6, 7]]).T
            elif p is None or t is None:
                raise Exception("Must provide p AND t or neither")
            #
            # TODO fix orientation if p and t is provided. refer to
            # the default mesh for correct orientation
            #
            #   2---6
            #  /   /|
            # 4---7 3
            # |   |/
            # 1---5
            #
            # The hidden node is 0.
            #
            self.p = p
            self.t = t
            if validate:
                self._validate()
            self._build_mappings()
            super(MeshHex, self).__init__()

    7.2

        @classmethod
        def init_tensor(cls, x, y, z):
            """Initialize a tensor product mesh.
    
            Parameters
            ----------
            x : numpy array (1d)
                The nodal coordinates in dimension x
            y : numpy array (1d)
                The nodal coordinates in dimension y
            z : numpy array (1d)
                The nodal coordinates in dimension z
            """
            npx = len(x)
            npy = len(y)
            npz = len(z)
            X, Y, Z = np.meshgrid(np.sort(x), np.sort(y), np.sort(z))   
            p = np.vstack((X.flatten('F'), Y.flatten('F'), Z.flatten('F')))
            ix = np.arange(npx*npy*npz)
            ne = (npx-1)*(npy-1)*(npz-1)
            t = np.zeros((8, ne))
            ix = ix.reshape(npy, npx, npz, order='F').copy()
            t[0, :] = ix[0:(npy-1), 0:(npx-1), 0:(npz-1)].reshape(ne, 1, order='F').copy().flatten()
            t[1, :] = ix[1:npy, 0:(npx-1), 0:(npz-1)].reshape(ne, 1, order='F').copy().flatten()
            t[2, :] = ix[0:(npy-1), 1:npx, 0:(npz-1)].reshape(ne, 1, order='F').copy().flatten()
            t[3, :] = ix[0:(npy-1), 0:(npx-1), 1:npz].reshape(ne, 1, order='F').copy().flatten()
            t[4, :] = ix[1:npy, 1:npx, 0:(npz-1)].reshape(ne, 1, order='F').copy().flatten()
            t[5, :] = ix[1:npy, 0:(npx-1), 1:npz].reshape(ne, 1, order='F').copy().flatten()
            t[6, :] = ix[0:(npy-1), 1:npx, 1:npz].reshape(ne, 1, order='F').copy().flatten()
            t[7, :] = ix[1:npy, 1:npx, 1:npz].reshape(ne, 1, order='F').copy().flatten()
            return cls(p, t.astype(np.int64))

    7.3

        def _build_mappings(self):
            """Build element-to-facet, element-to-edges, etc. mappings."""
            self.edges = np.sort(np.vstack((self.t[0, :], self.t[1, :])), axis=0)
            e = np.array([0, 2,
                          0, 3,
                          1, 4,
                          1, 5,
                          2, 4,
                          2, 6,
                          3, 5,
                          3, 6,
                          4, 7,
                          5, 7,
                          6, 7]) # see the picture in init
            for i in range(11):
                self.edges = np.hstack((self.edges, np.sort(np.vstack((self.t[e[2*i], :],
                                                                       self.t[e[2*i+1], :])), axis=0)))
    
            # unique edges
            self.edges, ixa, ixb = np.unique(self.edges, axis=1, return_index=True, return_inverse=True)
            self.edges = np.ascontiguousarray(self.edges)
    
            self.t2e = ixb.reshape((12, self.t.shape[1]))
    
            # define facets
            self.facets = np.sort(np.vstack((self.t[0, :],
                                             self.t[1, :],
                                             self.t[4, :],
                                             self.t[2, :])), axis=0)
            f = np.array([0, 3, 6, 2,
                          0, 1, 5, 3,
                          2, 6, 7, 4,
                          1, 5, 7, 4,
                          3, 5, 7, 6])
            for i in range(5):
                self.facets = np.hstack((self.facets, np.sort(np.vstack((self.t[f[4*i], :],
                                                                         self.t[f[4*i+1], :],
                                                                         self.t[f[4*i+2], :],
                                                                         self.t[f[4*i+3], :])), axis=0)))
    
    
            # unique facets
            self.facets, ixa, ixb = np.unique(self.facets, axis=1, return_index=True, return_inverse=True)
            self.facets = np.ascontiguousarray(self.facets)
    
            self.t2f = ixb.reshape((6, self.t.shape[1]))
    
            # build facet-to-hexa mapping: 2 (hexes) x Nfacets
            e_tmp = np.hstack((self.t2f[0, :], self.t2f[1, :],
                               self.t2f[2, :], self.t2f[3, :],
                               self.t2f[4, :], self.t2f[5, :]))
            t_tmp = np.tile(np.arange(self.t.shape[1]), (1, 6))[0]
    
            e_first, ix_first = np.unique(e_tmp, return_index=True)
            e_last, ix_last = np.unique(e_tmp[::-1], return_index=True)
            ix_last = e_tmp.shape[0] - ix_last - 1
    
            self.f2t = np.zeros((2, self.facets.shape[1]), dtype=np.int64)
            self.f2t[0, e_first] = t_tmp[ix_first]
            self.f2t[1, e_last] = t_tmp[ix_last]
    
            ## second row to zero if repeated (i.e., on boundary)
            self.f2t[1, np.nonzero(self.f2t[0, :] == self.f2t[1, :])[0]] = -1

    7.4

        def _uniform_refine(self):
            """Perform a single mesh refine that halves 'h'.
    
            Each hex is split into 8."""
            # rename variables
            t = self.t
            p = self.p
            e = self.edges
            f = self.facets
            sz = p.shape[1]
            t2e = self.t2e + sz
            t2f = self.t2f + np.max(t2e) + 1
            # hex middle point
            mid = range(self.t.shape[1]) + np.max(t2f) + 1
            # new vertices are the midpoints of edges ...
            newp1 = 0.5*np.vstack((p[0, e[0, :]] + p[0, e[1, :]],
                                   p[1, e[0, :]] + p[1, e[1, :]],
                                   p[2, e[0, :]] + p[2, e[1, :]]))
            # ... midpoints of facets ...
            newp2 = 0.25*np.vstack((p[0, f[0, :]] + p[0, f[1, :]] +
                                    p[0, f[2, :]] + p[0, f[3, :]],
                                    p[1, f[0, :]] + p[1, f[1, :]] +
                                    p[1, f[2, :]] + p[1, f[3, :]],
                                    p[2, f[0, :]] + p[2, f[1, :]] +
                                    p[2, f[2, :]] + p[2, f[3, :]]))
            # ... and element middle points
            newp3 = 0.125*np.vstack((p[0, t[0, :]] + p[0, t[1, :]] +
                                     p[0, t[2, :]] + p[0, t[3, :]] +
                                     p[0, t[4, :]] + p[0, t[5, :]] +
                                     p[0, t[6, :]] + p[0, t[7, :]],
                                     p[1, t[0, :]] + p[1, t[1, :]] +
                                     p[1, t[2, :]] + p[1, t[3, :]] +
                                     p[1, t[4, :]] + p[1, t[5, :]] +
                                     p[1, t[6, :]] + p[1, t[7, :]],
                                     p[2, t[0, :]] + p[2, t[1, :]] +
                                     p[2, t[2, :]] + p[2, t[3, :]] +
                                     p[2, t[4, :]] + p[2, t[5, :]] +
                                     p[2, t[6, :]] + p[2, t[7, :]]))
            newp = np.hstack((p, newp1, newp2, newp3))
            # build new hex indexing (this requires some serious meditation)
            newt = np.vstack((t[0, :],
                              t2e[0, :],
                              t2e[1, :],
                              t2e[2, :],
                              t2f[0, :],
                              t2f[2, :],
                              t2f[1, :],
                              mid))
            newt = np.hstack((newt, np.vstack((t2e[0, :],
                                               t[1, :],
                                               t2f[0, :],
                                               t2f[2, :],
                                               t2e[3, :],
                                               t2e[4, :],
                                               mid,
                                               t2f[4, :]))))
            newt = np.hstack((newt, np.vstack((t2e[1, :],
                                               t2f[0, :],
                                               t[2, :],
                                               t2f[1, :],
                                               t2e[5, :],
                                               mid,
                                               t2e[6, :],
                                               t2f[3, :]
                                               ))))
            newt = np.hstack((newt, np.vstack((t2e[2, :],
                                               t2f[2, :],
                                               t2f[1, :],
                                               t[3, :],
                                               mid,
                                               t2e[7, :],
                                               t2e[8, :],
                                               t2f[5, :]
                                               ))))
            newt = np.hstack((newt, np.vstack((t2f[0, :],
                                               t2e[3, :],
                                               t2e[5, :],
                                               mid,
                                               t[4, :],
                                               t2f[4, :],
                                               t2f[3, :],
                                               t2e[9, :]
                                               ))))
            newt = np.hstack((newt, np.vstack((t2f[2, :],
                                               t2e[4, :],
                                               mid,
                                               t2e[7, :],
                                               t2f[4, :],
                                               t[5, :],
                                               t2f[5, :],
                                               t2e[10, :],
                                               ))))
            newt = np.hstack((newt, np.vstack((t2f[1, :],
                                               mid,
                                               t2e[6, :],
                                               t2e[8, :],
                                               t2f[3, :],
                                               t2f[5, :],
                                               t[6, :],
                                               t2e[11, :]
                                               ))))
            newt = np.hstack((newt, np.vstack((mid,
                                               t2f[4, :],
                                               t2f[3, :],
                                               t2f[5, :],
                                               t2e[9, :],
                                               t2e[10, :],
                                               t2e[11, :],
                                               t[7, :]
                                               ))))
            # update fields
            self.p = newp
            self.t = newt
    
            self._build_mappings()

    7.5

            # TODO implement prolongation
    
        def save(self, filename, pointData=None, cellData=None):
            """Export the mesh and fields using meshio. (Hexahedron version.)
    
            Parameters
            ----------
            filename : string
                The filename for vtk-file.
            pointData : (optional) numpy array for one output or dict for multiple
            cellData : (optional) numpy array for one output or dict for multiple
            """
            import meshio
    
            # vtk requires a different ordering
            t = self.t[[0, 3, 6, 2, 1, 5, 7, 4], :]
    
            if pointData is not None:
                if type(pointData) != dict:
                    pointData = {'0':pointData}
    
            if cellData is not None:
                if type(cellData) != dict:
                    cellData = {'0':cellData}
    
            cells = { 'hexahedron' : t.T }
            mesh = meshio.Mesh(self.p.T, cells, pointData, cellData)
            meshio.write(filename, mesh)

    7.6

        def mapping(self):
            return skfem.mapping.MappingIsoparametric(self, skfem.element.ElementHex1())

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    四边体剖分

    class MeshTet(Mesh3D):
        """A mesh consisting of tetrahedral elements.
    
        Attributes
        ----------
        p : numpy array of size 3 x Nvertices
            The vertices of the mesh. Each column corresponds to a point.
        t : numpy array of size 4 x Nelements
            The element connectivity. Each column corresponds to a element
            and contains four column indices to MeshTet.p.
        facets : numpy array of size 3 x Nfacets
            Each column contains a triplet of column indices to MeshTet.p.
            Order: (0, 1, 2) (0, 1, 3) (0, 2, 3) (1, 2, 3)
        f2t : numpy array of size 2 x Nfacets
            Each column contains a pair of column indices to MeshTet.t
            or -1 on the second row if the facet is located on the boundary.
        t2f : numpy array of size 4 x Nelements
            Each column contains four indices to MeshTet.facets.
        edges : numpy array of size 2 x Nedges
            Each column corresponds to an edge and contains two indices to
            MeshTet.p.
            Order: (0, 1) (1, 2) (0, 2) (0, 3) (1, 3) (2, 3)
        t2e : numpy array of size 6 x Nelements
            Each column contains six indices to MeshTet.edges.
    
        Examples
        --------
    
        Read a tetrahedral mesh from gmsh-format using meshio.
    
        >>> m = MeshTet.load('examples/box.msh')
        >>> m.p.shape
        (3, 358)
        """
    
        refdom = "tet"
        brefdom = "tri"
        meshio_type = "tetra"

    8.1

        def __init__(self, p=None, t=None, validate=True):
            if p is None and t is None:
                p = np.array([[0., 0., 0.], [0., 0., 1.], [0., 1., 0.], [1., 0., 0.],
                              [0., 1., 1.], [1., 0., 1.], [1., 1., 0.], [1., 1., 1.]]).T
                t = np.array([[0, 1, 2, 3], [3, 5, 1, 7], [2, 3, 6, 7],
                              [2, 3, 1, 7], [1, 2, 4, 7]]).T
            elif p is None or t is None:
                raise Exception("Must provide p AND t or neither")
            self.p = p
            self.t = t
            if validate:
                self._validate()
            self.enable_facets = True
            self._build_mappings()
            super(MeshTet, self).__init__()

    8.2

        def _build_mappings(self):
            """Build element-to-facet, element-to-edges, etc. mappings."""
            # define edges: in the order (0,1) (1,2) (0,2) (0,3) (1,3) (2,3)
            self.edges = np.sort(np.vstack((self.t[0, :], self.t[1, :])), axis=0)
            e = np.array([1, 2,
                          0, 2,
                          0, 3,
                          1, 3,
                          2, 3])
            for i in range(5):
                self.edges = np.hstack((self.edges,
                                        np.sort(np.vstack((self.t[e[2*i], :],
                                                           self.t[e[2*i+1], :])),
                                                axis=0)))
    
            # unique edges
            self.edges, ixa, ixb = np.unique(self.edges, axis=1, return_index=True, return_inverse=True)
            self.edges = np.ascontiguousarray(self.edges)
    
            self.t2e = ixb.reshape((6, self.t.shape[1]))
    
            # define facets
            if self.enable_facets:
                self.facets = np.sort(np.vstack((self.t[0, :],
                                                 self.t[1, :],
                                                 self.t[2, :])), axis=0)
                f = np.array([0, 1, 3,
                              0, 2, 3,
                              1, 2, 3])
                for i in range(3):
                    self.facets = np.hstack((self.facets,
                                             np.sort(np.vstack((self.t[f[2*i], :],
                                                                self.t[f[2*i+1], :],
                                                                self.t[f[2*i+2]])),
                                                     axis=0)))
    
                # unique facets
                self.facets, ixa, ixb = np.unique(self.facets, axis=1, return_index=True, return_inverse=True)
                self.facets = np.ascontiguousarray(self.facets)
    
                self.t2f = ixb.reshape((4, self.t.shape[1]))
    
                # build facet-to-tetra mapping: 2 (tets) x Nfacets
                e_tmp = np.hstack((self.t2f[0, :], self.t2f[1, :],
                                   self.t2f[2, :], self.t2f[3, :]))
                t_tmp = np.tile(np.arange(self.t.shape[1]), (1, 4))[0]
    
                e_first, ix_first = np.unique(e_tmp, return_index=True)
                # this emulates matlab unique(e_tmp,'last')
                e_last, ix_last = np.unique(e_tmp[::-1], return_index=True)
                ix_last = e_tmp.shape[0] - ix_last-1
    
                self.f2t = np.zeros((2, self.facets.shape[1]), dtype=np.int64)
                self.f2t[0, e_first] = t_tmp[ix_first]
                self.f2t[1, e_last] = t_tmp[ix_last]
    
                # second row to zero if repeated (i.e., on boundary)
                self.f2t[1, np.nonzero(self.f2t[0, :] == self.f2t[1, :])[0]] = -1

    8.3

        def refine(self, N=None):
            """Refine the mesh, tetrahedral optimization.
            
            Parameters
            ----------
            N : (optional) int
                Perform N refinements.
            """
            if N is None:
                return self._uniform_refine()
            else:
                self.enable_facets = False
                for itr in range(N-1):
                    self._uniform_refine()
                self.enable_facets = True
                self._uniform_refine()

    8.4

        def _uniform_refine(self):
            """Perform a single mesh refine.
    
            Let the nodes of a tetrahedron be numbered as 0, 1, 2 and 3.
            It is assumed that the edges in self.t2e are given in the order
    
              I=(0,1), II=(1,2), III=(0,2), IV=(0,3), V=(1,3), VI=(2,3)
    
            by self._build_mappings(). Let I denote the midpoint of the edge
            (0,1), II denote the midpoint of the edge (1,2), etc. Then each
            tetrahedron is split into eight smaller subtetrahedra as follows.
    
            The first four subtetrahedra have the following nodes:
    
              1. (0,I,III,IV)
              2. (1,I,II,V)
              3. (2,II,III,VI)
              4. (3,IV,V,VI)
    
            The remaining middle-portion of the original tetrahedron consists
            of a union of two mirrored pyramids. This bi-pyramid can be splitted
            into four tetrahedra in a three different ways by connecting the
            midpoints of two opposing edges (there are three different pairs
            of opposite edges).
    
            For each tetrahedra in the original mesh, we split the bi-pyramid
            in such a way that the connection between the opposite edges
            is shortest. This minimizes the shape-regularity constant of
            the resulting mesh family.
            """
            # rename variables
            t = self.t
            p = self.p
            e = self.edges
            sz = p.shape[1]
            t2e = self.t2e + sz
            # new vertices are the midpoints of edges
            newp = 0.5*np.vstack((p[0, e[0, :]] + p[0, e[1, :]],
                                  p[1, e[0, :]] + p[1, e[1, :]],
                                  p[2, e[0, :]] + p[2, e[1, :]]))
            newp = np.hstack((p, newp))
            # new tets
            newt = np.vstack((t[0, :], t2e[0, :], t2e[2, :], t2e[3, :]))
            newt = np.hstack((newt, np.vstack((t[1, :], t2e[0, :], t2e[1, :], t2e[4, :]))))
            newt = np.hstack((newt, np.vstack((t[2, :], t2e[1, :], t2e[2, :], t2e[5, :]))))
            newt = np.hstack((newt, np.vstack((t[3, :], t2e[3, :], t2e[4, :], t2e[5, :]))))
            # compute middle pyramid diagonal lengths and choose shortest
            d1 = ((newp[0, t2e[2, :]] - newp[0, t2e[4, :]])**2 +
                  (newp[1, t2e[2, :]] - newp[1, t2e[4, :]])**2)
            d2 = ((newp[0, t2e[1, :]] - newp[0, t2e[3, :]])**2 +
                  (newp[1, t2e[1, :]] - newp[1, t2e[3, :]])**2)
            d3 = ((newp[0, t2e[0, :]] - newp[0, t2e[5, :]])**2 +
                  (newp[1, t2e[0, :]] - newp[1, t2e[5, :]])**2)
            I1 = d1 < d2
            I2 = d1 < d3
            I3 = d2 < d3
            c1 = I1*I2
            c2 = (~I1)*I3
            c3 = (~I2)*(~I3)
            # splitting the pyramid in the middle.
            # diagonals are [2,4], [1,3] and [0,5]
            # CASE 1: diagonal [2,4]
            newt = np.hstack((newt, np.vstack((t2e[2, c1], t2e[4, c1],
                                               t2e[0, c1], t2e[1, c1]))))
            newt = np.hstack((newt, np.vstack((t2e[2, c1], t2e[4, c1],
                                               t2e[0, c1], t2e[3, c1]))))
            newt = np.hstack((newt, np.vstack((t2e[2, c1], t2e[4, c1],
                                               t2e[1, c1], t2e[5, c1]))))
            newt = np.hstack((newt, np.vstack((t2e[2, c1], t2e[4, c1],
                                               t2e[3, c1], t2e[5, c1]))))
            # CASE 2: diagonal [1,3]
            newt = np.hstack((newt, np.vstack((t2e[1, c2], t2e[3, c2],
                                               t2e[0, c2], t2e[4, c2]))))
            newt = np.hstack((newt, np.vstack((t2e[1, c2], t2e[3, c2],
                                               t2e[4, c2], t2e[5, c2]))))
            newt = np.hstack((newt, np.vstack((t2e[1, c2], t2e[3, c2],
                                               t2e[5, c2], t2e[2, c2]))))
            newt = np.hstack((newt, np.vstack((t2e[1, c2], t2e[3, c2],
                                               t2e[2, c2], t2e[0, c2]))))
            # CASE 3: diagonal [0,5]
            newt = np.hstack((newt, np.vstack((t2e[0, c3], t2e[5, c3],
                                               t2e[1, c3], t2e[4, c3]))))
            newt = np.hstack((newt, np.vstack((t2e[0, c3], t2e[5, c3],
                                               t2e[4, c3], t2e[3, c3]))))
            newt = np.hstack((newt, np.vstack((t2e[0, c3], t2e[5, c3],
                                               t2e[3, c3], t2e[2, c3]))))
            newt = np.hstack((newt, np.vstack((t2e[0, c3], t2e[5, c3],
                                               t2e[2, c3], t2e[1, c3]))))
            # update fields
            self.p = newp
            self.t = newt
    
            self._build_mappings()

    8.5

            # TODO implement prolongation matrix
    
        def shapereg(self):
            """Return the largest shape-regularity constant."""
            def edgelen(n):
                return np.sqrt(np.sum((self.p[:, self.edges[0, self.t2e[n, :]]] -
                                       self.p[:, self.edges[1, self.t2e[n, :]]])**2,
                                      axis=0))
            edgelenmat = np.vstack(tuple(edgelen(i) for i in range(6)))
            return np.max(np.max(edgelenmat, axis=0)/np.min(edgelenmat, axis=0))

    8.6

        def mapping(self):
            return skfem.mapping.MappingAffine(self)

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    三角剖分

    class MeshTri(Mesh2D):
        """A mesh consisting of triangular elements.
        
        Attributes
        ----------
        p : numpy array of size 2 x Nvertices
            The vertices of the mesh
        t : numpy array of size 3 x Nelements
            The element connectivity
        facets : numpy array of size 2 x Nfacets
            Each column contains a pair of indices to p.
        f2t : numpy array of size 2 x Nfacets
            Each column contains a pair of indices to t
            or -1 on the second row if the facet is on
            the boundary.
        t2f : numpy array of size 3 x Nelements
            Each column contains three indices to facets.
    
        Examples
        --------
    
        Initialize a symmetric mesh of the unit square.
    
        >>> m = MeshTri.init_sqsymmetric()
        >>> m.t.shape
        (3, 8)
    
        The different constructors are:
            * load (requires meshio)
            * init_symmetric
            * init_sqsymmetric
            * init_refdom
    
        Facets (edges) and mappings from triangles to facets and vice versa are
        automatically constructed. In the following example we have 5 facets
        (edges).
    
        >>> m = MeshTri()
        >>> m.facets
        array([[0, 0, 1, 1, 2],
               [1, 2, 2, 3, 3]])
        >>> m.t2f
        array([[0, 2],
               [2, 4],
               [1, 3]])
        >>> m.f2t
        array([[ 0,  0,  1,  1,  1],
               [-1, -1,  0, -1, -1]])
    
        The value -1 implies that the facet (the edge) is on the boundary.
    
        Refine the triangular mesh of the unit square three times.
    
        >>> m = MeshTri()
        >>> m.refine(3)
        >>> m.p.shape
        (2, 81)
        """
    
        refdom = "tri"
        brefdom = "line"
        meshio_type = "triangle"
    
        p = np.array([])
        t = np.array([])
        facets = np.array([])
        f2t = np.array([])
        t2f = np.array([])

    9.1

        def __init__(self, p=None, t=None, validate=True, sort_t=True):
            """Initialize a triangular mesh.
    
            Parameters
            ----------
            p : (optional) numpy array of size 2 x Nvertices
                The points of the mesh.
            t : (optional) numpy array of size 3 x Nelements
                The element connectivity, i.e. indices to p.
            validate : (optional, default=True) bool
                Whether to run mesh validity checks or not.
            sort_t : (optional, default=True) bool
                Sort the element connectivity matrix before building mappings.
            """
            if p is None and t is None:
                p = np.array([[0., 1., 0., 1.], [0., 0., 1., 1.]], dtype=np.float_)
                t = np.array([[0, 1, 2], [1, 3, 2]], dtype=np.intp).T
            elif p is None or t is None:
                raise Exception("Must provide p AND t or neither")
            self.p = p
            self.t = t
            if validate:
                self._validate()
            self._build_mappings(sort_t=sort_t)
            super(MeshTri, self).__init__()

    9.2

        @classmethod
        def init_tensor(cls, x, y):
            """Initialize a tensor product mesh.
    
            Parameters
            ----------
            x : numpy array (1d)
                The nodal coordinates in dimension x
            y : numpy array (1d)
                The nodal coordinates in dimension y
            """
            return MeshQuad.init_tensor(x, y)._splitquads()

    9.3

        @classmethod
        def init_symmetric(cls):
            """Initialize a symmetric mesh of the unit square.
            
            The mesh topology is as follows:
            *------------*
            |          /|
            |        /  |
            |      /    |
            |     *      |
            |    /      |
            |  /        |
            |/          |
            *------------*
            """
            p = np.array([[0, 1, 1, 0, 0.5],
                          [0, 0, 1, 1, 0.5]], dtype=np.float_)
            t = np.array([[0, 1, 4],
                          [1, 2, 4],
                          [2, 3, 4],
                          [0, 3, 4]], dtype=np.intp).T
            return cls(p, t)

    9.4

        @classmethod
        def init_sqsymmetric(cls):
            """Initialize a symmetric mesh of the unit square.
            
            The mesh topology is as follows:
            *------------*
            |    |     /|
            |    |   /  |
            |    | /    |
            |-----*------|
            |    /|     |
            |  /  |     |
            |/    |     |
            *------------*
            """
            p = np.array([[0, 0.5, 1,   0, 0.5,   1, 0, 0.5, 1],
                          [0, 0,   0, 0.5, 0.5, 0.5, 1,   1, 1]], dtype=np.float_)
            t = np.array([[0, 1, 4],
                          [1, 2, 4],
                          [2, 4, 5],
                          [0, 3, 4],
                          [3, 4, 6],
                          [4, 6, 7],
                          [4, 7, 8],
                          [4, 5, 8]], dtype=np.intp).T
            return cls(p, t)

    9.5

        @classmethod
        def init_refdom(cls):
            """Initialize a mesh that includes only the reference triangle.
            
            The mesh topology is as follows:
            *
            |           
            |           
            |           
            |            
            |            
            |            
            |             
            *-------------*
            """
            p = np.array([[0., 1., 0.],
                          [0., 0., 1.]], dtype=np.float_)
            t = np.array([[0, 1, 2]], dtype=np.intp).T
            return cls(p, t)

    9.6

        def _build_mappings(self, sort_t=True):
            # sort to preserve orientations etc.
            if sort_t:
                self.t = np.sort(self.t, axis=0)
    
            # define facets: in the order (0,1) (1,2) (0,2)
            self.facets = np.sort(np.vstack((self.t[0, :], self.t[1, :])), axis=0)
            self.facets = np.hstack((self.facets,
                                     np.sort(np.vstack((self.t[1, :], self.t[2, :])),
                                             axis=0)))
            self.facets = np.hstack((self.facets,
                                     np.sort(np.vstack((self.t[0, :], self.t[2, :])),
                                             axis=0)))
    
            # get unique facets and build triangle-to-facet
            # mapping: 3 (edges) x Ntris
            tmp = np.ascontiguousarray(self.facets.T)
            tmp, ixa, ixb = np.unique(tmp.view([('', tmp.dtype)] * tmp.shape[1]),
                                      return_index=True, return_inverse=True)
            self.facets = self.facets[:, ixa]
            self.t2f = ixb.reshape((3, self.t.shape[1]))
    
            # build facet-to-triangle mapping: 2 (triangles) x Nedges
            e_tmp = np.hstack((self.t2f[0, :], self.t2f[1, :], self.t2f[2, :]))
            t_tmp = np.tile(np.arange(self.t.shape[1]), (1, 3))[0]
    
            e_first, ix_first = np.unique(e_tmp, return_index=True)
            # this emulates matlab unique(e_tmp,'last')
            e_last, ix_last = np.unique(e_tmp[::-1], return_index=True)
            ix_last = e_tmp.shape[0] - ix_last - 1
    
            self.f2t = np.zeros((2, self.facets.shape[1]), dtype=np.int64)
            self.f2t[0, e_first] = t_tmp[ix_first]
            self.f2t[1, e_last] = t_tmp[ix_last]
    
            # second row to zero if repeated (i.e., on boundary)
            self.f2t[1, np.nonzero(self.f2t[0, :] == self.f2t[1, :])[0]] = -1

    9.7

        def interpolator(self, x):
            """Return a function which interpolates values with P1 basis."""
            triang = mtri.Triangulation(self.p[0, :], self.p[1, :], self.t.T)
            interpf = mtri.LinearTriInterpolator(triang, x)
            # contruct an interpolator handle
            def handle(X, Y):
                return interpf(X, Y).data
            return handle

    9.8

        def const_interpolator(self, x):
            """Return a function which interpolates values with P0 basis."""
            triang = mtri.Triangulation(self.p[0, :], self.p[1, :], self.t.T)
            finder = triang.get_trifinder()
            # construct an interpolator handle
            def handle(X, Y):
                return x[finder(X, Y)]
            return handle

    9.9

        def smooth(self, k=1.0):
            """Apply smoothing to interior nodes."""
            from skfem.assembly import InteriorBasis, asm
            from skfem.element import ElementTriP1
            from skfem.utils import solve
            from skfem.models.poisson import laplace, mass
    
            e = ElementTriP1()
            ib = InteriorBasis(self, e)
    
            K = asm(laplace, ib)
            M = asm(mass, ib)
    
            I = self.interior_nodes()
            dx = - k*solve(M, K.dot(self.p[0, :]))
            dy = - k*solve(M, K.dot(self.p[1, :]))
    
            self.p[0, I] += dx[I]
            self.p[1, I] += dy[I]

    9.10

        def draw_debug(self):
            """Draw without mesh.facets. For debugging self.draw()."""
            fig = plt.figure()
            plt.hold('on')
            for itr in range(self.t.shape[1]):
                plt.plot(self.p[0,self.t[[0,1],itr]], self.p[1,self.t[[0,1],itr]], 'k-')
                plt.plot(self.p[0,self.t[[1,2],itr]], self.p[1,self.t[[1,2],itr]], 'k-')
                plt.plot(self.p[0,self.t[[0,2],itr]], self.p[1,self.t[[0,2],itr]], 'k-')
            return fig

    9.11

        def plot(self, z, smooth=False, ax=None, zlim=None, edgecolors=None):
            """Visualize nodal or elemental function (2d)."""
            if ax is None:
                fig = plt.figure()
                ax = fig.add_subplot(111)
            if edgecolors is None:
                edgecolors = 'k'
            if zlim == None:
                if smooth:
                    ax.tripcolor(self.p[0, :], self.p[1, :], self.t.T, z,
                                  shading='gouraud', edgecolors=edgecolors)
                else:
                    ax.tripcolor(self.p[0, :], self.p[1, :], self.t.T, z, edgecolors=edgecolors)
            else:
                if smooth:
                    ax.tripcolor(self.p[0, :], self.p[1, :], self.t.T, z,
                                  shading='gouraud', vmin=zlim[0], vmax=zlim[1], edgecolors=edgecolors)
                else:
                    ax.tripcolor(self.p[0, :], self.p[1, :], self.t.T, z,
                                  vmin=zlim[0], vmax=zlim[1], edgecolors=edgecolors)
            return ax

    9.12

        def plot3(self, z, smooth=False, ax=None):
            """Visualize nodal function (3d i.e. three axes)."""
            from mpl_toolkits.mplot3d import Axes3D
            if ax is None:
                fig = plt.figure()
                ax = Axes3D(fig)
            if len(z) == self.p.shape[1]:
                # use matplotlib
                ts = mtri.Triangulation(self.p[0, :], self.p[1, :], self.t.T)
                ax.plot_trisurf(self.p[0, :], self.p[1, :], z,
                                triangles=ts.triangles,
                                cmap=plt.cm.Spectral)
            elif len(z) == self.t.shape[1]:
                # one value per element (piecewise const)
                nt = self.t.shape[1]
                newt = np.arange(3*nt, dtype=np.int64).reshape((nt, 3))
                newpx = self.p[0, self.t].flatten(order='F')
                newpy = self.p[1, self.t].flatten(order='F')
                newz = np.vstack((z, z, z)).flatten(order='F')
                ts = mtri.Triangulation(newpx, newpx, newt)
                ax.plot_trisurf(newpx, newpy, newz,
                                triangles=ts.triangles,
                                cmap=plt.cm.Spectral)
            elif len(z) == 3*self.t.shape[1]:
                # three values per element (piecewise linear)
                nt = self.t.shape[1]
                newt = np.arange(3*nt, dtype=np.int64).reshape((nt, 3))
                newpx = self.p[0, self.t].flatten(order='F')
                newpy = self.p[1, self.t].flatten(order='F')
                ts = mtri.Triangulation(newpx, newpx, newt)
                ax.plot_trisurf(newpx, newpy, z,
                                triangles=ts.triangles,
                                cmap=plt.cm.Spectral)
            else:
                raise NotImplementedError("MeshTri.plot3: not implemented for "
                                          "the given shape of input vector!")
            return ax

    9.13

        def _uniform_refine(self):
            """Perform a single mesh refine."""
            # rename variables
            t = self.t
            p = self.p
            e = self.facets
            sz = p.shape[1]
            t2f = self.t2f + sz
            # new vertices are the midpoints of edges
            newp = 0.5*np.vstack((p[0, e[0, :]] + p[0, e[1, :]],
                                  p[1, e[0, :]] + p[1, e[1, :]]))
            newp = np.hstack((p, newp))
            # build new triangle definitions
            newt = np.vstack((t[0, :], t2f[0, :], t2f[2, :]))
            newt = np.hstack((newt, np.vstack((t[1, :], t2f[0, :], t2f[1, :]))))
            newt = np.hstack((newt, np.vstack((t[2, :], t2f[2, :], t2f[1, :]))))
            newt = np.hstack((newt, np.vstack((t2f[0, :], t2f[1, :], t2f[2, :]))))
            # update fields
            self.p = newp
            self.t = newt
    
            self._build_mappings()
    
            # prolongation matrix
            nsz = newp.shape[1]
            return coo_matrix(
                (np.hstack((np.ones(sz), .5 * np.ones(2 * (nsz - sz)))),
                 (np.hstack((np.arange(sz), np.arange(nsz - sz) + sz, np.arange(nsz - sz) + sz)),
                  np.hstack((np.arange(sz), e[0, :], e[1, :])))),
                shape=(nsz, sz)).tocsr()

    9.14

        def _uniform_refine(self):
            """Perform a single mesh refine."""
            # rename variables
            t = self.t
            p = self.p
            e = self.facets
            sz = p.shape[1]
            t2f = self.t2f + sz
            # new vertices are the midpoints of edges
            newp = 0.5*np.vstack((p[0, e[0, :]] + p[0, e[1, :]],
                                  p[1, e[0, :]] + p[1, e[1, :]]))
            newp = np.hstack((p, newp))
            # build new triangle definitions
            newt = np.vstack((t[0, :], t2f[0, :], t2f[2, :]))
            newt = np.hstack((newt, np.vstack((t[1, :], t2f[0, :], t2f[1, :]))))
            newt = np.hstack((newt, np.vstack((t[2, :], t2f[2, :], t2f[1, :]))))
            newt = np.hstack((newt, np.vstack((t2f[0, :], t2f[1, :], t2f[2, :]))))
            # update fields
            self.p = newp
            self.t = newt
    
            self._build_mappings()
    
            # prolongation matrix
            nsz = newp.shape[1]
            return coo_matrix(
                (np.hstack((np.ones(sz), .5 * np.ones(2 * (nsz - sz)))),
                 (np.hstack((np.arange(sz), np.arange(nsz - sz) + sz, np.arange(nsz - sz) + sz)),
                  np.hstack((np.arange(sz), e[0, :], e[1, :])))),
                shape=(nsz, sz)).tocsr()

    9.15

        def mapping(self):
            return skfem.mapping.MappingAffine(self)

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    if __name__ == "__main__":
        import doctest
        doctest.testmod()
  • 相关阅读:
    php public protected private属性实例详解
    php 获取代码执行时间和消耗的内存
    php使用位与运算符【&】或【|】实现权限管理
    jquery checkbox选中、改变状态、change和click事件
    PHP设置脚本最大执行时间的三种方法
    Windows上node.js的多版本管理工具
    win10家庭版安装Docker for Windows
    spring boot读取Excel
    JAVA泛型知识--> <? extends T>和<? super T>
    D3.js的v5版本入门教程(第十三章)—— 饼状图
  • 原文地址:https://www.cnblogs.com/wangshixi12/p/9438656.html
Copyright © 2011-2022 走看看