def alg_3_6(self, maxit=None):
        """
        1. 最粗网格上求解最小特征特征值问题,得到最小特征值 d_H 和特征向量 u_H
        2. 自适应求解  - \Delta u_h = u_H
            *  u_H 插值到下一层网格上做为新 u_H
        3. 最新网格层上求出的 uh 做为一个基函数,加入到最粗网格的有限元空间中,
           求解最小特征值问题。

        自适应 maxit, picard:2
        """
        print("算法 3.6")
        if maxit is None:
            maxit = self.maxit

        start = timer()
        if self.step == 0:
            idx = []
        else:
            idx = list(range(0, self.maxit, self.step)) + [self.maxit - 1]

        mesh = self.pde.init_mesh(n=self.numrefine)
        integrator = mesh.integrator(self.q)

        # 1. 粗网格上求解最小特征值问题
        space = LagrangeFiniteElementSpace(mesh, 1)
        AH = self.get_stiff_matrix(space)
        MH = self.get_mass_matrix(space)
        isFreeHDof = ~(space.boundary_dof())

        gdof = space.number_of_global_dofs()
        print("initial mesh :", gdof)

        uH = np.zeros(gdof, dtype=np.float)

        A = AH[isFreeHDof, :][:, isFreeHDof].tocsr()
        M = MH[isFreeHDof, :][:, isFreeHDof].tocsr()

        if self.matlab is False:
            uH[isFreeHDof], d = self.eig(A, M)
        else:
            uH[isFreeHDof], d = self.meigs(A, M)

        uh = space.function()
        uh[:] = uH

        GD = mesh.geo_dimension()
        if (self.step > 0) and (0 in idx):
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_6_0_' + str(NN) + '.pdf')
            plt.close()
            self.savemesh(mesh,
                          self.resultdir + 'mesh_3_6_0_' + str(NN) + '.mat')

        # 2. 以 u_H 为右端项自适应求解 -\Deta u = u_H
        I = eye(gdof)
        final = 0

        while True:
            eta = self.residual_estimate(uh)
            markedCell = mark(eta, self.theta)
            IM = mesh.bisect(markedCell, returnim=True)
            print(final + 1, "refine :", mesh.number_of_nodes())
            if (self.step > 0) and (final in idx):
                NN = mesh.number_of_nodes()
                fig = plt.figure()
                fig.set_facecolor('white')
                if GD == 2:
                    axes = fig.gca()
                else:
                    axes = Axes3D(fig)
                mesh.add_plot(axes, cellcolor='w')
                fig.savefig(self.resultdir + 'mesh_3_6_' + str(final + 1) +
                            '_' + str(NN) + '.pdf')
                plt.close()
                self.savemesh(
                    mesh, self.resultdir + 'mesh_3_6_' + str(final + 1) + '_' +
                    str(NN) + '.mat')

            I = IM @ I
            uH = IM @ uH

            space = LagrangeFiniteElementSpace(mesh, 1)
            gdof = space.number_of_global_dofs()

            A = self.get_stiff_matrix(space)
            M = self.get_mass_matrix(space)
            isFreeDof = ~(space.boundary_dof())
            b = M @ uH

            uh = space.function()
            if self.matlab is False:
                uh[isFreeDof] = self.psolve(
                    A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof],
                    M[isFreeDof, :][:, isFreeDof].tocsr())
            else:
                uh[isFreeDof] = self.msolve(
                    A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof])
            final += 1
            if gdof > 3e+4:
                break

        # 1. 自适应粗网格上求解最小特征值问题
        space = LagrangeFiniteElementSpace(mesh, 1)
        AH = self.get_stiff_matrix(space)
        MH = self.get_mass_matrix(space)
        isFreeHDof = ~(space.boundary_dof())

        gdof = space.number_of_global_dofs()
        print("initial mesh :", gdof)

        uH = np.zeros(gdof, dtype=np.float)

        A = AH[isFreeHDof, :][:, isFreeHDof].tocsr()
        M = MH[isFreeHDof, :][:, isFreeHDof].tocsr()

        if self.matlab is False:
            uH[isFreeHDof], d = self.eig(A, M)
        else:
            uH[isFreeHDof], d = self.meigs(A, M)

        uh = space.function()
        uh[:] = uH

        GD = mesh.geo_dimension()
        if (self.step > 0) and (0 in idx):
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_6_' + str(final) + '_' +
                        str(NN) + 'H.pdf')
            plt.close()
            self.savemesh(
                mesh, self.resultdir + 'mesh_3_6_' + str(final) + '_' +
                str(NN) + 'H.mat')

        I = eye(gdof)
        for i in range(final, maxit):
            eta = self.residual_estimate(uh)
            markedCell = mark(eta, self.theta)
            IM = mesh.bisect(markedCell, returnim=True)
            print(i + 1, "refine :", mesh.number_of_nodes())
            if (self.step > 0) and (i in idx):
                NN = mesh.number_of_nodes()
                fig = plt.figure()
                fig.set_facecolor('white')
                if GD == 2:
                    axes = fig.gca()
                else:
                    axes = Axes3D(fig)
                mesh.add_plot(axes, cellcolor='w')
                fig.savefig(self.resultdir + 'mesh_3_4_' + str(i + 1) + '_' +
                            str(NN) + '.pdf')
                plt.close()
                self.savemesh(
                    mesh, self.resultdir + 'mesh_3_4_' + str(i + 1) + '_' +
                    str(NN) + '.mat')

            I = IM @ I
            uH = IM @ uH

            space = LagrangeFiniteElementSpace(mesh, 1)
            gdof = space.number_of_global_dofs()

            A = self.get_stiff_matrix(space)
            M = self.get_mass_matrix(space)
            isFreeDof = ~(space.boundary_dof())
            b = M @ uH

            uh = space.function()
            if self.matlab is False:
                uh[isFreeDof] = self.psolve(
                    A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof],
                    M[isFreeDof, :][:, isFreeDof].tocsr())
            else:
                uh[isFreeDof] = self.msolve(
                    A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof])
            final = i + 1
            if gdof > self.maxdof:
                break

        if self.step > 0:
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_6_' + str(final) + '_' +
                        str(NN) + 'f.pdf')
            plt.close()
            self.savemesh(
                mesh, self.resultdir + 'mesh_3_6_' + str(final) + '_' +
                str(NN) + 'f.mat')

        # 3. 把 uh 加入粗网格空间, 组装刚度和质量矩阵
        w0 = uh @ A
        w1 = w0 @ uh
        w2 = w0 @ I
        AA = bmat([[AH, w2.reshape(-1, 1)], [w2, w1]], format='csr')

        w0 = uh @ M
        w1 = w0 @ uh
        w2 = w0 @ I
        MM = bmat([[MH, w2.reshape(-1, 1)], [w2, w1]], format='csr')

        isFreeDof = np.r_[isFreeHDof, True]

        u = np.zeros(len(isFreeDof))

        ## 求解特征值
        A = AA[isFreeDof, :][:, isFreeDof].tocsr()
        M = MM[isFreeDof, :][:, isFreeDof].tocsr()

        if self.multieigs is True:
            self.A = A
            self.M = M
            self.ml = pyamg.ruge_stuben_solver(self.A)
            self.eigs()
        else:
            if self.matlab is False:
                u[isFreeDof], d = self.eig(A, M)
            else:
                u[isFreeDof], d = self.meigs(A, M)

            print("smallest eigns:", d)
            end = timer()
            print("with time: ", end - start)
            uh *= u[-1]
            uh += I @ u[:-1]

            uh /= np.max(np.abs(uh))
            uh = space.function(array=uh)
            return uh
Exemple #2
0
class CahnHilliardRFEMModel():
    def __init__(self, pde, n, tau, q):
        self.pde = pde
        self.mesh = pde.space_mesh(n)
        self.timemesh, self.tau = self.pde.time_mesh(tau)
        self.femspace = LagrangeFiniteElementSpace(self.mesh, 1)

        self.uh0 = self.femspace.interpolation(pde.initdata)
        self.uh1 = self.femspace.function()

        self.area = self.mesh.entity_measure('cell')

        self.integrator = self.mesh.integrator(q)
        self.integralalg = IntegralAlg(self.integrator, self.mesh, self.area)

        self.A, self.B, self.gradphi = grad_recovery_matrix(self.femspace)
        self.M = doperator.mass_matrix(self.femspace, self.integrator,
                                       self.area)
        self.K = self.get_stiff_matrix()
        self.D = self.M + self.tau * self.K
        self.ml = pyamg.ruge_stuben_solver(self.D)
        print(self.ml)
        self.current = 0

    def get_stiff_matrix(self):
        mesh = self.mesh
        area = self.area

        gradphi = self.gradphi
        A = self.A
        B = self.B
        NC = mesh.number_of_cells()
        NN = mesh.number_of_nodes()

        node = mesh.entity('node')
        edge = mesh.entity('edge')
        cell = mesh.entity('cell')

        edge2cell = mesh.ds.edge_to_cell()
        isBdEdge = (edge2cell[:, 0] == edge2cell[:, 1])
        bdEdge = edge[isBdEdge]
        cellIdx = edge2cell[isBdEdge, [0]]

        # construct the unit outward normal on the boundary
        W = np.array([[0, -1], [1, 0]], dtype=np.int)
        n = (node[bdEdge[:, 1], ] - node[bdEdge[:, 0], :]) @ W
        h = np.sqrt(np.sum(n**2, axis=1))
        n /= h.reshape(-1, 1)

        # 计算梯度的恢复矩阵
        I = np.einsum('ij, k->ijk', cell, np.ones(3))
        J = I.swapaxes(-1, -2)

        val = np.einsum('i, ij, ik->ijk', area, gradphi[:, :, 0], gradphi[:, :,
                                                                          0])
        P = csc_matrix((val.flat, (I.flat, J.flat)), shape=(NN, NN))

        val = np.einsum('i, ij, ik->ijk', area, gradphi[:, :, 0], gradphi[:, :,
                                                                          1])
        Q = csc_matrix((val.flat, (I.flat, J.flat)), shape=(NN, NN))

        val = np.einsum('i, ij, ik->ijk', area, gradphi[:, :, 1], gradphi[:, :,
                                                                          1])
        S = csc_matrix((val.flat, (I.flat, J.flat)), shape=(NN, NN))

        K = A.transpose() @ P @ A + A.transpose() @ Q @ B + B.transpose(
        ) @ Q.transpose() @ A + B.transpose() @ S @ B

        # 中间的边界上的两项
        I = np.einsum('ij, k->ijk', bdEdge, np.ones(3))
        J = np.einsum('ij, k->ikj', cell[cellIdx], np.ones(2))
        val0 = 0.5 * h.reshape(-1, 1) * n[:, [0]] * gradphi[cellIdx, :, 0]
        val0 = np.repeat(val0, 2, axis=0).reshape(-1, 2, 2)
        P0 = csc_matrix((val0.flat, (I.flat, J.flat)), shape=(NN, NN))

        val0 = 0.5 * h.reshape(-1, 1) * n[:, [0]] * gradphi[cellIdx, :, 1]
        val0 = np.repeat(val0, 2, axis=0).reshape(-1, 2, 2)
        Q0 = csc_matrix((val0.flat, (I.flat, J.flat)), shape=(NN, NN))

        val0 = 0.5 * h.reshape(-1, 1) * n[:, [1]] * gradphi[cellIdx, :, 0]
        val0 = np.repeat(val0, 2, axis=0).reshape(-1, 2, 2)
        P1 = csc_matrix((val0.flat, (I.flat, J.flat)), shape=(NN, NN))

        val0 = 0.5 * h.reshape(-1, 1) * n[:, [1]] * gradphi[cellIdx, :, 1]
        val0 = np.repeat(val0, 2, axis=0).reshape(-1, 2, 2)
        Q1 = csc_matrix((val0.flat, (I.flat, J.flat)), shape=(NN, NN))

        M = A.transpose() @ P0 @ A + A.transpose() @ Q0 @ B + B.transpose(
        ) @ P1 @ A + B.transpose() @ Q1 @ B

        K -= (M + M.transpose())
        K *= self.pde.epsilon**2

        # 边界上两个方向导数相乘的积分
        I = np.einsum('ij, k->ijk', bdEdge, np.ones(2))
        J = I.swapaxes(-1, -2)
        val = np.array([(1 / 3, 1 / 6), (1 / 6, 1 / 3)])
        val0 = np.einsum('i, jk->ijk', n[:, 0] * n[:, 0] / h, val)
        P = csc_matrix((val0.flat, (I.flat, J.flat)), shape=(NN, NN))
        val0 = np.einsum('i, jk->ijk', n[:, 0] * n[:, 1] / h, val)
        Q = csc_matrix((val0.flat, (I.flat, J.flat)), shape=(NN, NN))
        val0 = np.einsum('i, jk->ijk', n[:, 1] * n[:, 1] / h, val)
        S = csc_matrix((val0.flat, (I.flat, J.flat)), shape=(NN, NN))

        K += self.pde.epsilon**2 * (
            A.transpose() @ P @ A + A.transpose() @ Q @ B +
            B.transpose() @ Q @ A + B.transpose() @ S @ B)
        return K

        # def get_right_vector(self):

    #    uh = self.uh0
    #    b =  self.get_non_linear_vector()
    #    return self.M@uh  - self.tau*b

    # def get_non_linear_vector(self):
    #    uh = self.uh0
    #    bcs, ws = self.integrator.quadpts, self.integrator.weights
    #    gradphi = self.femspace.grad_basis(bcs)

    #    uval = uh.value(bcs)
    #    guval = uh.grad_value(bcs)
    #    fval = (3*uval[..., np.newaxis]**2 - 1)*guval
    #    bb = np.einsum('i, ikm, ikjm, k->kj', ws, fval, gradphi, self.area)
    #    cell2dof = self.femspace.cell_to_dof()
    #    gdof = self.femspace.number_of_global_dofs()
    #    b = np.bincount(cell2dof.flat, weights=bb.flat, minlength=gdof)
    #    return b

    def get_right_vector(self, t):
        uh = self.uh0

        def f(x):
            return self.pde.source(x, t)

        b = doperator.source_vector(f, self.femspace, self.integrator,
                                    self.area)
        return self.M @ uh + self.tau * b

    def solve(self):
        timemesh = self.timemesh
        tau = self.tau
        N = len(timemesh)
        print(N)
        D = self.D
        for i in range(N):
            t = timemesh[i]
            b = self.get_right_vector(t)
            # self.uh1[:] =  spsolve(D, b)
            self.uh1[:] = self.ml.solve(b, tol=1e-12, accel='cg').reshape(
                (-1, ))

            self.current = i
            if self.current % 2 == 0:
                self.show_soultion()
            self.uh0[:] = self.uh1[:]
        error = self.get_L2_error((N - 1) * tau)
        print(error)

    #    def step(self):
    #        D = self.D
    #        b = self.get_right_vector(self.current)
    #        self.uh[:, self.current + 1] = spsolve(D, b)
    #        self.current += 1

    def show_soultion(self):
        mesh = self.mesh
        timemesh = self.timemesh
        cell = mesh.entity('cell')
        node = mesh.entity('node')
        fig = plt.figure()
        fig.set_facecolor('white')
        axes = fig.gca(projection='3d')
        axes.plot_trisurf(node[:, 0],
                          node[:, 1],
                          cell,
                          self.uh0,
                          cmap=plt.cm.jet,
                          lw=0.0)
        plt.savefig('./results/cahnHilliard' + str(self.current) + '.png')

    def get_L2_error(self, t):
        def solution(x):
            return self.pde.solution(x, t)

        u = solution
        uh = self.uh0.value
        L2 = self.integralalg.L2_error(u, uh)
        return L2
Exemple #3
0
class TwoFluidsWithGeostressSimulator():
    """

    Notes
    -----
    这是一个模拟两种流体和地质力学耦合的程序  

    * S_0: 流体 0 的饱和度,一般是水
    * S_1: 流体 1 的饱和度,可以为气或油
    * v: 总速度
    * p: 压力
    * u: 岩石位移  

    饱和度 P0 间断元,单元边界用迎风格式
    速度 RT0 元
    压强 P0 元
    岩石位移 P1 连续元离散

    目前, 模型
    * 忽略了毛细管压强和重力作用
    * 饱和度用分片线性间断元求解, 非线性的迎风格式

    渐近解决方案:
    1. Picard 迭代
    2. 气的可压性随着压强的变化而变化
    3. 考虑渗透率随着孔隙度的变化而变化 
    4. 考虑裂缝,裂缝用自适应网格加密,设设置更大的渗透率实现

    体积模量:  K = E/3/(1 - 2*nu) = lambda + 2/3* mu

    Develop
    ------

    """
    def __init__(self, mesh, args):
        self.args = args  # 模拟相关参数
        self.mesh = mesh

        NT = int((args.T1 - args.T0) / args.DT)
        self.timeline = UniformTimeLine(args.T0, args.T1, NT)

        self.GD = mesh.geo_dimension()
        if self.GD == 2:
            self.vspace = RaviartThomasFiniteElementSpace2d(self.mesh,
                                                            p=0)  # 速度空间
        elif self.GD == 3:
            self.vspace = RaviartThomasFiniteElementSpace3d(self.mesh, p=0)

        self.pspace = self.vspace.smspace  # 压力和饱和度所属的空间, 分片常数
        self.cspace = LagrangeFiniteElementSpace(self.mesh, p=1)  # 位移空间

        # 上一时刻物理量的值
        self.v = self.vspace.function()  # 速度函数
        self.p = self.pspace.function()  # 压力函数
        self.s = self.pspace.function()  # 水的饱和度函数 默认为0, 初始时刻区域内水的饱和度为0
        self.u = self.cspace.function(dim=self.GD)  # 位移函数
        self.phi = self.pspace.function()  # 孔隙度函数, 分片常数

        # 当前时刻物理量的值, 用于保存临时计算出的值, 模型中系数的计算由当前时刻
        # 的物理量的值决定
        self.cv = self.vspace.function()  # 速度函数
        self.cp = self.pspace.function()  # 压力函数
        self.cs = self.pspace.function()  # 水的饱和度函数 默认为0, 初始时刻区域内水的饱和度为0
        self.cu = self.cspace.function(dim=self.GD)  # 位移函数
        self.cphi = self.pspace.function()  # 孔隙度函数, 分片常数

        # 初值
        self.s[:] = self.mesh.celldata['fluid_0']
        self.p[:] = self.mesh.celldata['pressure_0']  # MPa
        self.phi[:] = self.mesh.celldata['porosity_0']  # 初始孔隙度

        self.s[:] = self.mesh.celldata['fluid_0']
        self.cp[:] = self.p  # 初始地层压力
        self.cphi[:] = self.phi  # 当前孔隙度系数

        # 源汇项
        self.f0 = self.cspace.function()
        self.f1 = self.cspace.function()

        self.f0[:] = self.mesh.nodedata['source_0']  # 流体 0 的源汇项
        self.f1[:] = self.mesh.nodedata['source_1']  # 流体 1 的源汇项

        # 一些常数矩阵和向量

        # 速度散度矩阵, 速度方程对应的散度矩阵, (\nabla\cdot v, w)
        self.B = self.vspace.div_matrix()

        # 压力方程对应的位移散度矩阵, (\nabla\cdot u, w) 位移散度矩阵
        # * 注意这里利用了压力空间分片常数, 线性函数导数也是分片常数的事实
        c = self.mesh.entity_measure('cell')
        c *= self.mesh.celldata['biot']

        val = self.mesh.grad_lambda()  # (NC, TD+1, GD)
        val *= c[:, None, None]
        pc2d = self.pspace.cell_to_dof()
        cc2d = self.cspace.cell_to_dof()
        pgdof = self.pspace.number_of_global_dofs()
        cgdof = self.cspace.number_of_global_dofs()
        I = np.broadcast_to(pc2d, shape=cc2d.shape)
        J = cc2d
        self.PU0 = csr_matrix((val[..., 0].flat, (I.flat, J.flat)),
                              shape=(pgdof, cgdof))
        self.PU1 = csr_matrix((val[..., 1].flat, (I.flat, J.flat)),
                              shape=(pgdof, cgdof))

        if self.GD == 3:
            self.PU2 = csr_matrix((val[..., 2].flat, (I.flat, J.flat)),
                                  shape=(pgdof, cgdof))

        # 线弹性矩阵的右端向量
        self.FU = np.zeros(self.GD * cgdof, dtype=np.float64)
        self.FU[0 * cgdof:1 * cgdof] -= self.p @ self.PU0
        self.FU[1 * cgdof:2 * cgdof] -= self.p @ self.PU1

        if self.GD == 3:
            self.FU[2 * cgdof:3 * cgdof] -= self.p @ self.PU2

        # 初始应力和等效应力项
        sigma = self.mesh.celldata['stress_0'] + self.mesh.celldata[
            'stress_eff']  # 初始应力和等效应力之和
        self.FU[0 * cgdof:1 * cgdof] -= sigma @ self.PU0
        self.FU[1 * cgdof:2 * cgdof] -= sigma @ self.PU1
        if self.GD == 3:
            self.FU[2 * cgdof:3 * cgdof] -= sigma0 @ self.PU2

    def recover(self, val):
        """

        Notes
        -----
        给定一个分片常数的量, 恢复为分片连续的量
        """

        mesh = self.mesh
        cell = self.mesh.entity('cell')
        NC = mesh.number_of_cells()
        NN = mesh.number_of_nodes()

        w = 1 / self.mesh.entity_measure('cell')  # 恢复权重
        w = np.broadcast_to(w[:, None], shape=cell.shape)

        r = np.zeros(NN, dtype=np.float64)
        d = np.zeros(NN, dtype=np.float64)

        np.add.at(d, cell, w)
        np.add.at(r, cell, val[:, None] * w)

        return r / d

    def add_time(self, n):
        """

        Notes
        ----

        增加 n 步的计算时间 
        """
        self.timeline.add_time(n)

    def pressure_coefficient(self):
        """

        Notes
        -----
        计算当前物理量下的压强质量矩阵系数
        """

        b = self.mesh.celldata['biot']
        K = self.mesh.celldata['K']
        c0 = self.mesh.meshdata['fluid_0']['compressibility']
        c1 = self.mesh.meshdata['fluid_1']['compressibility']

        s = self.cs  # 流体 0 当前的饱和度 (NQ, NC)
        phi = self.cphi  # 当前的孔隙度

        val = phi * s * c0
        val += phi * (1 - s) * c1  # 注意这里的 co 是常数, 但在水气混合物条件下应该依赖于压力
        val += (b - phi) / K
        return val

    def saturation_pressure_coefficient(self):
        """

        Notes
        -----
        计算当前物理量下, 饱和度方程中, 压强项对应的系数
        """

        b = self.mesh.celldata['biot']
        K = self.mesh.celldata['K']
        c0 = self.mesh.meshdata['fluid_0']['compressibility']

        s = self.cs  # 当前流体 0 的饱和度
        phi = self.cphi  # 当前孔隙度

        val = (b - phi) / K
        val += phi * c0
        val *= s
        return val

    def flux_coefficient(self):
        """
        Notes
        -----

        计算**当前**物理量下, 速度方程对应的系数

        计算通量的系数, 流体的相对渗透率除以粘性系数得到流动性系数 
        k_0/mu_0 是气体的流动性系数 
        k_1/mu_1 是油的流动性系数

        这里假设压强的单位是 MPa 

        1 d = 9.869 233e-13 m^2 = 1000 md
        1 cp = 1 mPa s = 1e-9 MPa*s = 1e-3 Pa*s

        """

        mu0 = self.mesh.meshdata['fluid_0']['viscosity']  # 单位是 1 cp = 1 mPa.s
        mu1 = self.mesh.meshdata['fluid_1']['viscosity']  # 单位是 1 cp = 1 mPa.s

        # 岩石的绝对渗透率, 这里考虑了量纲的一致性, 压强单位是 Pa
        k = self.mesh.celldata['permeability'] * 9.869233e-4

        s = self.cs  # 流体 0 当前的饱和度系数

        lam0 = self.mesh.fluid_relative_permeability_0(
            s)  # TODO: 考虑更复杂的饱和度和渗透的关系
        lam0 /= mu0

        lam1 = self.mesh.fluid_relative_permeability_1(
            s)  # TODO: 考虑更复杂的饱和度和渗透的关系
        lam1 /= mu1

        val = 1 / (lam0 + lam1)
        val /= k

        return val

    def fluid_fractional_flow_coefficient_0(self):
        """

        Notes
        -----

        计算**当前**物理量下, 饱和度方程中, 主流体的的流动性系数
        """

        s = self.cs  # 流体 0 当前的饱和度系数

        mu0 = self.mesh.meshdata['fluid_0']['viscosity']  # 单位是 1 cp = 1 mPa.s
        lam0 = self.mesh.fluid_relative_permeability_0(s)
        lam0 /= mu0

        lam1 = self.mesh.fluid_relative_permeability_1(s)
        mu1 = self.mesh.meshdata['fluid_1']['viscosity']  # 单位是 1 cp = 1 mPa.s
        lam1 /= mu1

        val = lam0 / (lam0 + lam1)
        return val

    def get_total_system(self):
        """
        Notes
        -----
        构造整个系统

        二维情形:
        x = [s, v, p, u0, u1]

        A = [[   S, None,   SP,  SU0,  SU1]
             [None,    V,   VP, None, None]
             [None,   PV,    P,  PU0,  PU1]
             [None, None,  UP0,  U00,  U01]
             [None, None,  UP1,  U10,  U11]]
        F = [FS, FV, FP, FU0, FU1]

        三维情形:

        x = [s, v, p, u0, u1, u2]
        A = [[   S, None,   SP,  SU0,  SU1,  SU2]
             [None,    V,   VP, None, None, None]
             [None,   PV,    P,  PU0,  PU1,  PU2]
             [None, None,  UP0,  U00,  U01,  U02]
             [None, None,  UP1,  U10,  U11,  U12]
             [None, None,  UP2,  U20,  U21,  U22]]
        F = [FS, FV, FP, FU0, FU1, FU2]

        """

        GD = self.GD
        A0, FS, isBdDof0 = self.get_saturation_system(q=2)
        A1, FV, isBdDof1 = self.get_velocity_system(q=2)
        A2, FP, isBdDof2 = self.get_pressure_system(q=2)

        if GD == 2:
            A3, A4, FU, isBdDof3 = self.get_dispacement_system(q=2)
            A = bmat([A0, A1, A2, A3, A4], format='csr')
            F = np.r_['0', FS, FV, FP, FU]
        elif GD == 3:
            A3, A4, A5, FU, isBdDof3 = self.get_dispacement_system(q=2)
            A = bmat([A0, A1, A2, A3, A4, A5], format='csr')
            F = np.r_['0', FS, FV, FP, FU]
        isBdDof = np.r_['0', isBdDof0, isBdDof1, isBdDof2, isBdDof3]
        return A, F, isBdDof

    def get_saturation_system(self, q=2):
        """
        Notes
        ----
        计算饱和度方程对应的离散系统

        [   S, None,   SP,  SU0,  SU1]

        [   S, None,   SP,  SU0,  SU1, SU2]
        """

        GD = self.GD
        qf = self.mesh.integrator(q, etype='cell')
        bcs, ws = qf.get_quadrature_points_and_weights()
        cellmeasure = self.mesh.entity_measure('cell')

        # SP 是对角矩阵
        c = self.saturation_pressure_coefficient()  # (NC, )
        c *= cellmeasure
        SP = diags(c, 0)

        # S 质量矩阵组装, 对角矩阵
        c = self.cphi[:] * cellmeasure
        S = diags(c, 0)

        # SU0, SU1, 饱和度方程中的位移散度对应的矩阵
        b = self.mesh.celldata['biot']
        val = self.mesh.grad_lambda()  # (NC, TD+1, GD)
        c = self.cs[:] * b  # (NC, ), 注意用当前的水饱和度
        c *= cellmeasure
        val *= c[:, None, None]

        pgdof = self.pspace.number_of_global_dofs()  # 压力空间自由度个数
        cgdof = self.cspace.number_of_global_dofs()  # 连续空间自由度个数

        pc2d = self.pspace.cell_to_dof()
        cc2d = self.cspace.cell_to_dof()

        I = np.broadcast_to(pc2d, shape=cc2d.shape)
        J = cc2d

        SU0 = csr_matrix((val[..., 0].flat, (I.flat, J.flat)),
                         shape=(pgdof, cgdof))
        SU1 = csr_matrix((val[..., 1].flat, (I.flat, J.flat)),
                         shape=(pgdof, cgdof))

        if GD == 3:
            SU2 = csr_matrix((val[..., 2].flat, (I.flat, J.flat)),
                             shape=(pgdof, cgdof))

        # 右端矩阵
        dt = self.timeline.current_time_step_length()
        FS = self.f0.value(bcs)  # (NQ, NC)
        FS *= ws[:, None]
        FS = np.sum(FS, axis=0)
        FS *= cellmeasure
        FS *= dt

        FS += SP @ self.p  # 上一时间步的压强
        FS += S @ self.s  # 上一时间步的饱和度
        FS += SU0 @ self.u[:, 0]  # 上一时间步的位移 x 分量
        FS += SU1 @ self.u[:, 1]  # 上一个时间步的位移 y 分量
        if GD == 3:
            FS += SU2 @ self.u[:, 2]  # 上一个时间步的位移 z 分量

        # 用当前时刻的速度场, 构造迎风格式
        face2cell = self.mesh.ds.face_to_cell()
        isBdFace = face2cell[:, 0] == face2cell[:, 1]

        qf = self.mesh.integrator(2, 'face')  # 边或面上的积分公式
        bcs, ws = qf.get_quadrature_points_and_weights()
        facemeasure = self.mesh.entity_measure('face')

        # 边的定向法线,它是左边单元的外法线,右边单元内法线。
        fn = self.mesh.face_unit_normal()
        val0 = np.einsum('qfm, fm->qf', self.cv.face_value(bcs),
                         fn)  # 当前速度和法线的内积

        # 流体 0 的流动分数, 与水的饱和度有关, 如果饱和度为0, 则为 0
        F0 = self.fluid_fractional_flow_coefficient_0()
        val1 = F0[face2cell[:, 0]]  # 边的左边单元的水流动分数
        val2 = F0[face2cell[:, 1]]  # 边的右边单元的水流动分数
        val2[isBdFace] = 0.0  # 边界的贡献是 0,没有流入和流出

        flag = val0 < 0.0  # 对于左边单元来说,是流入项
        # 对于右边单元来说,是流出项

        # 左右单元流入流出的绝对量是一样的
        val = val0 * val1[None, :]  # val0 >= 0.0, 左边单元是流出
        # val0 < 0.0, 左边单元是流入
        val[flag] = (val0 * val2[None, :])[flag]  # val0 >= 0, 右边单元是流入
        # val0 < 0, 右边单元是流出

        b = np.einsum('q, qf, f->f', ws, val, facemeasure)
        b *= dt
        np.subtract.at(FS, face2cell[:, 0], b)

        isInFace = ~isBdFace  # 只处理内部边
        np.add.at(FS, face2cell[isInFace, 1], b[isInFace])

        isBdDof = np.zeros(pgdof, dtype=np.bool_)

        if GD == 2:
            return [S, None, SP, SU0, SU1], FS, isBdDof
        elif GD == 3:
            return [S, None, SP, SU0, SU1, SU2], FS, isBdDof

    def get_velocity_system(self, q=2):
        """
        Notes
        -----
        计算速度方程对应的离散系统.


         [None,    V,   VP, None, None]

         [None,    V,   VP, None, None, None]
        """

        GD = self.GD
        dt = self.timeline.current_time_step_length()
        cellmeasure = self.mesh.entity_measure('cell')
        qf = self.mesh.integrator(q, etype='cell')
        bcs, ws = qf.get_quadrature_points_and_weights()

        # 速度对应的矩阵  V
        c = self.flux_coefficient()
        c *= cellmeasure
        phi = self.vspace.basis(bcs)
        V = np.einsum('q, qcin, qcjn, c->cij', ws, phi, phi, c, optimize=True)

        c2d = self.vspace.cell_to_dof()
        I = np.broadcast_to(c2d[:, :, None], shape=V.shape)
        J = np.broadcast_to(c2d[:, None, :], shape=V.shape)

        gdof = self.vspace.number_of_global_dofs()
        V = csr_matrix((V.flat, (I.flat, J.flat)), shape=(gdof, gdof))

        # 压强矩阵
        VP = -self.B

        # 右端向量, 0 向量
        FV = np.zeros(gdof, dtype=np.float64)
        isBdDof = self.vspace.dof.is_boundary_dof()

        if GD == 2:
            return [None, V, VP, None, None], FV, isBdDof
        elif GD == 3:
            return [None, V, VP, None, None, None], FV, isBdDof

    def get_pressure_system(self, q=2):
        """
        Notes
        -----
        计算压强方程对应的离散系统

        这里组装矩阵时, 利用了压强是分片常数的特殊性 

        [  Noe,  PV,   P, PU0,   PU1]

        [  None, PV,   P, PU0,   PU1, PU2]
        """

        GD = self.GD
        dt = self.timeline.current_time_step_length()
        cellmeasure = self.mesh.entity_measure('cell')
        qf = self.mesh.integrator(q, etype='cell')
        bcs, ws = qf.get_quadrature_points_and_weights()

        PV = dt * self.B.T

        # P 是对角矩阵, 利用分片常数的
        c = self.pressure_coefficient()  # (NQ, NC)
        c *= cellmeasure
        P = diags(c, 0)

        # 组装压力方程的右端向量
        # * 这里利用了压力空间基是分片常数
        FP = self.f1.value(bcs) + self.f0.value(bcs)  # (NQ, NC)
        FP *= ws[:, None]

        FP = np.sum(FP, axis=0)
        FP *= cellmeasure
        FP *= dt

        FP += P @ self.p  # 上一步的压力向量
        FP += self.PU0 @ self.u[:, 0]
        FP += self.PU1 @ self.u[:, 1]

        if GD == 3:
            FP += self.PU2 @ self.u[:, 2]

        gdof = self.pspace.number_of_global_dofs()
        isBdDof = np.zeros(gdof, dtype=np.bool_)

        if GD == 2:
            return [None, PV, P, self.PU0, self.PU1], FP, isBdDof
        elif GD == 3:
            return [None, PV, P, self.PU0, self.PU1, self.PU2], FP, isBdDof

    def get_dispacement_system(self, q=2):
        """
        Notes
        -----
        计算位移方程对应的离散系统, 即线弹性方程系统.
        
        GD == 2:
         [None, None, UP0, U00,   U01]
         [None, None, UP1, U10,   U11]

        GD == 3:
         [None, None, UP0, U00, U01, U02]
         [None, None, UP1, U10, U11, U12]
         [None, None, UP2, U20, U21, U22]

        """

        GD = self.GD
        # 拉梅参数 (lambda, mu)
        lam = self.mesh.celldata['lambda']
        mu = self.mesh.celldata['mu']
        U = self.linear_elasticity_matrix(lam, mu, format='list')

        isBdDof = self.cspace.dof.is_boundary_dof()

        if GD == 2:
            return ([None, None, -self.PU0.T, U[0][0],
                     U[0][1]], [None, None, -self.PU1.T, U[1][0],
                                U[1][1]], self.FU, np.r_['0', isBdDof,
                                                         isBdDof])
        elif GD == 3:
            return ([None, None, -self.PU0.T, U[0][0], U[0][1], U[0][2]
                     ], [None, None, -self.PU1.T, U[1][0], U[1][1], U[1][2]],
                    [None, None, -self.PU2.T, U[2][0], U[2][1],
                     U[2][2]], self.FU, np.r_['0', isBdDof, isBdDof, isBdDof])

    def linear_elasticity_matrix(self, lam, mu, format='csr', q=None):
        """
        Notes
        -----
        注意这里的拉梅常数是单元分片常数
        lam.shape == (NC, ) # MPa
        mu.shape == (NC, ) # MPa
        """

        GD = self.GD
        if GD == 2:
            idx = [(0, 0), (0, 1), (1, 1)]
            imap = {(0, 0): 0, (0, 1): 1, (1, 1): 2}
        elif GD == 3:
            idx = [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)]
            imap = {
                (0, 0): 0,
                (0, 1): 1,
                (0, 2): 2,
                (1, 1): 3,
                (1, 2): 4,
                (2, 2): 5
            }
        A = []

        qf = self.cspace.integrator if q is None else self.mesh.integrator(
            q, 'cell')
        bcs, ws = qf.get_quadrature_points_and_weights()
        grad = self.cspace.grad_basis(bcs)  # (NQ, NC, ldof, GD)

        # 分块组装矩阵
        gdof = self.cspace.number_of_global_dofs()
        cellmeasure = self.cspace.cellmeasure
        for k, (i, j) in enumerate(idx):
            Aij = np.einsum('i, ijm, ijn, j->jmn', ws, grad[..., i],
                            grad[..., j], cellmeasure)
            A.append(Aij)

        if GD == 2:
            C = [[None, None], [None, None]]
            D = mu[:, None, None] * (A[0] + A[2])
        elif GD == 3:
            C = [[None, None, None], [None, None, None], [None, None, None]]
            D = mu[:, None, None] * (A[0] + A[3] + A[5])

        cell2dof = self.cspace.cell_to_dof()  # (NC, ldof)
        ldof = self.cspace.number_of_local_dofs()
        NC = self.mesh.number_of_cells()
        shape = (NC, ldof, ldof)
        I = np.broadcast_to(cell2dof[:, :, None], shape=shape)
        J = np.broadcast_to(cell2dof[:, None, :], shape=shape)

        for i in range(GD):
            Aii = D + (mu + lam)[:, None, None] * A[imap[(i, i)]]
            C[i][i] = csr_matrix((Aii.flat, (I.flat, J.flat)),
                                 shape=(gdof, gdof))
            for j in range(i + 1, GD):
                Aij = lam[:, None, None] * A[imap[
                    (i, j)]] + mu[:, None, None] * A[imap[
                        (i, j)]].swapaxes(-1, -2)
                C[i][j] = csr_matrix((Aij.flat, (I.flat, J.flat)),
                                     shape=(gdof, gdof))
                C[j][i] = C[i][j].T

        if format == 'csr':
            return bmat(C, format='csr')  # format = bsr ??
        elif format == 'bsr':
            return bmat(C, format='bsr')
        elif format == 'list':
            return C

    def solve_linear_system_0(self, ctx=None):
        # 构建总系统
        A, F, isBdDof = self.get_total_system()

        # 处理边界条件, 这里是 0 边界
        gdof = len(isBdDof)
        bdIdx = np.zeros(gdof, dtype=np.int_)
        bdIdx[isBdDof] = 1
        Tbd = diags(bdIdx)
        T = diags(1 - bdIdx)
        A = T @ A @ T + Tbd
        F[isBdDof] = 0.0

        # 求解
        if ctx is None:
            x = spsolve(A, F)
        else:
            if ctx.myid == 0:
                ctx.set_centralized_sparse(A)
                x = F.copy()
                ctx.set_rhs(x)  # Modified in place
            ctx.set_silent()
            ctx.run(job=6)  # Analysis + Factorization + Solve
            #ctx.destroy() # Cleanup

        vgdof = self.vspace.number_of_global_dofs()
        pgdof = self.pspace.number_of_global_dofs()
        cgdof = self.cspace.number_of_global_dofs()

        start = 0
        end = pgdof
        s = x[start:end]

        start = end
        end += vgdof
        v = x[start:end]

        start = end
        end += pgdof
        p = x[start:end]

        start = end
        u = x[start:]

        return s, v, p, u

    def solve_linear_system_1(self, ctx=None):
        """
        Notes
        -----
        构造整个系统

        二维情形:
        x = [s, v, p, u0, u1]

        A = [[   S, None,   SP,  SU0,  SU1]
             [None,    V,   VP, None, None]
             [None,   PV,    P,  PU0,  PU1]
             [None, None,  UP0,  U00,  U01]
             [None, None,  UP1,  U10,  U11]]
        F = [FS, FV, FP, FU0, FU1]

        三维情形:

        x = [s, v, p, u0, u1, u2]
        A = [[   S, None,   SP,  SU0,  SU1,  SU2]
             [None,    V,   VP, None, None, None]
             [None,   PV,    P,  PU0,  PU1,  PU2]
             [None, None,  UP0,  U00,  U01,  U02]
             [None, None,  UP1,  U10,  U11,  U12]
             [None, None,  UP2,  U20,  U21,  U22]]
        F = [FS, FV, FP, FU0, FU1, FU2]

        """

        vgdof = self.vspace.number_of_global_dofs()
        pgdof = self.pspace.number_of_global_dofs()
        cgdof = self.cspace.number_of_global_dofs()

        GD = self.GD
        A0, FS, isBdDof0 = self.get_saturation_system(q=2)
        A1, FV, isBdDof1 = self.get_velocity_system(q=2)
        A2, FP, isBdDof2 = self.get_pressure_system(q=2)

        if GD == 2:
            A3, A4, FU, isBdDof3 = self.get_dispacement_system(q=2)
            A = bmat([A1[1:], A2[1:], A3[1:], A4[1:]], format='csr')
            F = np.r_['0', FV, FP, FU]
        elif GD == 3:
            A3, A4, A5, FU, isBdDof3 = self.get_dispacement_system(q=2)
            A = bmat([A1[1:], A2[1:], A3[1:], A4[1:], A5[1:]], format='csr')
            F = np.r_['0', FV, FP, FU]

        isBdDof = np.r_['0', isBdDof1, isBdDof2, isBdDof3]

        gdof = len(isBdDof)
        bdIdx = np.zeros(gdof, dtype=np.int_)
        bdIdx[isBdDof] = 1
        Tbd = diags(bdIdx)
        T = diags(1 - bdIdx)
        A = T @ A @ T + Tbd
        F[isBdDof] = 0.0

        #[   S, None,   SP,  SU0,  SU1, SU2]
        if ctx is None:
            x = spsolve(A, F)
        else:
            if ctx.myid == 0:
                ctx.set_centralized_sparse(A)
                x = F.copy()
                ctx.set_rhs(x)  # Modified in place
            ctx.set_silent()
            ctx.run(job=6)  # Analysis + Factorization + Solve
            #ctx.destroy() # Cleanup

        v = x[0:vgdof]
        p = x[vgdof:vgdof + pgdof]
        u = x[vgdof + pgdof:]

        s = FS
        s -= A0[2] @ p
        s -= A0[3] @ u[0 * cgdof:1 * cgdof]
        s -= A0[4] @ u[1 * cgdof:2 * cgdof]
        if GD == 3:
            s -= A0[5] @ u[2 * cgdof:3 * cgdof]
        s /= A0[0].diagonal()

        return s, v, p, u

    def solve_linear_system_2(self):
        """
        Notes
        -----
        构造整个系统

        二维情形:
        x = [s, v, p, u0, u1]

        A = [[   S, None,   SP,  SU0,  SU1]
             [None,    V,   VP, None, None]
             [None,   PV,    P,  PU0,  PU1]
             [None, None,  UP0,  U00,  U01]
             [None, None,  UP1,  U10,  U11]]
        F = [FS, FV, FP, FU0, FU1]

        三维情形:

        x = [s, v, p, u0, u1, u2]
        A = [[   S, None,   SP,  SU0,  SU1,  SU2]
             [None,    V,   VP, None, None, None]
             [None,   PV,    P,  PU0,  PU1,  PU2]
             [None, None,  UP0,  U00,  U01,  U02]
             [None, None,  UP1,  U10,  U11,  U12]
             [None, None,  UP2,  U20,  U21,  U22]]
        F = [FS, FV, FP, FU0, FU1, FU2]

        """

        vgdof = self.vspace.number_of_global_dofs()
        pgdof = self.pspace.number_of_global_dofs()
        cgdof = self.cspace.number_of_global_dofs()

        GD = self.GD
        A0, FS, isBdDof0 = self.get_saturation_system(q=2)
        A1, FV, isBdDof1 = self.get_velocity_system(q=2)
        A2, FP, isBdDof2 = self.get_pressure_system(q=2)

        if GD == 2:
            A3, A4, FU, isBdDof3 = self.get_dispacement_system(q=2)
            A = bmat([A1[1:], A2[1:], A3[1:], A4[1:]], format='csr')
            F = np.r_['0', FV, FP, FU]
        elif GD == 3:
            A3, A4, A5, FU, isBdDof3 = self.get_dispacement_system(q=2)
            A = bmat([A1[1:], A2[1:], A3[1:], A4[1:], A5[1:]], format='csr')
            F = np.r_['0', FV, FP, FU]

        isBdDof = np.r_['0', isBdDof1, isBdDof2, isBdDof3]

        gdof = len(isBdDof)
        bdIdx = np.zeros(gdof, dtype=np.int_)
        bdIdx[isBdDof] = 1
        Tbd = diags(bdIdx)
        T = diags(1 - bdIdx)
        A = T @ A @ T + Tbd
        F[isBdDof] = 0.0

        #[   S, None,   SP,  SU0,  SU1, SU2]
        solver = FastSover(A, F)
        x = solver.solve()

        v = x[0:vgdof]
        p = x[vgdof:vgdof + pgdof]
        u = x[vgdof + pgdof:]

        s = FS
        s -= A0[2] @ p
        s -= A0[3] @ u[0 * cgdof:1 * cgdof]
        s -= A0[4] @ u[1 * cgdof:2 * cgdof]
        if GD == 3:
            s -= A0[5] @ u[2 * cgdof:3 * cgdof]
        s /= A0[0].diagonal()

        return s, v, p, u

    def picard_iteration(self, ctx=None):
        GD = self.GD
        e0 = 1.0
        k = 0
        while e0 > 1e-10:
            s, v, p, u = self.solve_linear_system_1(ctx=ctx)

            e0 = 0.0
            e0 += np.sum((self.cs - s)**2)
            self.cs[:] = s

            e0 += np.sum((self.cp - p)**2)
            self.cp[:] = p

            self.cv[:] = v
            self.cu.T.flat[:] = u

            e0 = np.sqrt(e0)  # 误差的 l2 norm
            print(e0)

            k += 1
            if k >= self.args.npicard:
                print('picard iteration arrive max iteration with error:', e0)
                break

        # 更新解
        self.s[:] = self.cs
        self.v[:] = self.cv
        self.p[:] = self.cp
        self.u[:] = self.cu

    def update_mesh_data(self):
        """

        Notes
        -----
        更新 mesh 中的数据
        """
        GD = self.GD
        bc = np.array((GD + 1) * [1 / (GD + 1)], dtype=np.float64)

        mesh = self.mesh

        v = self.v
        p = self.p
        s = self.s
        u = self.u

        # 单元中心的流体速度
        val = v.value(bc)
        if GD == 2:
            val = np.concatenate(
                (val, np.zeros((val.shape[0], 1), dtype=val.dtype)), axis=1)
        mesh.celldata['velocity'] = val

        # 分片常数的压强
        val = self.recover(p[:])
        mesh.nodedata['pressure'] = val

        # 分片常数的饱和度
        val = self.recover(s[:])
        mesh.nodedata['fluid_0'] = val

        val = self.recover(1 - s)
        mesh.nodedata['fluid_1'] = val

        # 节点处的位移
        s = u.grad_value(bc)  # (NC, GD, GD)
        if GD == 2:
            u = np.concatenate((u[:], np.zeros(
                (u.shape[0], 1), dtype=u.dtype)),
                               axis=1)

        mesh.nodedata['displacement'] = u[:]

        # 增加应变和应力的计算

        NC = self.mesh.number_of_cells()
        s0 = np.zeros((NC, 3, 3), dtype=np.float64)
        s1 = np.zeros((NC, 3, 3), dtype=np.float64)

        s0[:, 0:GD, 0:GD] = s + s.swapaxes(-1, -2)
        s0[:, 0:GD, 0:GD] /= 2

        lam = self.mesh.celldata['lambda']
        mu = self.mesh.celldata['mu']

        s1[:, 0:GD, 0:GD] = s0[:, 0:GD, 0:GD]
        s1[:, 0:GD, 0:GD] *= mu[:, None, None]
        s1[:, 0:GD, 0:GD] *= 2

        s1[:, range(GD),
           range(GD)] += (lam * s0.trace(axis1=-1, axis2=-2))[:, None]
        s1[:, range(GD), range(GD)] += mesh.celldata['stress_0'][:, None]
        s1[:, range(GD), range(GD)] += mesh.celldata['stress_eff'][:, None]
        s1[:, range(GD),
           range(GD)] -= (mesh.celldata['biot'] *
                          (p - mesh.celldata['pressure_0']))[:, None]

        mesh.celldata['strain'] = s0.reshape(NC, -1)
        mesh.celldata['stress'] = s1.reshape(NC, -1)

    def run(self, ctx=None, writer=None, queue=None):
        """

        Notes
        -----

        计算所有时间层物理量。
        """

        args = self.args

        timeline = self.timeline
        dt = timeline.current_time_step_length()

        if queue is not None:
            n = timeline.current
            fname = args.output + str(n).zfill(10) + '.vtu'
            self.update_mesh_data()
            data = {'name': fname, 'mesh': self.mesh}
            queue.put(data)

        if writer is not None:
            n = timeline.current
            fname = args.output + str(n).zfill(10) + '.vtu'
            self.update_mesh_data()
            writer(fname, self.mesh)

        while not timeline.stop():
            ct = timeline.current_time_level() / 3600 / 24  # 天为单位
            print('当前时刻为第', ct, '天')
            self.picard_iteration(ctx=ctx)
            timeline.current += 1
            if timeline.current % args.step == 0:
                if queue is not None:
                    n = timeline.current
                    fname = args.output + str(n).zfill(10) + '.vtu'
                    self.update_mesh_data()
                    data = {'name': fname, 'mesh': self.mesh}
                    queue.put(data)

                if writer is not None:
                    n = timeline.current
                    fname = args.output + str(n).zfill(10) + '.vtu'
                    self.update_mesh_data()
                    writer(fname, self.mesh)

        if queue is not None:
            n = timeline.current
            fname = args.output + str(n).zfill(10) + '.vtu'
            self.update_mesh_data()
            data = {'name': fname, 'mesh': self.mesh}
            queue.put(data)
            queue.put(-1)  # 发送模拟结束信号

        if writer is not None:
            n = timeline.current
            fname = args.output + str(n).zfill(10) + '.vtu'
            self.update_mesh_data()
            writer(fname, self.mesh)
    def alg_3_2(self, maxit=None):
        """
        1. 自适应求解 -\Delta u = 1。
        1. 在最细网格上求最小特征值和特征向量。

        refine maxit, picard: 1
        """
        print("算法 3.2")
        if maxit is None:
            maxit = self.maxit

        start = timer()
        if self.step == 0:
            idx = []
        else:
            idx = list(range(0, self.maxit, self.step)) + [self.maxit - 1]

        mesh = self.pde.init_mesh(n=self.numrefine)
        GD = mesh.geo_dimension()
        if (self.step > 0) and (0 in idx):
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_2_0_' + str(NN) + '.pdf')
            plt.close()
            self.savemesh(mesh,
                          self.resultdir + 'mesh_3_2_0_' + str(NN) + '.mat')

        final = 0
        integrator = mesh.integrator(self.q)
        for i in range(maxit):
            space = LagrangeFiniteElementSpace(mesh, 1)
            gdof = space.number_of_global_dofs()

            A = self.get_stiff_matrix(space)
            M = self.get_mass_matrix(space)
            b = M @ np.ones(gdof)

            isFreeDof = ~(space.boundary_dof())
            A = A[isFreeDof, :][:, isFreeDof].tocsr()
            M = M[isFreeDof, :][:, isFreeDof].tocsr()

            uh = space.function()
            if self.matlab is False:
                uh[isFreeDof] = self.psolve(A, b[isFreeDof], M)
            else:
                uh[isFreeDof] = self.msolve(A, b[isFreeDof])

            eta = self.residual_estimate(uh)
            markedCell = mark(eta, self.theta)
            IM = mesh.bisect(markedCell, returnim=True)
            NN = mesh.number_of_nodes()
            print(i + 1, "refine :", NN)
            if (self.step > 0) and (i in idx):
                fig = plt.figure()
                fig.set_facecolor('white')
                if GD == 2:
                    axes = fig.gca()
                else:
                    axes = Axes3D(fig)
                mesh.add_plot(axes, cellcolor='w')
                fig.savefig(self.resultdir + 'mesh_3_2_' + str(i + 1) + '_' +
                            str(NN) + '.pdf')
                plt.close()
                self.savemesh(
                    mesh, self.resultdir + 'mesh_3_2_' + str(i + 1) + '_' +
                    str(NN) + '.mat')
            final = i + 1
            if NN > self.maxdof:
                break

        if self.step > 0:
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_2_' + str(final) + '_' +
                        str(NN) + '.pdf')
            plt.close()
            self.savemesh(
                mesh, self.resultdir + 'mesh_3_2_' + str(final) + '_' +
                str(NN) + '.mat')

        space = LagrangeFiniteElementSpace(mesh, 1)
        gdof = space.number_of_global_dofs()

        A = self.get_stiff_matrix(space)
        M = self.get_mass_matrix(space)
        isFreeDof = ~(space.boundary_dof())
        A = A[isFreeDof, :][:, isFreeDof].tocsr()
        M = M[isFreeDof, :][:, isFreeDof].tocsr()

        if self.multieigs is True:
            self.A = A
            self.M = M
            self.ml = pyamg.ruge_stuben_solver(self.A)
            self.eigs()
        else:
            uh = IM @ uh
            if self.matlab is False:
                uh[isFreeDof], d = self.eig(A, M)
            else:
                uh[isFreeDof], d = self.meigs(A, M)
            print("smallest eigns:", d)
            end = timer()
            print("with time: ", end - start)
            return space.function(array=uh)
    def alg_3_5(self, maxit=None):
        """
        1. 最粗网格上求解最小特征特征值问题,得到最小特征值 d_H 和特征向量 u_H
        2. 自适应求解  - \Delta u_h = u_H
            *  u_H 插值到下一层网格上做为新 u_H
        3. 在最细网格上求解一次最小特征值问题

        自适应 maxit, picard:2
        """
        print("算法 3.5")

        if maxit is None:
            maxit = self.maxit

        start = timer()

        if self.step == 0:
            idx = []
        else:
            idx = list(range(0, self.maxit, self.step))

        mesh = self.pde.init_mesh(n=self.numrefine)
        # 1. 粗网格上求解最小特征值问题
        space = LagrangeFiniteElementSpace(mesh, 1)
        AH = self.get_stiff_matrix(space)
        MH = self.get_mass_matrix(space)
        isFreeHDof = ~(space.boundary_dof())

        gdof = space.number_of_global_dofs()
        uH = np.zeros(gdof, dtype=mesh.ftype)
        print("initial mesh :", gdof)

        A = AH[isFreeHDof, :][:, isFreeHDof].tocsr()
        M = MH[isFreeHDof, :][:, isFreeHDof].tocsr()
        if self.matlab is False:
            uH[isFreeHDof], d = self.eig(A, M)
        else:
            uH[isFreeHDof], d = self.meigs(A, M)

        uh = space.function()
        uh[:] = uH

        GD = mesh.geo_dimension()
        if (self.step > 0) and (0 in idx):
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_5_0_' + str(NN) + '.pdf')
            plt.close()
            self.savemesh(mesh,
                          self.resultdir + 'mesh_3_5_0_' + str(NN) + '.mat')

        # 2. 以 u_H 为右端项自适应求解 -\Deta u = u_H

        I = eye(gdof)
        final = 0
        while True:
            eta = self.residual_estimate(uh)
            markedCell = mark(eta, self.theta)
            IM = mesh.bisect(markedCell, returnim=True)
            print(final + 1, "refine :", mesh.number_of_nodes())
            if (self.step > 0) and (final in idx):
                NN = mesh.number_of_nodes()
                fig = plt.figure()
                fig.set_facecolor('white')
                if GD == 2:
                    axes = fig.gca()
                else:
                    axes = Axes3D(fig)
                mesh.add_plot(axes, cellcolor='w')
                fig.savefig(self.resultdir + 'mesh_3_5_' + str(final + 1) +
                            '_' + str(NN) + '.pdf')
                plt.close()
                self.savemesh(
                    mesh, self.resultdir + 'mesh_3_5_' + str(final + 1) + '_' +
                    str(NN) + '.mat')

            I = IM @ I
            uH = IM @ uH

            space = LagrangeFiniteElementSpace(mesh, 1)
            gdof = space.number_of_global_dofs()

            A = self.get_stiff_matrix(space)
            M = self.get_mass_matrix(space)
            isFreeDof = ~(space.boundary_dof())
            b = M @ uH

            uh = space.function()
            if self.matlab is False:
                uh[isFreeDof] = self.psolve(
                    A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof],
                    M[isFreeDof, :][:, isFreeDof].tocsr())
            else:
                uh[isFreeDof] = self.msolve(
                    A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof])
            final += 1
            if gdof > 4e+4:
                break

        # 1. 自适应粗网格上求解最小特征值问题
        space = LagrangeFiniteElementSpace(mesh, 1)
        AH = self.get_stiff_matrix(space)
        MH = self.get_mass_matrix(space)
        isFreeHDof = ~(space.boundary_dof())

        gdof = space.number_of_global_dofs()
        print("initial mesh :", gdof)

        uH = np.zeros(gdof, dtype=np.float)

        A = AH[isFreeHDof, :][:, isFreeHDof].tocsr()
        M = MH[isFreeHDof, :][:, isFreeHDof].tocsr()

        if self.matlab is False:
            uH[isFreeHDof], d = self.eig(A, M)
        else:
            uH[isFreeHDof], d = self.meigs(A, M)

        uh = space.function()
        uh[:] = uH

        GD = mesh.geo_dimension()
        if (self.step > 0) and (0 in idx):
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_5_' + str(final) + '_' +
                        str(NN) + 'H.pdf')
            plt.close()
            self.savemesh(
                mesh, self.resultdir + 'mesh_3_5_' + str(final) + '_' +
                str(NN) + 'H.mat')

        I = eye(gdof)
        for i in range(final, maxit - 1):
            eta = self.residual_estimate(uh)
            markedCell = mark(eta, self.theta)
            IM = mesh.bisect(markedCell, returnim=True)
            NN = mesh.number_of_nodes()
            print(i + 1, "refine : ", NN)
            if (self.step > 0) and (i in idx):
                NN = mesh.number_of_nodes()
                fig = plt.figure()
                fig.set_facecolor('white')
                if GD == 2:
                    axes = fig.gca()
                else:
                    axes = Axes3D(fig)
                mesh.add_plot(axes, cellcolor='w')
                fig.savefig(self.resultdir + 'mesh_3_5_' + str(i + 1) + '_' +
                            str(NN) + '.pdf')
                plt.close()
                self.savemesh(
                    mesh, self.resultdir + 'mesh_3_3_' + str(i + 1) + '_' +
                    str(NN) + '.mat')
            final = i + 1

            I = IM @ I
            uH = IM @ uH

            if NN > self.maxdof:
                break

            space = LagrangeFiniteElementSpace(mesh, 1)
            gdof = space.number_of_global_dofs()

            A = self.get_stiff_matrix(space)
            M = self.get_mass_matrix(space)
            isFreeDof = ~(space.boundary_dof())
            b = M @ uH

            uh = space.function()
            if self.matlab is False:
                uh[isFreeDof] = self.psolve(
                    A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof],
                    M[isFreeDof, :][:, isFreeDof].tocsr())
            else:
                uh[isFreeDof] = self.msolve(
                    A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof])

        # 3. 在最细网格上求解一次最小特征值问题

        if self.step > 0:
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_5_' + str(final) + '_' +
                        str(NN) + 'f.pdf')
            plt.close()
            self.savemesh(
                mesh, self.resultdir + 'mesh_3_5_' + str(final) + '_' +
                str(NN) + 'f.mat')

        space = LagrangeFiniteElementSpace(mesh, 1)
        gdof = space.number_of_global_dofs()
        A = self.get_stiff_matrix(space)
        M = self.get_mass_matrix(space)
        isFreeDof = ~(space.boundary_dof())
        uh = space.function(array=uh)
        Ah = A[isFreeDof, :][:, isFreeDof].tocsr()
        Mh = M[isFreeDof, :][:, isFreeDof].tocsr()
        uh = space.function()

        if self.multieigs is True:
            self.A = Ah
            self.M = Mh
            self.ml = pyamg.ruge_stuben_solver(self.A)
            self.eigs()
        else:
            if self.matlab is False:
                uh[isFreeDof], d = self.eig(Ah, Mh)
            else:
                uh[isFreeDof], d = self.meigs(Ah, Mh)
            print("smallest eigns:", d)
            end = timer()
            print("with time: ", end - start)

            return uh
    def alg_0(self, maxit=None):
        """
        1. 最粗网格上求解最小特征特征值问题,得到最小特征值 d_H 和特征向量 u_H
        2. 自适应求解  - \Delta u_h = d_H*u_H
            *  每层网格上求出的 u_h,插值到下一层网格上做为 u_H
            *  并更新 d_H = u_h@A@u_h/u_h@M@u_h, 其中 A 是当前网格层上的刚度矩
               阵,M 为当前网格层的质量矩阵。

        自适应 maxit, picard: 1
        """
        print("算法 0")
        if maxit is None:
            maxit = self.maxit

        start = timer()
        if self.step == 0:
            idx = []
        else:
            idx = list(range(0, self.maxit, self.step)) + [self.maxit - 1]

        mesh = self.pde.init_mesh(n=self.numrefine)

        # 1. 粗网格上求解最小特征值问题
        space = LagrangeFiniteElementSpace(mesh, 1)
        gdof = space.number_of_global_dofs()
        print("initial mesh:", gdof)
        uh = np.zeros(gdof, dtype=np.float)
        AH = self.get_stiff_matrix(space)
        MH = self.get_mass_matrix(space)
        isFreeHDof = ~(space.boundary_dof())
        A = AH[isFreeHDof, :][:, isFreeHDof].tocsr()
        M = MH[isFreeHDof, :][:, isFreeHDof].tocsr()

        if self.picard is True:
            uh[isFreeHDof], d = picard(A,
                                       M,
                                       np.ones(sum(isFreeHDof)),
                                       sigma=self.sigma)
        else:
            uh[isFreeHDof], d = self.eig(A, M)

        GD = mesh.geo_dimension()
        if (self.step > 0) and (0 in idx):
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_0_0_' + str(NN) + '.pdf')
            plt.close()
            self.savemesh(mesh,
                          self.resultdir + 'mesh_0_0_' + str(NN) + '.mat')

        # 2. 以 u_h 为右端项自适应求解 -\Deta u = d*u_h
        I = eye(gdof)
        for i in range(maxit):
            uh = space.function(array=uh)
            eta = self.residual_estimate(uh)
            markedCell = mark(eta, self.theta)
            IM = mesh.bisect(markedCell, returnim=True)
            print(i + 1, "refine: ", mesh.number_of_nodes())
            if (self.step > 0) and (i in idx):
                NN = mesh.number_of_nodes()
                fig = plt.figure()
                fig.set_facecolor('white')
                if GD == 2:
                    axes = fig.gca()
                else:
                    axes = Axes3D(fig)
                mesh.add_plot(axes, cellcolor='w')
                fig.savefig(self.resultdir + 'mesh_0_' + str(i + 1) + '_' +
                            str(NN) + '.pdf')
                plt.close()
                self.savemesh(
                    mesh, self.resultdir + 'mesh_0_' + str(i + 1) + '_' +
                    str(NN) + '.mat')

            I = IM @ I
            uh = IM @ uh
            space = LagrangeFiniteElementSpace(mesh, 1)
            gdof = space.number_of_global_dofs()
            A = self.get_stiff_matrix(space)
            M = self.get_mass_matrix(space)
            isFreeDof = ~(space.boundary_dof())
            b = d * M @ uh
            if self.sigma is None:
                ml = pyamg.ruge_stuben_solver(
                    A[isFreeDof, :][:, isFreeDof].tocsr())
                uh[isFreeDof] = ml.solve(b[isFreeDof],
                                         x0=uh[isFreeDof],
                                         tol=1e-12,
                                         accel='cg').reshape((-1, ))
            else:
                K = A[isFreeDof, :][:, isFreeDof].tocsr(
                ) + self.sigma * M[isFreeDof, :][:, isFreeDof].tocsr()
                b += self.sigma * M @ uh
                ml = pyamg.ruge_stuben_solver(K)
                uh[isFreeDof] = ml.solve(b[isFreeDof],
                                         x0=uh[isFreeDof],
                                         tol=1e-12,
                                         accel='cg').reshape(-1)
                # uh[isFreeDof] = spsolve(A[isFreeDof, :][:, isFreeDof].tocsr(), b[isFreeDof])
            d = uh @ A @ uh / (uh @ M @ uh)

            if gdof > self.maxdof:
                break

        if self.multieigs is True:
            self.A = A[isFreeDof, :][:, isFreeDof].tocsr()
            self.M = M[isFreeDof, :][:, isFreeDof].tocsr()
            self.ml = pyamg.ruge_stuben_solver(self.A)
            self.eigs()

        end = timer()
        print("smallest eigns:", d, "with time: ", end - start)

        uh = space.function(array=uh)
        return uh
    def alg_3_1(self, maxit=None):
        """
        1. 自适应在每层网格上求解最小特征值问题

        refine: maxit, picard: maxit + 1
        """
        print("算法 3.1")

        if maxit is None:
            maxit = self.maxit

        start = timer()
        if self.step == 0:
            idx = []
        else:
            idx = list(range(0, self.maxit, self.step)) + [self.maxit - 1]

        mesh = self.pde.init_mesh(n=self.numrefine)
        GD = mesh.geo_dimension()
        if (self.step > 0) and (0 in idx):
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_1_0_' + str(NN) + '.pdf')
            plt.close()
            self.savemesh(mesh,
                          self.resultdir + 'mesh_3_1_0_' + str(NN) + '.mat')

        space = LagrangeFiniteElementSpace(mesh, 1)
        isFreeDof = ~(space.boundary_dof())
        gdof = space.number_of_global_dofs()
        uh = np.ones(gdof, dtype=mesh.ftype)
        uh[~isFreeDof] = 0
        IM = eye(gdof)
        for i in range(maxit + 1):
            area = mesh.entity_measure('cell')
            A = self.get_stiff_matrix(space)
            M = self.get_mass_matrix(space)
            uh = IM @ uh
            A = A[isFreeDof, :][:, isFreeDof].tocsr()
            M = M[isFreeDof, :][:, isFreeDof].tocsr()

            if self.matlab is False:
                uh[isFreeDof], d = self.eig(A, M)
            else:
                uh[isFreeDof], d = self.meigs(A, M)

            if i < maxit:
                uh = space.function(array=uh)
                eta = self.residual_estimate(uh)
                markedCell = mark(eta, self.theta)
                IM = mesh.bisect(markedCell, returnim=True)
                print(i + 1, "refine: ", mesh.number_of_nodes())

                if (self.step > 0) and (i in idx):
                    NN = mesh.number_of_nodes()
                    fig = plt.figure()
                    fig.set_facecolor('white')
                    if GD == 2:
                        axes = fig.gca()
                    else:
                        axes = Axes3D(fig)
                    mesh.add_plot(axes, cellcolor='w')
                    fig.savefig(self.resultdir + 'mesh_3_1_' + str(i + 1) +
                                '_' + str(NN) + '.pdf')
                    plt.close()
                    self.savemesh(
                        mesh, self.resultdir + 'mesh_3_1_' + str(i + 1) + '_' +
                        str(NN) + '.mat')

                space = LagrangeFiniteElementSpace(mesh, 1)
                isFreeDof = ~(space.boundary_dof())
                gdof = space.number_of_global_dofs()
            if gdof > self.maxdof:
                break

        if self.step > 0:
            NN = mesh.number_of_nodes()
            fig = plt.figure()
            fig.set_facecolor('white')
            if GD == 2:
                axes = fig.gca()
            else:
                axes = Axes3D(fig)
            mesh.add_plot(axes, cellcolor='w')
            fig.savefig(self.resultdir + 'mesh_3_1_' + str(i + 1) + '_' +
                        str(NN) + '.pdf')
            plt.close()
            self.savemesh(
                mesh, self.resultdir + 'mesh_3_1_' + str(i + 1) + '_' +
                str(NN) + '.mat')

        if self.multieigs is True:
            self.A = A
            self.M = M
            self.ml = pyamg.ruge_stuben_solver(self.A)
            self.eigs()

        end = timer()
        print("smallest eigns:", d, "with time: ", end - start)

        uh = space.function(array=uh)
        return uh
Exemple #8
0
def spr_curl(uh):
    """
    Notes
    -----
    给定一个解, 恢复节点处的值
    """
    mesh = uh.space.mesh

    NN = mesh.number_of_nodes()
    NE = mesh.number_of_edges()
    NC = mesh.number_of_cells()

    node = mesh.entity('node')
    edge = mesh.entity('edge')
    cell = mesh.entity('cell')

    # 计算数值解在单元重心处的 curl 值
    bc = np.array([1 / 3, 1 / 3, 1 / 3], dtype=mesh.ftype)
    cellVal = uh.curl_value(bc)  #(NC, )

    # 计算每条边的平均 curl 值
    edge2cell = mesh.ds.edge_to_cell()
    edgeVal = (cellVal[edge2cell[:, 0]] + cellVal[edge2cell[:, 1]]) / 2.0

    # 计算每个节点的最小二乘矩阵
    h = mesh.node_size()
    A, b = spr_edge(mesh, h, edgeVal)

    # 处理边界点
    # 找到每个边界点对应的内部点, 把对应内部点的样本点当做边界节点的样本点

    isBdNode = mesh.ds.boundary_node_flag()
    idxMap = np.arange(NN, dtype=mesh.itype)  # 节点映射数组, 自身到自身的映射

    # 找到一端是边界点, 一端是内部节点的边, 修改节点映射数组
    flag = isBdNode[edge[:, 0]] & (~isBdNode[edge[:, 1]])
    idxMap[edge[flag, 0]] = edge[flag, 1]
    flag = isBdNode[edge[:, 1]] & (~isBdNode[edge[:, 0]])
    idxMap[edge[flag, 1]] = edge[flag, 0]

    # 找到没有内部节点相邻的角点, 修改节点映射数组
    isCEdge = edge2cell[:, 0] != edge2cell[:, 1]
    isCEdge = isCEdge & isBdNode[edge[:, 0]] & isBdNode[edge[:, 1]]

    idxMap[cell[edge2cell[isCEdge, 0],
                edge2cell[isCEdge, 2]]] = cell[edge2cell[isCEdge, 1],
                                               edge2cell[isCEdge, 3]]
    idxMap[cell[edge2cell[isCEdge, 1],
                edge2cell[isCEdge, 3]]] = cell[edge2cell[isCEdge, 0],
                                               edge2cell[isCEdge, 2]]

    # 计算边界节点对应的最小二乘矩阵和右端
    # 注意可以直接利用对应内部节点对应的最小二乘矩阵和右端来计算边界点的系统,
    # 它们有内在的数学关系
    c = h[idxMap[isBdNode]] / h[isBdNode]
    xe = (node[idxMap[isBdNode]] - node[isBdNode]) / h[isBdNode, None]

    A[isBdNode, 0, 0] = A[idxMap[isBdNode], 0, 0]

    A[isBdNode, 0, 1] = A[idxMap[isBdNode], 0, 0] * xe[:, 0]
    A[isBdNode, 0, 1] += A[idxMap[isBdNode], 0, 1] * c
    A[isBdNode, 1, 0] = A[isBdNode, 0, 1]

    A[isBdNode, 0, 2] = A[idxMap[isBdNode], 0, 0] * xe[:, 1]
    A[isBdNode, 0, 2] += A[idxMap[isBdNode], 0, 2] * c
    A[isBdNode, 2, 0] = A[isBdNode, 0, 2]

    A[isBdNode, 1, 1] = A[idxMap[isBdNode], 0, 0] * xe[:, 0]**2
    A[isBdNode, 1, 1] += A[idxMap[isBdNode], 0, 1] * xe[:, 0] * 2 * c
    A[isBdNode, 1, 1] += A[idxMap[isBdNode], 1, 1] * c**2

    A[isBdNode, 1, 2] = A[idxMap[isBdNode], 0, 0] * xe[:, 0] * xe[:, 1]
    A[isBdNode, 1, 2] += A[idxMap[isBdNode], 0, 1] * xe[:, 1] * c
    A[isBdNode, 1, 2] += A[idxMap[isBdNode], 0, 2] * xe[:, 0] * c
    A[isBdNode, 1, 2] += A[idxMap[isBdNode], 1, 2] * c**2
    A[isBdNode, 2, 1] = A[isBdNode, 1, 2]

    A[isBdNode, 2, 2] = A[idxMap[isBdNode], 0, 0] * xe[:, 1]**2
    A[isBdNode, 2, 2] += A[idxMap[isBdNode], 0, 2] * xe[:, 1] * 2 * c
    A[isBdNode, 2, 2] += A[idxMap[isBdNode], 2, 2] * c**2

    b[isBdNode, 0] = b[idxMap[isBdNode], 0]

    b[isBdNode, 1] = b[idxMap[isBdNode], 0] * xe[:, 0]
    b[isBdNode, 1] += b[idxMap[isBdNode], 1] * c

    b[isBdNode, 2] = b[idxMap[isBdNode], 0] * xe[:, 1]
    b[isBdNode, 2] += b[idxMap[isBdNode], 2] * c

    A = inv(A)
    val = (A @ b[:, :, None]).reshape(-1, 3)

    space = LagrangeFiniteElementSpace(mesh, p=1)
    ruh = space.function()  # (gdof, 2)
    ruh[:] = val[:, 0]

    return ruh
                             tol=1e-8,
                             callback=counter)
        print("Convergence info:", info)
        print("Number of iteration of pcg:", counter.niter)

        return uh


n = int(sys.argv[1])

pde = BoxDomainData3d()
mesh = pde.init_mesh(n=n)

space = LagrangeFiniteElementSpace(mesh, p=1)
bc = DirichletBC(space, pde.dirichlet, threshold=pde.is_dirichlet_boundary)
uh = space.function(dim=3)
A = space.linear_elasticity_matrix(pde.lam, pde.mu, q=1)
F = space.source_vector(pde.source, dim=3)
A, F = bc.apply(A, F, uh)

if False:
    uh.T.flat[:] = spsolve(A, F)
elif False:
    N = len(F)
    print(N)
    start = timer()
    ilu = spilu(A.tocsc(), drop_tol=1e-6, fill_factor=40)
    end = timer()
    print('time:', end - start)

    M = LinearOperator((N, N), lambda x: ilu.solve(x))
Exemple #10
0
]
errorMatrix = np.zeros((2, maxit), dtype=np.float)
NDof = np.zeros(maxit, dtype=np.float)

# 初始网格
fig = plt.figure()
axes = fig.gca()
mesh.add_plot(axes)

for i in range(maxit):
    space = LagrangeFiniteElementSpace(mesh, p=p)  # 建立有限元空间

    NDof[i] = space.number_of_global_dofs()  # 有限元空间自由度的个数
    bc = DirichletBC(space, pde.dirichlet)  # DirichletBC 条件

    uh = space.function()  # 有限元函数
    A = space.stiff_matrix()  # 刚度矩阵
    F = space.source_vector(pde.source)  # 载荷向量

    A, F = bc.apply(A, F, uh)  # 处理边界条件

    uh[:] = spsolve(A, F).reshape(-1)  # 稀疏矩阵直接解法器

    #ml = pyamg.ruge_stuben_solver(A)  # 代数多重网格解法器
    #uh[:] = ml.solve(F, tol=1e-12, accel='cg').reshape(-1)

    errorMatrix[0, i] = space.integralalg.L2_error(pde.solution,
                                                   uh)  # 计算 L2 误差
    errorMatrix[1, i] = space.integralalg.L2_error(pde.gradient,
                                                   uh.grad_value)  # 计算 H1 误差
Exemple #11
0
    space = LagrangeFiniteElementSpace(mesh, p=p)

    pBS = periodicBoundarySettings(mesh, space.dof, set_periodic_edge_func)
    DirEdgeInd = pBS.idxNotPeriodicEdge
    periodicDof0, periodicDof1, dirDof = pBS.set_boundaryDofs()

    # |--- test
    # pdof = np.concatenate([periodicDof0, periodicDof1])
    # print('pdof = ', pdof)
    # bddof, = np.nonzero(space.boundary_dof())
    # print('diff_pdof = ', np.setdiff1d(bddof, pdof))

    NDof[i] = space.number_of_global_dofs()
    bc = DirichletBC(space, pde.dirichlet, threshold=DirEdgeInd)

    uh = space.function()
    A = space.stiff_matrix()

    F = space.source_vector(pde.source)

    A, F = bc.apply(A, F, uh)

    F, A = pBS.set_periodicAlgebraicSystem(periodicDof0,
                                           periodicDof1,
                                           F,
                                           lhsM=A)

    uh[:] = spsolve(A, F).reshape(-1)

    errorMatrix[0, i] = space.integralalg.L2_error(pde.solution, uh)
    errorMatrix[1, i] = space.integralalg.L2_error(pde.gradient, uh.grad_value)
Exemple #12
0
class FEMCahnHilliardModel2d:
    def __init__(self, pde, mesh, p, dt):
        self.pde = pde
        self.p = p
        self.mesh = mesh
        self.timemesh, self.dt = self.pde.time_mesh(dt)
        self.itype = self.mesh.itype
        self.ftype = self.mesh.ftype
        self.pde = pde
        self.space = LagrangeFiniteElementSpace(mesh, p)
        self.dof = self.space.dof
        self.cellmeasure = mesh.entity_measure('cell')
        self.integralalg = FEMeshIntegralAlg(self.mesh,
                                             p + 4,
                                             cellmeasure=self.cellmeasure)
        self.uh = self.space.function()
        self.wh = self.space.function()
        self.StiffMatrix = self.space.stiff_matrix()
        self.MassMatrix = self.space.mass_matrix()

    def setCoefficient_T1stOrder(self, dt_minimum=None):
        pde = self.pde
        dt_min = self.dt if dt_minimum is None else dt_minimum
        m = pde.m
        epsilon = pde.epsilon

        s = np.sqrt(4 * epsilon / (m * dt_min))
        alpha = 1. / (2 * epsilon) * (
            -s + np.sqrt(abs(s**2 - 4 * epsilon / (m * self.dt))))
        return s, alpha

    def setCoefficient_T2ndOrder(self, dt_minimum=None):
        pde = self.pde
        dt_min = self.dt if dt_minimum is None else dt_minimum
        m = pde.m
        epsilon = pde.epsilon

        s = np.sqrt(4 * (3 / 2) * epsilon / (m * dt_min))
        alpha = 1. / (2 * epsilon) * (
            -s + np.sqrt(abs(s**2 - 4 * (3 / 2) * epsilon / (m * self.dt))))
        return s, alpha

    def uh_grad_value_at_faces(self, vh, f_bcs, cellidx, localidx):
        cell2dof = self.dof.cell2dof
        f_gphi = self.space.edge_grad_basis(f_bcs, cellidx,
                                            localidx)  # (NE,NQ,cldof,GD)
        val = np.einsum('ik, ijkm->jim', vh[cell2dof[cellidx]],
                        f_gphi)  # (NQ,NE,GD)
        return val

    def grad_free_energy_at_faces(self, uh, f_bcs, idxBdEdge, cellidx,
                                  localidx):
        """
        1. Compute the grad of free energy at FACE Gauss-integration points (barycentric coordinates).
        2. In this function, the free energy has NO coefficients.
        -------
        :param uh:
        :param f_bcs: f_bcs.shape: (NQ,(GD-1)+1)
        :return:
        """

        uh_val = self.space.value(uh, f_bcs)[..., idxBdEdge]  # (NQ,NBE)
        guh_val = self.uh_grad_value_at_faces(uh, f_bcs, cellidx,
                                              localidx)  # (NQ,NBE,GD)

        guh_val[..., 0] = 3 * uh_val**2 * guh_val[..., 0] - guh_val[..., 0]
        guh_val[..., 1] = 3 * uh_val**2 * guh_val[..., 1] - guh_val[..., 1]
        return guh_val  # (NQ,NBE,2)

    def grad_free_energy_at_cells(self, uh, c_bcs):
        """
        1. Compute the grad of free energy at CELL Gauss-integration points (barycentric coordinates).
        2. In this function, the free energy has NO coefficients.
        -------
        :param uh:
        :param c_bcs: c_bcs.shape: (NQ,GD+1)
        :return:
        """

        uh_val = self.space.value(uh, c_bcs)  # (NQ,NC)
        guh_val = self.space.grad_value(uh, c_bcs)  # (NQ,NC,2)

        guh_val[..., 0] = 3 * uh_val**2 * guh_val[..., 0] - guh_val[..., 0]
        guh_val[..., 1] = 3 * uh_val**2 * guh_val[..., 1] - guh_val[..., 1]
        return guh_val  # (NQ,NC,2)

    @timer
    def CH_Solver_T1stOrder(self):
        pde = self.pde
        timemesh = self.timemesh
        NT = len(timemesh)
        dt = self.dt
        uh = self.uh
        wh = self.wh
        sm = self.StiffMatrix
        mm = self.MassMatrix
        space = self.space
        dof = self.dof
        face2dof = dof.face_to_dof()  # (NE,fldof)
        cell2dof = dof.cell_to_dof()  # (NC,cldof)

        dt_min = pde.dt_min if hasattr(pde, 'dt_min') else dt
        s, alpha = self.setCoefficient_T1stOrder(dt_minimum=dt_min)
        m = pde.m
        epsilon = pde.epsilon
        eta = pde.eta

        print('    # #################################### #')
        print('      Time 1st-order scheme')
        print('    # #################################### #')

        print('    # ------------ parameters ------------ #')
        print(
            '    s = %.4e,  alpha = %.4e,  m = %.4e,  epsilon = %.4e,  eta = %.4e'
            % (s, alpha, m, epsilon, eta))
        print('    t0 = %.4e,  T = %.4e, dt = %.4e' %
              (timemesh[0], timemesh[-1], dt))
        print(' ')

        idxNeuEdge = self.set_Neumann_edge()
        nBd = self.mesh.face_unit_normal(index=idxNeuEdge)  # (NBE,2)
        NeuCellIdx = self.mesh.ds.edge2cell[idxNeuEdge, 0]
        NeuLocalIdx = self.mesh.ds.edge2cell[idxNeuEdge, 2]
        neu_face_measure = self.mesh.entity_measure(
            'face', index=idxNeuEdge)  # (Nneu,2)
        cell_measure = self.mesh.cell_area()

        f_q = self.integralalg.faceintegrator
        f_bcs, f_ws = f_q.get_quadrature_points_and_weights(
        )  # f_bcs.shape: (NQ,(GD-1)+1)
        f_pp = self.mesh.bc_to_point(
            f_bcs, index=idxNeuEdge
        )  # f_pp.shape: (NQ,NBE,GD) the physical Gauss points
        c_q = self.integralalg.cellintegrator
        c_bcs, c_ws = c_q.get_quadrature_points_and_weights(
        )  # c_bcs.shape: (NQ,GD+1)
        c_pp = self.mesh.bc_to_point(
            c_bcs)  # c_pp.shape: (NQ_cell,NC,GD) the physical Gauss points

        phi_f = space.face_basis(
            f_bcs)  # (NQ,1,fldof). 实际上这里可以直接用 pspace.basis(f_bcs), 两个函数的代码是相同的
        phi_c = space.basis(c_bcs)  # (NQ,NC,clodf)
        gphi_c = space.grad_basis(c_bcs)  # (NQ,NC,cldof,GD)

        # # time-looping
        print('    # ------------ begin the time-looping ------------ #')
        for nt in range(NT - 1):
            currt_t = timemesh[nt]
            next_t = currt_t + dt

            if nt % max([int(NT / 10), 1]) == 0:
                print('    currt_t = %.4e' % currt_t)
            if nt == 0:
                # the initial value setting
                u_c = pde.solution(c_pp, pde.t0)  # (NQC,NC)
                gu_c = pde.gradient(c_pp, pde.t0)  # (NQC,NC,2)
                u_f = pde.solution(f_pp, pde.t0)  # (NQF,NBE)
                gu_f = pde.gradient(f_pp, pde.t0)  # (NQF,NBE,2)

                grad_free_energy_c = epsilon / eta**2 * (
                    3 * np.repeat(u_c[..., np.newaxis], 2, axis=2)**2 * gu_c -
                    gu_c)
                grad_free_energy_f = epsilon / eta**2 * (
                    3 * np.repeat(u_f[..., np.newaxis], 2, axis=2)**2 * gu_f -
                    gu_f)

                uh_val = u_c  # (NQ,NC)
                guh_val_c = gu_c  # (NQ,NC,2)
                guh_val_f = gu_f  # (NQ,NE,2)

                # gu_c_test = gu_c.copy()
                # gu_c_test[..., 0] = epsilon / eta ** 2 * (3 * u_c ** 2 * gu_c_test[..., 0] - gu_c_test[..., 0])
                # gu_c_test[..., 1] = epsilon / eta ** 2 * (3 * u_c ** 2 * gu_c_test[..., 1] - gu_c_test[..., 1])
                # gu_f_test = gu_f.copy()
                # gu_f_test[..., 0] = epsilon / eta ** 2 * (3 * u_f ** 2 * gu_f_test[..., 0] - gu_f_test[..., 0])
                # gu_f_test[..., 1] = epsilon / eta ** 2 * (3 * u_f ** 2 * gu_f_test[..., 1] - gu_f_test[..., 1])
                # print(np.allclose(gu_c_test, grad_free_energy_c))
                # print(np.allclose(gu_f_test, grad_free_energy_f))
            else:
                grad_free_energy_c = epsilon / eta**2 * self.grad_free_energy_at_cells(
                    uh, c_bcs)  # (NQ,NC,2)
                grad_free_energy_f = epsilon / eta**2 * self.grad_free_energy_at_faces(
                    uh, f_bcs, idxNeuEdge, NeuCellIdx,
                    NeuLocalIdx)  # (NQ,NE,2)
                uh_val = space.value(uh, c_bcs)  # (NQ,NC)
                guh_val_c = space.grad_value(uh, c_bcs)  # (NQ,NC,2)
                guh_val_f = self.uh_grad_value_at_faces(
                    uh, f_bcs, NeuCellIdx, NeuLocalIdx)  # (NQ,NE,2)

            # # # ---------------------------------------- yc test --------------------------------------------------- # #
            # if nt == 0:
            #     def init_solution(p):
            #         return pde.solution(p, 0)
            #     uh[:] = space.interpolation(init_solution)
            # grad_free_energy_c = epsilon / eta ** 2 * self.grad_free_energy_at_cells(uh, c_bcs)  # (NQ,NC,2)
            # grad_free_energy_f = epsilon / eta ** 2 * self.grad_free_energy_at_faces(uh, f_bcs, idxNeuEdge, NeuCellIdx,
            #                                                                          NeuLocalIdx)  # (NQ,NE,2)
            # uh_val = space.value(uh, c_bcs)  # (NQ,NC)
            # guh_val_c = space.grad_value(uh, c_bcs)  # (NQ,NC,2)
            # guh_val_f = self.uh_grad_value_at_faces(uh, f_bcs, NeuCellIdx, NeuLocalIdx)  # (NQ,NE,2)
            # # # --------------------------------------- end test ---------------------------------------------------- # #

            Neumann = pde.neumann(f_pp, next_t, nBd)  # (NQ,NE)
            LaplaceNeumann = pde.laplace_neumann(f_pp, next_t, nBd)  # (NQ,NE)
            f_val = pde.source(c_pp, next_t, m, epsilon, eta)  # (NQ,NC)

            # # get the auxiliary equation Right-hand-side-Vector
            aux_rv = np.zeros((dof.number_of_global_dofs(), ),
                              dtype=self.ftype)  # (Ndof,)

            # # aux_rhs_c_0:  -1. / (epsilon * m * dt) * (uh^n,phi)_\Omega
            aux_rhs_c_0 = -1. / (epsilon * m) * (
                1 / dt * np.einsum('i, ij, ijk, j->jk', c_ws, uh_val, phi_c,
                                   cell_measure) +
                np.einsum('i, ij, ijk, j->jk', c_ws, f_val, phi_c,
                          cell_measure))  # (NC,cldof)
            # # aux_rhs_c_1: -s / epsilon * (\nabla uh^n, \nabla phi)_\Omega
            aux_rhs_c_1 = -s / epsilon * (
                np.einsum('i, ij, ijk, j->jk', c_ws, guh_val_c[..., 0],
                          gphi_c[..., 0], cell_measure) +
                np.einsum('i, ij, ijk, j->jk', c_ws, guh_val_c[..., 1],
                          gphi_c[..., 1], cell_measure))  # (NC,cldof)
            # # aux_rhs_c_2: 1 / epsilon * (\nabla h(uh^n), \nabla phi)_\Omega
            aux_rhs_c_2 = 1. / epsilon * (np.einsum(
                'i, ij, ijk, j->jk', c_ws, grad_free_energy_c[..., 0],
                gphi_c[..., 0], cell_measure) + np.einsum(
                    'i, ij, ijk, j->jk', c_ws, grad_free_energy_c[..., 1],
                    gphi_c[..., 1], cell_measure))  # (NC,cldof)

            # # aux_rhs_f_0: (\nabla wh^{n+1}\cdot n, phi)_\Gamma, wh is the solution of auxiliary equation
            aux_rhs_f_0 = alpha * np.einsum('i, ij, ijn, j->jn', f_ws, Neumann, phi_f, neu_face_measure) \
                          + np.einsum('i, ij, ijn, j->jn', f_ws, LaplaceNeumann, phi_f, neu_face_measure)  # (Nneu,fldof)
            # # aux_rhs_f_1: s / epsilon * (\nabla uh^n \cdot n, phi)_\Gamma
            aux_rhs_f_1 = s / epsilon * np.einsum(
                'i, ijk, jk, ijn, j->jn', f_ws, guh_val_f, nBd, phi_f,
                neu_face_measure)  # (Nneu,fldof)
            # # aux_rhs_f_2: -1 / epsilon * (\nabla h(uh^n) \cdot n, phi)_\Gamma
            aux_rhs_f_2 = -1. / epsilon * np.einsum(
                'i, ijk, jk, ijn, j->jn', f_ws, grad_free_energy_f, nBd, phi_f,
                neu_face_measure)  # (Nneu,fldof)

            np.add.at(aux_rv, cell2dof,
                      aux_rhs_c_0 + aux_rhs_c_1 + aux_rhs_c_2)
            np.add.at(aux_rv, face2dof[idxNeuEdge, :],
                      aux_rhs_f_0 + aux_rhs_f_1 + aux_rhs_f_2)

            # # update the solution of auxiliary equation
            wh[:] = spsolve(sm + (alpha + s / epsilon) * mm, aux_rv)

            # # update the original solution
            orig_rv = np.zeros((dof.number_of_global_dofs(), ),
                               dtype=self.ftype)  # (Ndof,)
            wh_val = space.value(wh, c_bcs)  # (NQ,NC)
            orig_rhs_c = -np.einsum('i, ij, ijk, j->jk', c_ws, wh_val, phi_c,
                                    cell_measure)  # (NC,cldof)
            orig_rhs_f = np.einsum('i, ij, ijn, j->jn', f_ws, Neumann, phi_f,
                                   neu_face_measure)
            np.add.at(orig_rv, cell2dof, orig_rhs_c)
            np.add.at(orig_rv, face2dof[idxNeuEdge, :], orig_rhs_f)
            uh[:] = spsolve(sm - alpha * mm, orig_rv)
            # currt_l2err = self.currt_error(uh, next_t)
            print('max(uh) = ', max(uh))
            # if max(uh[:]) > 1e5:
            #     break
        print('    # ------------ end the time-looping ------------ #\n')

        l2err, h1err = self.currt_error(uh, timemesh[-1])
        print('    # ------------ the last errors ------------ #')
        print('    l2err = %.4e, h1err = %.4e' % (l2err, h1err))
        return l2err, h1err

    def CH_Solver_T2ndOrder(self):
        pde = self.pde
        timemesh = self.timemesh
        NT = len(timemesh)
        space = self.space
        dt = self.dt
        uh = self.uh
        last_uh = space.function()
        wh = self.wh
        sm = self.StiffMatrix
        mm = self.MassMatrix
        dof = self.dof
        face2dof = dof.face_to_dof()  # (NE,fldof)
        cell2dof = dof.cell_to_dof()  # (NC,cldof)

        dt_min = pde.dt_min if hasattr(pde, 'dt_min') else dt
        s, alpha = self.setCoefficient_T2ndOrder(dt_minimum=dt_min)
        m = pde.m
        epsilon = pde.epsilon
        eta = pde.eta

        print('    # #################################### #')
        print('      Time 2nd-order scheme')
        print('    # #################################### #')

        print('    # ------------ parameters ------------ #')
        print(
            '    s = %.4e,  alpha = %.4e,  m = %.4e,  epsilon = %.4e,  eta = %.4e'
            % (s, alpha, m, epsilon, eta))
        print('    t0 = %.4e,  T = %.4e, dt = %.4e' %
              (timemesh[0], timemesh[-1], dt))
        print(' ')

        idxNeuEdge = self.set_Neumann_edge()
        nBd = self.mesh.face_unit_normal(index=idxNeuEdge)  # (NBE,2)
        NeuCellIdx = self.mesh.ds.edge2cell[idxNeuEdge, 0]
        NeuLocalIdx = self.mesh.ds.edge2cell[idxNeuEdge, 2]
        neu_face_measure = self.mesh.entity_measure(
            'face', index=idxNeuEdge)  # (Nneu,2)
        cell_measure = self.mesh.cell_area()

        f_q = self.integralalg.faceintegrator
        f_bcs, f_ws = f_q.get_quadrature_points_and_weights(
        )  # f_bcs.shape: (NQ,(GD-1)+1)
        f_pp = self.mesh.bc_to_point(
            f_bcs, index=idxNeuEdge
        )  # f_pp.shape: (NQ,NBE,GD) the physical Gauss points
        c_q = self.integralalg.cellintegrator
        c_bcs, c_ws = c_q.get_quadrature_points_and_weights(
        )  # c_bcs.shape: (NQ,GD+1)
        c_pp = self.mesh.bc_to_point(
            c_bcs)  # c_pp.shape: (NQ_cell,NC,GD) the physical Gauss points

        phi_f = space.face_basis(
            f_bcs)  # (NQ,1,fldof). 实际上这里可以直接用 pspace.basis(f_bcs), 两个函数的代码是相同的
        phi_c = space.basis(c_bcs)  # (NQ,NC,clodf)
        gphi_c = space.grad_basis(c_bcs)  # (NQ,NC,cldof,GD)

        # # time-looping
        print('    # ------------ begin the time-looping ------------ #')

        def init_solution(p):
            return pde.solution(p, 0)

        last_uh[:] = space.interpolation(init_solution)
        for nt in range(NT - 1):
            currt_t = timemesh[nt]
            next_t = currt_t + dt
            if nt % max([int(NT / 10), 1]) == 0:
                print('    currt_t = %.4e' % currt_t)
            if nt == 0:
                # the initial value setting
                s, alpha = self.setCoefficient_T1stOrder(dt_minimum=dt_min)

                u_c = pde.solution(c_pp, pde.t0)  # (NQC,NC)
                gu_c = pde.gradient(c_pp, pde.t0)  # (NQC,NC,2)
                u_f = pde.solution(f_pp, pde.t0)  # (NQF,NBE)
                gu_f = pde.gradient(f_pp, pde.t0)  # (NQF,NBE,2)

                grad_free_energy_c = epsilon / eta**2 * (
                    3 * np.repeat(u_c[..., np.newaxis], 2, axis=2)**2 * gu_c -
                    gu_c)
                grad_free_energy_f = epsilon / eta**2 * (
                    3 * np.repeat(u_f[..., np.newaxis], 2, axis=2)**2 * gu_f -
                    gu_f)

                uh_val = u_c  # (NQ,NC)
                guh_val_c = gu_c  # (NQ,NC,2)
                guh_val_f = gu_f  # (NQ,NE,2)
            else:
                s, alpha = self.setCoefficient_T2ndOrder(dt_minimum=dt_min)
                grad_free_energy_c = epsilon / eta**2 * self.grad_free_energy_at_cells(
                    2 * uh - last_uh, c_bcs)  # (NQ,NC,2)
                grad_free_energy_f = epsilon / eta**2 * self.grad_free_energy_at_faces(
                    2 * uh - last_uh, f_bcs, idxNeuEdge, NeuCellIdx,
                    NeuLocalIdx)  # (NQ,NE,2)
                uh_val = space.value(2 * uh - 1 / 2 * last_uh,
                                     c_bcs)  # (NQ,NC)
                guh_val_c = space.grad_value(2 * uh - last_uh,
                                             c_bcs)  # (NQ,NC,2)
                guh_val_f = self.uh_grad_value_at_faces(
                    2 * uh - last_uh, f_bcs, NeuCellIdx,
                    NeuLocalIdx)  # (NQ,NE,2)

                last_uh[:] = uh[:]  # update the last_uh

            Neumann = pde.neumann(f_pp, next_t, nBd)  # (NQ,NE)
            LaplaceNeumann = pde.laplace_neumann(f_pp, next_t, nBd)  # (NQ,NE)
            f_val = pde.source(c_pp, next_t, m, epsilon, eta)  # (NQ,NC)

            # # get the auxiliary equation Right-hand-side-Vector
            aux_rv = np.zeros((dof.number_of_global_dofs(), ),
                              dtype=self.ftype)  # (Ndof,)
            aux_rv_temp = np.zeros((dof.number_of_global_dofs(), ),
                                   dtype=self.ftype)

            # # aux_rhs_c_0:  -1. / (epsilon * m * dt) * (uh^n,phi)_\Omega
            aux_rhs_c_0 = -1. / (epsilon * m) * (
                1 / dt * np.einsum('i, ij, ijk, j->jk', c_ws, uh_val, phi_c,
                                   cell_measure) +
                np.einsum('i, ij, ijk, j->jk', c_ws, f_val, phi_c,
                          cell_measure))  # (NC,cldof)
            # # aux_rhs_c_1: -s / epsilon * (\nabla uh^n, \nabla phi)_\Omega
            aux_rhs_c_1 = -s / epsilon * (
                np.einsum('i, ij, ijk, j->jk', c_ws, guh_val_c[..., 0],
                          gphi_c[..., 0], cell_measure) +
                np.einsum('i, ij, ijk, j->jk', c_ws, guh_val_c[..., 1],
                          gphi_c[..., 1], cell_measure))  # (NC,cldof)
            # # aux_rhs_c_2: 1 / epsilon * (\nabla h(uh^n), \nabla phi)_\Omega
            aux_rhs_c_2 = 1. / epsilon * (np.einsum(
                'i, ij, ijk, j->jk', c_ws, grad_free_energy_c[..., 0],
                gphi_c[..., 0], cell_measure) + np.einsum(
                    'i, ij, ijk, j->jk', c_ws, grad_free_energy_c[..., 1],
                    gphi_c[..., 1], cell_measure))  # (NC,cldof)

            # # aux_rhs_f_0: (\nabla wh^{n+1}\cdot n, phi)_\Gamma, wh is the solution of auxiliary equation
            aux_rhs_f_0 = alpha * np.einsum('i, ij, ijn, j->jn', f_ws, Neumann, phi_f, neu_face_measure) \
                          + np.einsum('i, ij, ijn, j->jn', f_ws, LaplaceNeumann, phi_f, neu_face_measure)  # (Nneu,fldof)
            # # aux_rhs_f_1: s / epsilon * (\nabla uh^n \cdot n, phi)_\Gamma
            aux_rhs_f_1 = s / epsilon * np.einsum(
                'i, ijk, jk, ijn, j->jn', f_ws, guh_val_f, nBd, phi_f,
                neu_face_measure)  # (Nneu,fldof)
            # # aux_rhs_f_2: -1 / epsilon * (\nabla h(uh^n) \cdot n, phi)_\Gamma
            aux_rhs_f_2 = -1. / epsilon * np.einsum(
                'i, ijk, jk, ijn, j->jn', f_ws, grad_free_energy_f, nBd, phi_f,
                neu_face_measure)  # (Nneu,fldof)

            np.add.at(aux_rv_temp, cell2dof, aux_rhs_c_0)
            np.add.at(aux_rv, cell2dof,
                      aux_rhs_c_0 + aux_rhs_c_1 + aux_rhs_c_2)
            np.add.at(aux_rv, face2dof[idxNeuEdge, :],
                      aux_rhs_f_0 + aux_rhs_f_1 + aux_rhs_f_2)

            # # update the solution of auxiliary equation
            wh[:] = spsolve(sm + (alpha + s / epsilon) * mm, aux_rv)

            # # update the original solution
            orig_rv = np.zeros((dof.number_of_global_dofs(), ),
                               dtype=self.ftype)  # (Ndof,)
            wh_val = space.value(wh, c_bcs)  # (NQ,NC)
            orig_rhs_c = -np.einsum('i, ij, ijk, j->jk', c_ws, wh_val, phi_c,
                                    cell_measure)  # (NC,cldof)
            orig_rhs_f = np.einsum('i, ij, ijn, j->jn', f_ws, Neumann, phi_f,
                                   neu_face_measure)
            np.add.at(orig_rv, cell2dof, orig_rhs_c)
            np.add.at(orig_rv, face2dof[idxNeuEdge, :], orig_rhs_f)
            uh[:] = spsolve(sm - alpha * mm, orig_rv)
        print('    # ------------ end the time-looping ------------ #\n')

        l2err, h1err = self.currt_error(uh, timemesh[-1])
        print('    # ------------ the last errors ------------ #')
        print('    l2err = %.4e, h1err = %.4e' % (l2err, h1err))
        return l2err, h1err

    def currt_error(self, uh, t):
        pde = self.pde

        def currt_solution(p):
            return pde.solution(p, t)

        l2err = self.space.integralalg.L2_error(currt_solution, uh)

        def currt_grad_solution(p):
            return pde.gradient(p, t)

        h1err = self.space.integralalg.L2_error(currt_grad_solution,
                                                uh.grad_value)

        return l2err, h1err

    def set_Dirichlet_edge(self, idxDirEdge=None):
        if idxDirEdge is not None:
            return idxDirEdge

        mesh = self.mesh
        edge2cell = mesh.ds.edge_to_cell()
        isBdEdge = (edge2cell[:, 0] == edge2cell[:, 1]
                    )  # (NE,), the bool vars, to get the boundary edges

        isDirEdge = isBdEdge  # here, we set all the boundary edges are Dir edges
        idxDirEdge, = np.nonzero(isDirEdge)  # (NE_Dir,)

        return idxDirEdge

    def set_Neumann_edge(self, idxNeuEdge=None):
        if idxNeuEdge is not None:
            return idxNeuEdge

        mesh = self.mesh
        edge2cell = mesh.ds.edge_to_cell()
        bdEdge = (edge2cell[:, 0] == edge2cell[:, 1]
                  )  # the bool vars, to get the boundary edges

        isNeuEdge = bdEdge  # here, we first set all the boundary edges are Neu edges
        idxNeuEdge, = np.nonzero(isNeuEdge)  # (NE_Dir,)
        return idxNeuEdge
p = int(sys.argv[2])
scale = float(sys.argv[3])

pde = BoxDomain2DData()

mesh = pde.init_mesh(n=n)

area = mesh.entity_measure('cell')
print(area[0])

space = LagrangeFiniteElementSpace(mesh, p=p)

bc0 = DirichletBC(space, pde.dirichlet, threshold=pde.is_dirichlet_boundary)
bc1 = NeumannBC(space, pde.neumann, threshold=pde.is_neumann_boundary)

uh = space.function(dim=2)  # (gdof, 2) and vector fem function uh[i, j]
A = space.linear_elasticity_matrix(pde.mu, pde.lam)  # (2*gdof, 2*gdof)
F = space.source_vector(pde.source, dim=2)
F = bc1.apply(F)
A, F = bc0.apply(A, F, uh)

N = len(F)
print(N)

if False:
    uh.T.flat[:] = spsolve(A, F)  # (2, gdof ).flat
else:
    ilu = spilu(A.tocsc(), drop_tol=1e-6, fill_factor=40)
    M = LinearOperator((N, N), lambda x: ilu.solve(x))
    start = timer()
    uh.T.flat[:], info = cg(A, F, tol=1e-8, M=M)  # solve with CG
Exemple #14
0
class FEMNavierStokesModel2d:
    def __init__(self, pde, mesh, p, dt, T):
        self.p = p
        self.mesh = mesh
        self.dt = dt
        self.T = T
        self.itype = self.mesh.itype
        self.ftype = self.mesh.ftype
        self.pde = pde
        self.vspace = LagrangeFiniteElementSpace(mesh, p+1)
        self.pspace = LagrangeFiniteElementSpace(mesh, p)
        self.vdof = self.vspace.dof
        self.pdof = self.pspace.dof
        self.cellmeasure = mesh.entity_measure('cell')
        self.integralalg = FEMeshIntegralAlg(self.mesh, p+4, cellmeasure=self.cellmeasure)
        self.uh0 = self.vspace.function()
        self.uh1 = self.vspace.function()
        self.ph = self.pspace.function()

    @timer
    def NS_VC_Solver(self):
        """
        The Navier-Stokes Velocity-Correction scheme solver.
        """
        pde = self.pde
        dt = self.dt
        uh0 = self.uh0
        uh1 = self.uh1
        ph = self.ph
        vspace = self.vspace
        pspace = self.pspace
        vdof = self.vdof
        pdof = self.pdof
        pface2dof = pdof.face_to_dof()  # (NE,fldof)
        pcell2dof = pdof.cell_to_dof()  # (NC,cldof)
        ucell2dof = vspace.cell_to_dof()  # (NC,cldof)
        vface2dof = vdof.face_to_dof()  # (NE,fldof)

        idxDirEdge = self.set_Dirichlet_edge()
        cellidxDir = self.mesh.ds.edge2cell[idxDirEdge, 0]
        localidxDir = self.mesh.ds.edge2cell[idxDirEdge, 2]

        Dir_face2dof = pface2dof[idxDirEdge, :]  # (NDir,flodf)
        Dir_cell2dof = pcell2dof[cellidxDir, :]  # (NDir,cldof)
        n_Dir = self.mesh.face_unit_normal(index=idxDirEdge)  # (NDir,2)
        dir_face_measure = self.mesh.entity_measure('face', index=idxDirEdge)  # (NDir,2)
        cell_measure = self.mesh.cell_area()
        dir_cell_measure = self.mesh.cell_area(cellidxDir)

        idxNeuEdge = self.mesh.ds.boundary_face_index()  # here Neumann edges are all the boundary
        n_Neu = self.mesh.face_unit_normal(index=idxNeuEdge)  # (NNeu,2)
        neu_face_measure = self.mesh.entity_measure('face', index=idxNeuEdge)  # (NNeu,2)
        Neu_face2dof = vface2dof[idxNeuEdge, :]  # (NNeu,flodf)

        f_q = self.integralalg.faceintegrator
        f_bcs, f_ws = f_q.get_quadrature_points_and_weights()  # f_bcs.shape: (NQ,(GD-1)+1)
        f_pp = self.mesh.bc_to_point(f_bcs, index=idxDirEdge)  # f_pp.shape: (NQ,NDir,GD) the physical Gauss points
        f_pp_Neu = self.mesh.bc_to_point(f_bcs, index=idxNeuEdge)  # f_pp.shape: (NQ,NNeu,GD) the physical Gauss points

        c_q = self.integralalg.cellintegrator
        c_bcs, c_ws = c_q.get_quadrature_points_and_weights()  # c_bcs.shape: (NQ,GD+1)
        c_pp = self.mesh.bc_to_point(c_bcs)  # c_pp.shape: (NQ_cell,NC,GD) the physical Gauss points

        # last_uh0 = vspace.function()
        # last_uh1 = vspace.function()
        # next_ph = pspace.function()

        # # t^{n+1}: Pressure-Left-StiffMatrix
        plsm = self.pspace.stiff_matrix()
        basis_int = pspace.integral_basis()
        p_phi = pspace.face_basis(f_bcs)  # (NQ,1,fldof). 实际上这里可以直接用 pspace.basis(f_bcs), 两个函数的代码是相同的
        p_gphi_f = pspace.edge_grad_basis(f_bcs, cellidxDir, localidxDir)  # (NDir,NQ,cldof,GD)
        p_gphi_c = pspace.grad_basis(c_bcs)  # (NQ_cell,NC,ldof,GD)

        # # t^{n+1}: Velocity-Left-MassMatrix and -StiffMatrix
        ulmm = self.vspace.mass_matrix()
        ulsm = self.vspace.stiff_matrix()
        u_phi = vspace.basis(c_bcs)  # (NQ,1,cldof)
        u_phi_f = vspace.face_basis(f_bcs)  # (NQ,1,fldof) 实际上这里可以直接用 vspace.basis(f_bcs), 两个函数的代码是相同的

        def init_velocity0(p):
            return pde.velocity(p, 0)[..., 0]
        uh0[:] = vspace.interpolation(init_velocity0)

        def init_velocity1(p):
            return pde.velocity(p, 0)[..., 1]
        uh1[:] = vspace.interpolation(init_velocity1)

        next_t = 0
        for nt in range(int(self.T/dt)):
            curr_t = nt * dt
            next_t = curr_t + dt

            # ---------------------------------------
            # 1st-step: get the p^{n+1}
            # ---------------------------------------
            # # Pressure-Right-Matrix
            # for Dirichlet-face-integration
            gu_val0 = self.uh_grad_value_at_faces(uh0, f_bcs, cellidxDir, localidxDir)  # grad_u0: (NQ,NDir,GD)
            gu_val1 = self.uh_grad_value_at_faces(uh1, f_bcs, cellidxDir, localidxDir)  # grad_u0: (NQ,NDir,GD)

            # for cell-integration
            u_val0 = vspace.value(uh0, c_bcs)  # (NQ,NC)
            u_val1 = vspace.value(uh1, c_bcs)

            nolinear_val = self.NSNolinearTerm(uh0, uh1, c_bcs)  # last_nolinear_val.shape: (NQ,NC,GD)
            nolinear_val0 = nolinear_val[..., 0]  # (NQ,NC)
            nolinear_val1 = nolinear_val[..., 1]  # (NQ,NC)

            Neumann_0 = pde.neumann_0(f_pp_Neu, next_t, n_Neu)  # (NQ,NE)
            Neumann_1 = pde.neumann_1(f_pp_Neu, next_t, n_Neu)  # (NQ,NE)
            uDir_val = pde.dirichlet(f_pp, next_t)  # (NQ,NDir,GD)
            f_val = pde.source(c_pp, next_t)  # (NQ,NC,GD)

            # # --- to update the pressure value --- # #
            # # to get the Pressure's Right-hand Vector
            prv = np.zeros((pdof.number_of_global_dofs(),), dtype=self.ftype)  # (Npdof,)

            # for Dirichlet faces integration
            dir_int0 = -1/dt * np.einsum('i, ijk, jk, ijn, j->jn', f_ws, uDir_val, n_Dir, p_phi, dir_face_measure)  # (NDir,fldof)
            dir_int1 = - pde.nu * (np.einsum('i, j, ij, jin, j->jn', f_ws, n_Dir[:, 1], gu_val1[..., 0]-gu_val0[..., 1],
                                             p_gphi_f[..., 0], dir_face_measure)
                                   + np.einsum('i, j, ij, jin, j->jn', f_ws, -n_Dir[:, 0], gu_val1[..., 0]-gu_val0[..., 1],
                                               p_gphi_f[..., 1], dir_face_measure))  # (NDir,cldof)
            # for cell integration
            cell_int0 = 1/dt * (np.einsum('i, ij, ijk, j->jk', c_ws, u_val0, p_gphi_c[..., 0], cell_measure)
                                + np.einsum('i, ij, ijk, j->jk', c_ws, u_val1, p_gphi_c[..., 1], cell_measure))  # (NC,cldof)
            cell_int1 = -(np.einsum('i, ij, ijk, j->jk', c_ws, nolinear_val0, p_gphi_c[..., 0], cell_measure)
                          + np.einsum('i, ij, ijk, j->jk', c_ws, nolinear_val1, p_gphi_c[..., 1], cell_measure))  # (NC,cldof)
            cell_int2 = (np.einsum('i, ij, ijk, j->jk', c_ws, f_val[..., 0], p_gphi_c[..., 0], cell_measure)
                         + np.einsum('i, ij, ijk, j->jk', c_ws, f_val[..., 1], p_gphi_c[..., 1], cell_measure))  # (NC,cldof)

            np.add.at(prv, Dir_face2dof, dir_int0)
            np.add.at(prv, Dir_cell2dof, dir_int1)
            np.add.at(prv, pcell2dof, cell_int0 + cell_int1 + cell_int2)

            # # Method I: The following code is right! Pressure satisfies \int_\Omega p = 0
            plsm_temp = bmat([[plsm, basis_int.reshape(-1, 1)], [basis_int, None]], format='csr')
            prv = np.r_[prv, 0]
            ph[:] = spsolve(plsm_temp, prv)[:-1]  # we have added one addtional dof

            # # Method II: Using the Dirichlet boundary of pressure
            # def dir_pressure(p):
            #     return pde.pressure(p, next_t)
            # bc = DirichletBC(pspace, dir_pressure)
            # plsm_temp, prv = bc.apply(plsm.copy(), prv)
            # ph[:] = spsolve(plsm_temp, prv).reshape(-1)

            # # --- to update the velocity value --- # #
            grad_ph = pspace.grad_value(ph, c_bcs)  # (NQ,NC,2)

            # the velocity u's Left-Matrix
            ulm0 = 1 / dt * ulmm + pde.nu * ulsm
            ulm1 = 1 / dt * ulmm + pde.nu * ulsm

            # # to get the u's Right-hand Vector
            def dir_u0(p):
                return pde.dirichlet(p, next_t)[..., 0]

            def dir_u1(p):
                return pde.dirichlet(p, next_t)[..., 1]

            # for the first-component of velocity
            urv0 = np.zeros((vdof.number_of_global_dofs(),), dtype=self.ftype)  # (Nvdof,)
            urv0_c = np.einsum('i, ij, ijk, j->jk', c_ws, u_val0/dt - grad_ph[..., 0] - nolinear_val0
                               + f_val[..., 0], u_phi, cell_measure)  # (NC,clodf)
            urv0_f = self.pde.nu * np.einsum('i, ij, ijn, j->jn', f_ws, Neumann_0, u_phi_f, neu_face_measure)
            np.add.at(urv0, ucell2dof, urv0_c)
            # np.add.at(urv0, Neu_face2dof, urv0_f)
            u0_bc = DirichletBC(vspace, dir_u0, threshold=idxDirEdge)
            ulm0, urv0 = u0_bc.apply(ulm0, urv0)
            uh0[:] = spsolve(ulm0, urv0).reshape(-1)

            # for the second-component of velocity
            urv1 = np.zeros((vdof.number_of_global_dofs(),), dtype=self.ftype)  # (Nvdof,)
            urv1_c = np.einsum('i, ij, ijk, j->jk', c_ws, u_val1/dt - grad_ph[..., 1] - nolinear_val1
                               + f_val[..., 1], u_phi, cell_measure)  # (NC,clodf)
            urv1_f = self.pde.nu * np.einsum('i, ij, ijn, j->jn', f_ws, Neumann_1, u_phi_f, neu_face_measure)
            np.add.at(urv1, ucell2dof, urv1_c)
            # np.add.at(urv1, Neu_face2dof, urv1_f)
            u1_bc = DirichletBC(vspace, dir_u1, threshold=idxDirEdge)
            ulm1, urv1 = u1_bc.apply(ulm1, urv1)
            uh1[:] = spsolve(ulm1, urv1).reshape(-1)

            if nt % 50 == 0:
                print('# ------------ logging the circle info ------------ #')
                print('current t = ', curr_t)
                p_l2err, u0_l2err, u1_l2err = self.currt_error(ph, uh0, uh1, next_t)
                print('p_l2err = %e,  u0_l2err = %e,  u1_l2err = %e' % (p_l2err, u0_l2err, u1_l2err))
                print('# ------------------------------------------------- # \n')
                if np.isnan(p_l2err) | np.isnan(u0_l2err) | np.isnan(u1_l2err):
                    print('Some error is nan: breaking the program')
                    break

            # print('end of current time')

        print('# ------------ the end error ------------ #')
        p_l2err, u0_l2err, u1_l2err = self.currt_error(ph, uh0, uh1, next_t)
        print('p_l2err = %e,  u0_l2err = %e,  u1_l2err = %e' % (p_l2err, u0_l2err, u1_l2err))

    def currt_error(self, ph, uh0, uh1, t):
        pde = self.pde

        def currt_pressure(p):
            return pde.pressure(p, t)
        p_l2err = self.pspace.integralalg.L2_error(currt_pressure, ph)
        # print('p_l2err = %e' % p_l2err)

        def currt_u0(p):
            return pde.velocity(p, t)[..., 0]
        u0_l2err = self.vspace.integralalg.L2_error(currt_u0, uh0)
        # print('u0_l2err = %e' % u0_l2err)

        def currt_u1(p):
            return pde.velocity(p, t)[..., 1]
        u1_l2err = self.vspace.integralalg.L2_error(currt_u1, uh1)
        # print('u1_l2err = %e' % u1_l2err)

        return p_l2err, u0_l2err, u1_l2err

    def uh_grad_value_at_faces(self, vh, f_bcs, cellidx, localidx):
        cell2dof = self.vdof.cell2dof
        f_gphi = self.vspace.edge_grad_basis(f_bcs, cellidx, localidx)  # (NE,NQ,cldof,GD)

        # val.shape: (NQ,NE,GD)
        # vh.shape: (v_gdof,)
        # vh[cell2dof[cellidx]].shape: (Ncellidx,cldof)
        val = np.einsum('ik, ijkm->jim', vh[cell2dof[cellidx]], f_gphi)
        return val

    def NSNolinearTerm(self, uh0, uh1, bcs):
        vspace = self.vspace
        val0 = vspace.value(uh0, bcs)  # val0.shape: (NQ,NC)
        val1 = vspace.value(uh1, bcs)  # val1.shape: (NQ,NC)
        gval0 = vspace.grad_value(uh0, bcs)  # gval0.shape: (NQ,NC,2)
        gval1 = vspace.grad_value(uh1, bcs)

        NSNolinear = np.empty(gval0.shape, dtype=self.ftype)  # NSNolinear.shape: (NQ,NC,2)

        NSNolinear[..., 0] = val0 * gval0[..., 0] + val1 * gval0[..., 1]
        NSNolinear[..., 1] = val0 * gval1[..., 0] + val1 * gval1[..., 1]

        return NSNolinear

    def set_Dirichlet_edge(self, idxDirEdge=None):
        if idxDirEdge is not None:
            return idxDirEdge

        mesh = self.mesh
        edge2cell = mesh.ds.edge_to_cell()
        isBdEdge = (edge2cell[:, 0] == edge2cell[:, 1])  # (NE,), the bool vars, to get the boundary edges

        isDirEdge = isBdEdge  # here, we set all the boundary edges are Dir edges
        idxDirEdge, = np.nonzero(isDirEdge)  # (NE_Dir,)

        return idxDirEdge

    def set_Neumann_edge(self, idxNeuEdge=None):
        if idxNeuEdge is not None:
            return idxNeuEdge

        mesh = self.mesh
        edge2cell = mesh.ds.edge_to_cell()
        bdEdge = (edge2cell[:, 0] == edge2cell[:, 1])  # the bool vars, to get the boundary edges

        isNeuEdge = bdEdge  # here, we first set all the boundary edges are Neu edges

        issetNeuEdge = 'no'
        if issetNeuEdge == 'no':
            isNeuEdge = None

        idxNeuEdge, = np.nonzero(isNeuEdge)  # (NE_Dir,)

        return idxNeuEdge
Exemple #15
0
class PoissonFEMModel(object):
    def __init__(self, pde, mesh, p, q=3):
        self.space = LagrangeFiniteElementSpace(mesh, p, q=q)
        self.mesh = self.space.mesh
        self.pde = pde
        self.uh = self.space.function()
        self.uI = self.space.interpolation(pde.solution)
        self.integrator = self.mesh.integrator(p + 2)

    def recover_estimate(self, rguh):
        qf = self.integrator
        bcs, ws = qf.quadpts, qf.weights

        val0 = rguh.value(bcs)
        val1 = self.uh.grad_value(bcs)
        l = np.sum((val1 - val0)**2, axis=-1)
        e = np.einsum('i, ij->j', ws, l)
        e *= self.space.cellmeasure
        return np.sqrt(e)

    def residual_estimate(self, uh=None):
        if uh is None:
            uh = self.uh
        mesh = self.mesh
        GD = mesh.geo_dimension()
        NC = mesh.number_of_cells()

        n = mesh.face_normal()
        bc = np.array([1 / (GD + 1)] * (GD + 1), dtype=mesh.ftype)
        grad = uh.grad_value(bc)

        ps = mesh.bc_to_point(bc)
        try:
            d = self.pde.diffusion_coefficient(ps)
        except AttributeError:
            d = np.ones(NC, dtype=mesh.ftype)

        if isinstance(d, float):
            grad *= d
        elif len(d) == GD:
            grad = np.einsum('m, im->im', d, grad)
        elif isinstance(d, np.ndarray):
            if len(d.shape) == 1:
                grad = np.einsum('i, im->im', d, grad)
            elif len(d.shape) == 2:
                grad = np.einsum('im, im->im', d, grad)
            elif len(d.shape) == 3:
                grad = np.einsum('imn, in->in', d, grad)

        if GD == 2:
            face2cell = mesh.ds.edge_to_cell()
            h = np.sqrt(np.sum(n**2, axis=-1))
        elif GD == 3:
            face2cell = mesh.ds.face_to_cell()
            h = np.sum(n**2, axis=-1)**(1 / 4)

        J = h * np.sum(
            (grad[face2cell[:, 0]] - grad[face2cell[:, 1]]) * n, axis=-1)**2

        NC = mesh.number_of_cells()
        eta = np.zeros(NC, dtype=mesh.ftype)
        np.add.at(eta, face2cell[:, 0], J)
        np.add.at(eta, face2cell[:, 1], J)
        return np.sqrt(eta)

    def get_left_matrix(self):
        return self.space.stiff_matrix()

    def get_right_vector(self):
        return self.space.source_vector(self.pde.source)

    def solve(self):
        bc = DirichletBC(self.space, self.pde.dirichlet)

        start = timer()
        A = self.get_left_matrix()
        b = self.get_right_vector()
        end = timer()
        self.A = A

        print("Construct linear system time:", end - start)

        AD, b = bc.apply(A, b)

        start = timer()
        self.uh[:] = spsolve(AD, b)
        end = timer()
        print("Solve time:", end - start)

        ls = {'A': AD, 'b': b, 'solution': self.uh.copy()}

        return ls  # return the linear system

    def l2_error(self):
        e = self.uh - self.uI
        return np.sqrt(np.mean(e**2))

    def uIuh_error(self):
        e = self.uh - self.uI
        return np.sqrt(e @ self.A @ e)

    def L2_error(self, uh=None):
        u = self.pde.solution
        if uh is None:
            uh = self.uh.value
        else:
            uh = uh.value
        L2 = self.space.integralalg.L2_error(u, uh)
        return L2

    def H1_semi_error(self, uh=None):
        gu = self.pde.gradient
        if uh is None:
            guh = self.uh.grad_value
        else:
            guh = uh.grad_value
        H1 = self.space.integralalg.L2_error(gu, guh)
        return H1

    def recover_error(self, rguh):
        gu = self.pde.gradient
        guh = rguh.value
        mesh = self.mesh
        re = self.space.integralalg.L2_error(gu, guh, mesh)
        return re
Exemple #16
0
pde = CosCosData()
print(pde.a)

domain = pde.domain()
mesh = MF.boxmesh2d(domain, nx=10, ny=10, meshtype='tri')
intalg = FEMeshIntegralAlg(mesh, q)

c = intalg.mesh_integral(pde.solution, q=q, power=2)

print(c)

space = LagrangeFiniteElementSpace(mesh, p=p)
node = mesh.entity('node')  # (NN, 2)
uI = pde.solution(node)  # ndarray (NN, )

uI = space.function(array=uI)  # 返回一个有限元函数对象

fig0 = plt.figure()
axes = fig0.gca()
mesh.add_plot(axes)

fig1 = plt.figure()
axes = fig1.gca(projection='3d')
uI.add_plot(axes, cmap='rainbow')
plt.show()

if False:
    box = [0, 1, 0, 1]
    mesh = mf.boxmesh2d(box, nx=n, ny=n, meshtype='tri')
    space = LagrangeFiniteElementSpace(mesh, p=p)