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
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
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
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))
] 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 误差
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)
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
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
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
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)