Ejemplo n.º 1
0
class ModelSover():
    def __init__(self, model, p, NS=0, NT=100):
        self.model = model

        self.mesh0 = model.space_mesh(NS=NS, p=p)  # 初始网格
        self.mesh1 = model.space_mesh(NS=NS, p=p)  # 计算网格

        self.timeline = model.time_mesh(NT=NT)

        self.cspace = ParametricLagrangeFiniteElementSpace(self.mesh1,
                                                           p=p,
                                                           spacetype='C')
        self.dspace = ParametricLagrangeFiniteElementSpace(self.mesh1,
                                                           p=p - 1,
                                                           spacetype='D')

        self.integrator = self.cspace.integralalg.integrator

        bc = self.mesh0.entity_barycenter('cell')
        self.rho = model.init_rho(bc)  # (NC, )
        self.gamma = model.adiabatic_index(bc)  #(NC, )

        self.MV = self.cspace.mass_matrix(c=self.rho)
        self.ME = self.dspace.mass_matrix(c=self.rho)

        GD = self.mesh1.geo_dimension()
        self.x = self.cspace.function(dim=GD)  # 位置
        self.v = self.cspace.function(dim=GD)  # 速度
        self.e = self.dspace.function()  # 能量

        self.x[:] = self.mesh1.entity('node')  # 初始化自由度位置
        model.init_velocity(self.v)  # 初始化速度
        model.init_energe(self.e)  # 初始化能量

        self.cx = self.cspace.function(dim=GD)  # 保存临时的解
        self.cv = self.cspace.function(dim=GD)
        self.ce = self.dspace.function()

        self.cx[:] = self.mesh1.entity('node')
        model.init_velocity(self.cv)
        model.init_energe(self.ce)

        self.mesh1.celldata['rho'] = self.rho
        self.mesh1.celldata['gamma'] = self.gamma
        self.mesh1.nodedata['velocity'] = self.cv

    def get_force_matrix(self, q=None):

        # 积分公式
        qf = self.integrator if q is None else self.mesh1.integrator(
            q, etype='cell')
        # bcs : (NQ, n)
        # ws : (NQ, )
        bcs, ws = qf.get_quadrature_points_and_weights()

        rm = self.mesh1.reference_cell_measure()  # 参考单元测度
        d = self.mesh1.first_fundamental_form(bcs)
        d = np.sqrt(np.linalg.det(d))

        d *= self.model.stress(bcs, self.ce, self.rho, self.gamma, self.mesh0,
                               self.mesh1)

        gphi = self.cspace.grad_basis(bcs)  # (NQ, NC, ldof, GD)
        phi = self.dspace.basis(bcs)

        M0 = np.einsum('q, qci, qcj, qc->cij', ws * rm, gphi[..., 0], phi,
                       d)  # (NC, ldof0, ldof1)
        M1 = np.einsum('q, qci, qcj, qc->cij', ws * rm, gphi[..., 1], phi,
                       d)  # (NC, ldof0, ldof1)

        c2d0 = self.cspace.cell_to_dof()
        c2d1 = self.dspace.cell_to_dof()

        I = np.broadcast_to(c2d0[:, :, None], shape=M0.shape)
        J = np.broadcast_to(c2d1[:, None, :], shape=M0.shape)

        gdof0 = self.cspace.number_of_global_dofs()
        gdof1 = self.dspace.number_of_global_dofs()
        M0 = csr_matrix((M0.flat, (I.flat, J.flat)), shape=(gdof0, gdof1))

        M1 = csr_matrix((M1.flat, (I.flat, J.flat)), shape=(gdof0, gdof1))

        return M0, M1

    def solve_one_step(self):

        dt = self.timeline.current_time_step_length()

        M0, M1 = self.get_force_matrix()

        one = np.ones(M0.shape[1])
        F0 = spsolve(self.MV, M0 @ one)
        F1 = spsolve(self.MV, M1 @ one)

        self.cv[:, 0] = self.v[:, 0] - dt / 2 * F0
        self.cv[:, 1] = self.v[:, 1] - dt / 2 * F1

        # 边界条件处理

        edge2cell = self.mesh1.ds.edge_to_cell()
        isBdEdge = edge2cell[:, 0] == edge2cell[:, 1]
        edge2dof = self.cspace.edge_to_dof()[isBdEdge]
        en = self.mesh1.edge_unit_normal(index=isBdEdge)  # (NE, GD)

        # (NE, ldof, GD) * (NE, 1, GD) = (NE, ldof, GD) --> (NE, ldof)
        l = np.sum(self.cv[edge2dof, :] * en[:, None, :], axis=-1)
        val = l[..., None] * en[:, None, :]  # (NE, ldof, 1) * (NE, 1, GD)-->
        np.subtract.at(self.cv, (edge2dof, np.s_[:]), val)

        F = spsolve(self.ME, self.cv[:, 0] @ M0 + self.cv[:, 1] @ M1)
        self.ce[:] = self.e + dt / 2 * F

        self.cx[:] = self.x + dt / 2 * self.cv

        self.mesh1.node[:] = self.cx
        M0, M1 = self.get_force_matrix()

        F0 = spsolve(self.MV, M0 @ one)
        F1 = spsolve(self.MV, M1 @ one)
        self.cv[:, 0] = self.v[:, 0] - dt * F0
        self.cv[:, 1] = self.v[:, 1] - dt * F1

        l = np.sum(self.cv[edge2dof, :] * en[:, None, :],
                   axis=-1)  # (NE, ldof)
        val = l[..., None] * en[:, None, :]  # (NE, ldof, GD)
        np.subtract.at(self.cv, (edge2dof, np.s_[:]), val)

        v = (self.cv + self.v) / 2
        F = spsolve(self.ME, v[:, 0] @ M0 + v[:, 1] @ M1)
        self.ce[:] = self.e + dt * F
        self.cx[:] = self.x + dt * v

        self.x[:] = self.cx
        self.v[:] = self.cv
        self.e[:] = self.ce

        self.mesh1.node[:] = self.x
        self.mesh1.nodedata['velocity'] = self.v

    def solve(self, step=1):
        """

        Notes
        -----

        计算所有的时间层。
        """

        timeline = self.timeline
        dt = timeline.current_time_step_length()
        timeline.reset()  # 时间置零

        n = timeline.current
        fname = 'test_' + str(n).zfill(10) + '.vtu'
        self.mesh1.to_vtk(fname=fname)
        while not timeline.stop():
            self.solve_one_step()
            timeline.current += 1
            if timeline.current % step == 0:
                n = timeline.current
                fname = 'test_' + str(n).zfill(10) + '.vtu'
                self.mesh1.to_vtk(fname=fname)
        timeline.reset()
class LagrangianHydrodynamicsSimulator():
    def __init__(self, model, p, NS=0, NT=100):

        self.model = model  # 物理模型
        self.mesh0 = model.space_mesh(NS=NS, p=p)  # 初始网格
        self.mesh1 = model.space_mesh(NS=NS, p=p)  # 计算网格

        self.timeline = model.time_mesh(NT=NT)  # 时间离散网格

        # 运动学空间
        self.cspace = ParametricLagrangeFiniteElementSpace(self.mesh1,
                                                           p=p,
                                                           spacetype='C')
        # 热力学空间
        self.dspace = ParametricLagrangeFiniteElementSpace(self.mesh1,
                                                           p=p - 1,
                                                           spacetype='D')

        self.integrator = self.cspace.integralalg.integrator

        bc = self.mesh0.entity_barycenter('cell')
        # 物质密度
        self.rho = model.init_rho(bc)  # (NC, )
        # 绝热指数
        self.gamma = model.adiabatic_index(bc)  #(NC, )

        self.MV = self.cspace.mass_matrix(c=self.rho)  # 运动空间的质量矩阵
        self.ME = self.dspace.mass_matrix(c=self.rho)  # 热力学空间的质量矩阵

        GD = self.mesh1.geo_dimension()
        self.x = self.cspace.function(dim=GD)  # 位置
        self.v = self.cspace.function(dim=GD)  # 速度
        self.e = self.dspace.function()  # 能量

        self.x[:] = self.mesh1.entity('node')  # 初始化自由度位置
        model.init_velocity(self.v)  # 初始化速度
        model.init_energe(self.e)  # 初始化能量

        self.cx = self.cspace.function(dim=GD)  # 保存临时的解
        self.cv = self.cspace.function(dim=GD)
        self.ce = self.dspace.function()

        self.cx[:] = self.mesh1.entity('node')
        model.init_velocity(self.cv)
        model.init_energe(self.ce)

        self.mesh1.celldata['rho'] = self.rho
        self.mesh1.celldata['gamma'] = self.gamma
        self.mesh1.nodedata['velocity'] = self.cv

    def get_force_matrix(self, q=None):

        # 积分公式
        qf = self.integrator if q is None else self.mesh1.integrator(
            q, etype='cell')
        # bcs : (NQ, n)
        # ws : (NQ, )
        bcs, ws = qf.get_quadrature_points_and_weights()

        rm = self.mesh1.reference_cell_measure()  # 参考单元测度
        d = self.mesh1.first_fundamental_form(bcs)
        d = np.sqrt(np.linalg.det(d))

        d *= self.model.stress(bcs, self.ce, self.rho, self.gamma, self.mesh0,
                               self.mesh1)

        gphi = self.cspace.grad_basis(bcs)  # (NQ, NC, ldof, GD)
        phi = self.dspace.basis(bcs)

        M0 = np.einsum('q, qci, qcj, qc->cij', ws * rm, gphi[..., 0], phi,
                       d)  # (NC, ldof0, ldof1)
        M1 = np.einsum('q, qci, qcj, qc->cij', ws * rm, gphi[..., 1], phi,
                       d)  # (NC, ldof0, ldof1)

        c2d0 = self.cspace.cell_to_dof()
        c2d1 = self.dspace.cell_to_dof()

        I = np.broadcast_to(c2d0[:, :, None], shape=M0.shape)
        J = np.broadcast_to(c2d1[:, None, :], shape=M0.shape)

        gdof0 = self.cspace.number_of_global_dofs()
        gdof1 = self.dspace.number_of_global_dofs()
        M0 = csr_matrix((M0.flat, (I.flat, J.flat)), shape=(gdof0, gdof1))

        M1 = csr_matrix((M1.flat, (I.flat, J.flat)), shape=(gdof0, gdof1))

        return M0, M1

    def solve_one_step(self):

        dt = self.timeline.current_time_step_length()

        M0, M1 = self.get_force_matrix()

        one = np.ones(M0.shape[1])
        F0 = spsolve(self.MV, M0 @ one)
        F1 = spsolve(self.MV, M1 @ one)

        self.cv[:, 0] = self.v[:, 0] - dt / 2 * F0
        self.cv[:, 1] = self.v[:, 1] - dt / 2 * F1

        # 网格节点自由度
        # dof == 0: 表示固定点
        # dof == 1: 表示边界上的点
        # dof == 2: 区域内部点
        dof = self.mesh0.nodedata['dof']

        # 边界条件处理
        self.cv[dof == 0] = 0.0
        en = self.mesh0.meshdata['bd_normal']
        vv = self.cv[dof == 1]
        l = np.sum(vv * en, axis=-1)  # (NE, )
        self.cv[dof == 1] -= l[:, None] * en

        F = spsolve(self.ME, self.cv[:, 0] @ M0 + self.cv[:, 1] @ M1)
        self.ce[:] = self.e + dt / 2 * F

        self.cx[:] = self.x + dt / 2 * self.cv

        self.mesh1.node[:] = self.cx
        M0, M1 = self.get_force_matrix()

        F0 = spsolve(self.MV, M0 @ one)
        F1 = spsolve(self.MV, M1 @ one)
        self.cv[:, 0] = self.v[:, 0] - dt * F0
        self.cv[:, 1] = self.v[:, 1] - dt * F1

        # 边界条件处理
        self.cv[dof == 0] = 0.0
        vv = self.cv[dof == 1]
        l = np.sum(vv * en, axis=-1)  # (NE, )
        self.cv[dof == 1] -= l[:, None] * en

        v = (self.cv + self.v) / 2
        F = spsolve(self.ME, v[:, 0] @ M0 + v[:, 1] @ M1)
        self.ce[:] = self.e + dt * F
        self.cx[:] = self.x + dt * v

        self.x[:] = self.cx
        self.v[:] = self.cv
        self.e[:] = self.ce

        self.mesh1.node[:] = self.x
        self.mesh1.nodedata['velocity'] = self.v

    def solve(self, step=1):
        """

        Notes
        -----

        计算所有的时间层。
        """

        timeline = self.timeline
        dt = timeline.current_time_step_length()
        timeline.reset()  # 时间置零

        n = timeline.current
        fname = 'test_' + str(n).zfill(10) + '.vtu'
        self.mesh1.to_vtk(fname=fname)
        while not timeline.stop():
            self.solve_one_step()
            timeline.current += 1
            if timeline.current % step == 0:
                n = timeline.current
                fname = 'test_' + str(n).zfill(10) + '.vtu'
                self.mesh1.to_vtk(fname=fname)
        timeline.reset()