def main(n): '''Solves grad-div problem in 2d with HypreAMS preconditioning''' # Exact solution x, y = sp.symbols('x[0] x[1]') u = sp.sin(pi * x * (1 - x) * y * (1 - y)) sp_div = lambda f: f[0].diff(x, 1) + f[1].diff(y, 1) sp_grad = lambda f: sp.Matrix([f.diff(x, 1), f.diff(y, 1)]) sigma = sp_grad(u) f = -sp_div(sigma) + u sigma_expr, u_expr, f_expr = list(map(as_expression, (sigma, u, f))) # The discrete problem mesh = UnitSquareMesh(n, n) V = FunctionSpace(mesh, 'RT', 1) Q = FunctionSpace(mesh, 'DG', 0) W = (V, Q) sigma, u = list(map(TrialFunction, W)) tau, v = list(map(TestFunction, W)) a00 = inner(sigma, tau) * dx a01 = inner(div(tau), u) * dx a10 = inner(div(sigma), v) * dx a11 = -inner(u, v) * dx L0 = inner(Constant((0, 0)), tau) * dx L1 = inner(-f_expr, v) * dx AA = block_assemble([[a00, a01], [a10, a11]]) bb = block_assemble([L0, L1]) # b00 = inner(sigma, tau)*dx + inner(div(sigma), div(tau))*dx # B00 = LU(assemble(b00)) B00 = HypreAMS(V) b11 = inner(u, v) * dx B11 = LumpedInvDiag(assemble(b11)) BB = block_mat([[B00, 0], [0, B11]]) AAinv = MinRes(AA, precond=BB, tolerance=1e-10, maxiter=500, show=2) # Compute solution sigma_h, u_h = AAinv * bb sigma_h, u_h = Function(V, sigma_h), Function(Q, u_h) niters = len(AAinv.residuals) - 1 # error = sqrt(errornorm(sigma_expr, sigma_h, 'Hdiv', degree_rise=1)**2 + # errornorm(u_expr, u_h, 'L2', degree_rise=1)**2) hmin = mesh.mpi_comm().tompi4py().allreduce(mesh.hmin(), pyMPI.MIN) error = 1. return hmin, V.dim() + Q.dim(), niters, error
print(clog.header()) for i in range(5): n = 4*2**i mesh = UnitSquareMesh(n, n) facet_f = MeshFunction('size_t', mesh, 1, 0) [subdomain.mark(facet_f, tag) for tag, subdomain in mms['subdomains'].items()] a, L, W, bcs = setup_problem(facet_f, mms, flux_deg=args.flux_degree) A, b = map(ii_assemble, (a, L)) A, b = apply_bc(A, b, bcs) B = setup_preconditioner(W, facet_f, mms, flux_deg=args.flux_degree, hs_block=args.hs) wh = ii_Function(W) Ainv = MinRes(A, precond=B, tolerance=1E-10, show=0) # NOTE: assigning from block_vec wh.vector()[:] = ii_convert(Ainv*b) sigmah, uh, ph = wh niters = len(Ainv.residuals) clog.add((sigmah, uh, ph, niters)) print(clog.report_last(with_name=False)) eoc = 0.95 if args.flux_degree == 1 else 1.95 # Match increamental and lstsq rate passed = all(clog[var].get_rate()[0] > eoc for var in ('sigma', 'u', 'p')) passed = passed and all(clog[var].get_rate()[1] > eoc for var in ('sigma', 'u', 'p')) passed = passed and all(count < 40 for count in clog['niters'])
def lagrange_mixed(lmbda, mu, f, h, mesh, Z=None): ''' Solves -div(sigma) = f in Omega sigma.n = h on boundary where sigma(u) = 2*mu*eps(u) + lambda*div(u)*I. The problem is reformulated by Lagrange multiplier nu to inforce orthogonality with the space of rigid motions. To get robustnes in lmbda solid pressure p = lambda*div u is introduced. The system to be solved with MinRes is P*[A C B; *[u, = P*[L, C' D 0; p, 0, B' 0 0] nu] 0] with P a precondtioner. We run on series of meshes to show mesh independence of the solver. ''' if not isinstance(mesh, Mesh): # NOTE: You can precompute the 'symbolic' basis and pass it here return [lagrange_mixed(lmbda, mu, f, h, mesh_) for mesh_ in mesh] # For cube V = VectorFunctionSpace(mesh, 'CG', 2) Q = FunctionSpace(mesh, 'CG', 1) u, v = TrialFunction(V), TestFunction(V) p, q = TrialFunction(Q), TestFunction(Q) # Strain epsilon = lambda u: sym(grad(u)) # Stress gdim = mesh.geometry().dim() sigma = lambda u: 2*mu*epsilon(u) + lmbda*tr(epsilon(u))*Identity(gdim) a = 2*mu*inner(sym(grad(u)), sym(grad(v)))*dx A = assemble(a) c = inner(div(v), p)*dx C = assemble(c) d = -(inner(p, q)/Constant(lmbda))*dx D = assemble(d) m = inner(u, v)*dx M = assemble(m) # NOTE: Avoiding use of Q space in the assembly - dense blocks! X = VectorFunctionSpace(mesh, 'R', 0, dim=6) Zh = rigid_motions.RMBasis(V, X, Z) # L^2 orthogonal B = M*Zh # System operator AA = block_mat([[A, C, B], [block_transpose(C), D, 0], [block_transpose(B), 0, 0]]) # Right hand side L = inner(f, v)*dx + inner(h, v)*ds b0 = assemble(L) b1 = assemble(inner(Constant(0), q)*dx) # Equivalent to assemble(inner(Constant((0, )*6), q)*dx) but cheaper b2 = Function(X).vector() bb = block_vec([b0, b1, b2]) # Block diagonal preconditioner IV = assemble(a + m) IQ = assemble(inner(p, q)*dx) IX = rigid_motions.identity_matrix(X) BB = block_mat([[AMG(IV), 0, 0], [0, AMG(IQ), 0], [0, 0, IX]]) # Solve, using random initial guess x0 = AA.create_vec() [as_backend_type(xi).vec().setRandom() for xi in x0] AAinv = MinRes(AA, precond=BB, initial_guess=x0, maxiter=120, tolerance=1E-8, show=2, relativeconv=True) x = AAinv*bb # # Functions from coefficients # # uh = Function(V, x[0]) # Displacement # # ph = Function(Q, x[1]) # Solid pressure # # nuh = Zh.rigid_motion(x[2]) # Function in V niters = len(AAinv.residuals) - 1 assert niters < 120 P = rigid_motions.Projector(Zh) P*x0[0] # to get orthogonality if MPI.rank(mesh.mpi_comm()) == 0: print '\033[1;37;31m%s\033[0m' % ('Orthogonality %g' % max(P.alphas)) pass return V.dim() + Q.dim() + 6, niters
def setup_preconditioner(W, which, eps): from block.algebraic.petsc import AMG from xii.linalg.block_utils import ReductionOperator, RegroupOperator # This is best on H1 x H1 x L2 spaces where the Discacciati proces # well posedness if which == 0: # The following settings seem not so bad for GMRES # -ksp_rtol 1E-6 # -ksp_monitor_true_residual none # -ksp_type gmres # -ksp_gmres_restart 30 # -ksp_gmres_modifiedgramschmidt 1 u1, p1, p = map(TrialFunction, W) v1, q1, q = map(TestFunction, W) b00 = inner(grad(u1), grad(v1)) * dx + inner(u1, v1) * dx B00 = AMG(ii_assemble(b00)) b11 = inner(p1, q1) * dx B11 = AMG(ii_assemble(b11)) b22 = inner(grad(p), grad(q)) * dx + inner(p, q) * dx B22 = AMG(ii_assemble(b22)) return block_diag_mat([B00, B11, B22]) # System without coupling: Solve Poisson and Stokes individually iface_domain = BoundaryMesh(W[-1].mesh(), 'exterior') M = FunctionSpace(iface_domain, 'DG', 0) u1, p1, p = map(TrialFunction, W) v1, q1, q = map(TestFunction, W) dxGamma = Measure('dx', domain=iface_domain) # We will need traces of the functions on the boundary Tu1, Tv1 = map(lambda x: Trace(x, iface_domain), (u1, v1)) Tp, Tq = map(lambda x: Trace(x, iface_domain), (p, q)) n = OuterNormal(iface_domain, [0.5, 0.5]) # Outer of Darcy n1 = -n # Outer of Stokes # Get tangent vector tau1 = Constant(((0, -1), (1, 0))) * n1 stokes = [[0] * 2 for i in range(2)] stokes[0][0] = Constant(2)*inner(sym(grad(u1)), sym(grad(v1)))*dx +\ inner(u1, v1)*dx +\ inner(dot(Tu1, tau1), dot(Tv1, tau1))*dxGamma stokes[0][1] = -inner(p1, div(v1)) * dx stokes[1][0] = -inner(q1, div(u1)) * dx if which == 1: B0 = UMFPACK_LU(ii_convert(ii_assemble(stokes))) poisson = inner(p, q) * dx + inner(grad(p), grad(q)) * dx B1 = AMG(assemble(poisson)) # 2x2 B = block_diag_mat([B0, B1]) # Need an adapter for 3 vectors R = ReductionOperator([2, 3], W) return R.T * B * R from block.iterative import MinRes # Solve stokes with Minres A0 = ii_assemble(stokes) # The preconditioner B = block_diag_mat([ AMG(ii_assemble(inner(grad(u1), grad(v1)) * dx + inner(u1, v1) * dx)), AMG(ii_assemble(inner(p1, q1) * dx)) ]) # Approx A0_inv = MinRes(A0, precond=B, relativeconv=True, tolerance=1E-5) B0 = block_op_from_action(action=lambda b, A0_inv=A0_inv: A0_inv * b, create_vec=lambda i, A0=A0: A0.create_vec(i)) poisson = inner(p, q) * dx + inner(grad(p), grad(q)) * dx B1 = AMG(assemble(poisson)) # 2x2 B = block_diag_mat([B0, B1]) # Need an adapter for 3 vectors R = RegroupOperator([2, 3], W) return R.T * B * R
def lagrange_primal(lmbda, mu, f, h, mesh, Z=None): ''' Solves -div(sigma) = f in Omega sigma.n = h on boundary where sigma(u) = 2*mu*eps(u) + lambda*div(u)*I. The problem is reformulated by Lagrange multiplier p to inforce orthogonality with the space of rigid motions. The system to be solved with MinRes is P*[A B;*[u; = P*[L, B' ] p] 0] with P a precondtioner. We run on series of meshes to show mesh independence of the solver. ''' if not isinstance(mesh, Mesh): # NOTE: You can precompute the 'symbolic' basis and pass it here return [lagrange_primal(lmbda, mu, f, h, mesh_) for mesh_ in mesh] # For cube V = VectorFunctionSpace(mesh, 'CG', 1) u, v = TrialFunction(V), TestFunction(V) # Strain epsilon = lambda u: sym(grad(u)) # Stress gdim = mesh.geometry().dim() sigma = lambda u: 2 * mu * epsilon(u) + lmbda * tr(epsilon(u)) * Identity( gdim) # Energy of elastic deformation a = inner(sigma(u), epsilon(v)) * dx A = assemble(a) # Mass matrix for B m = inner(u, v) * dx M = assemble(m) # NOTE: Avoiding use of Q space in the assembly - dense blocks! Q = VectorFunctionSpace(mesh, 'R', 0, dim=6) Zh = rigid_motions.RMBasis(V, Q, Z) # L^2 orthogonal B = M * Zh # System operator AA = block_mat([[A, B], [block_transpose(B), 0]]) # Right hand side L = inner(f, v) * dx + inner(h, v) * ds b0 = assemble(L) # Equivalent to assemble(inner(Constant((0, )*6), q)*dx) but cheaper b1 = Function(Q).vector() bb = block_vec([b0, b1]) # Block diagonal preconditioner AM = assemble(a + m) I = rigid_motions.identity_matrix(Q) BB = block_mat([[AMG(AM), 0], [0, I]]) # Solve, using random initial guess x0 = AA.create_vec() [as_backend_type(xi).vec().setRandom() for xi in x0] AAinv = MinRes(AA, precond=BB, initial_guess=x0, maxiter=100, tolerance=1E-8, show=2, relativeconv=True) x = AAinv * bb # Functions from coefficients # uh = Function(V, x[0]) # Displacement # zh = Zh.rigid_motion(x[1]) # Function in V niters = len(AAinv.residuals) - 1 assert niters < 100 P = rigid_motions.Projector(Zh) P * x[0] # to get orthogonality if MPI.rank(mesh.mpi_comm()) == 0: print '\033[1;37;31m%s\033[0m' % ('Orthogonality %g' % max(P.alphas)) return V.dim() + 6, niters
def mini_block(n): '''Just check MMS''' mesh = UnitSquareMesh(n, n) # Just approx f_space = VectorFunctionSpace(mesh, 'DG', 1) h_space = TensorFunctionSpace(mesh, 'DG', 1) u_int = interpolate(u_exact, VectorFunctionSpace(mesh, 'CG', 2)) p_int = interpolate(p_exact, FunctionSpace(mesh, 'CG', 2)) f = project(-div(grad(u_int)) + grad(p_int), f_space) h = project(-p_int * Identity(2) + grad(u_int), h_space) # ---------------- V = VectorFunctionSpace(mesh, 'Lagrange', 1) Vb = VectorFunctionSpace(mesh, 'Bubble', 3) Q = FunctionSpace(mesh, 'Lagrange', 1) W = [V, Vb, Q] u, ub, p = list(map(TrialFunction, W)) v, vb, q = list(map(TestFunction, W)) n = FacetNormal(mesh) a = [[0] * len(W) for _ in range(len(W))] a[0][0] = inner(grad(u), grad(v)) * dx a[0][2] = -inner(div(v), p) * dx a[2][0] = -inner(div(u), q) * dx a[1][1] = inner(grad(ub), grad(vb)) * dx a[1][2] = -inner(div(vb), p) * dx a[2][1] = -inner(div(ub), q) * dx a[0][1] = inner(grad(v), grad(ub)) * dx a[1][0] = inner(grad(vb), grad(u)) * dx # NOTE: bubbles don't contribute to surface L = [ inner(dot(h, n), v) * ds + inner(f, v) * dx, inner(f, vb) * dx, inner(Constant(0), q) * dx ] # Bubbles don't have bcs on the surface bcs = [[DirichletBC(W[0], u_exact, 'near(x[0], 0)')], [], []] AA = block_assemble(a) bb = block_assemble(L) block_bc(bcs, True).apply(AA).apply(bb) # Preconditioner B0 = AMG(AA[0][0]) H1_Vb = inner(grad(ub), grad(vb)) * dx + inner(ub, vb) * dx B1 = LumpedInvDiag(assemble(H1_Vb)) L2_Q = assemble(inner(p, q) * dx) B2 = LumpedInvDiag(L2_Q) BB = block_mat([[B0, 0, 0], [0, B1, 0], [0, 0, B2]]) x0 = AA.create_vec() x0.randomize() AAinv = MinRes(AA, precond=BB, tolerance=1e-10, maxiter=500, relativeconv=True, show=2, initial_guess=x0) # Compute solution u, ub, p = AAinv * bb uh_ = Function(V, u) uhb = Function(Vb, ub) ph = Function(Q, p) uh = Sum([uh_, uhb], degree=1) return uh, ph, sum(Wi.dim() for Wi in W)
L1 = inner(f, c) * dx from block import block_assemble, block_bc, block_mat from block.iterative import MinRes from block.algebraic.petsc import ML, Cholesky # lhs AA = block_assemble([[a00, a01, a02], [a10, a11, a12], [a20, a21, 0]]) # rhs b = block_assemble([L0, L1, 0]) # Collect boundary conditions bcs = [[bc0, bc1], [], []] block_bc(bcs, True).apply(AA).apply(b) b22 = inner(p, q) * dx B22 = assemble(b22) # Possible action of preconditioner BB = block_mat([[ML(AA[0, 0]), 0, 0], [0, Cholesky(AA[1, 1]), 0], [0, 0, Cholesky(B22)]]) AAinv = MinRes(AA, precond=BB, tolerance=1E-10, maxiter=500) U, B, P = AAinv * b p = Function(Q) p.vector()[:] = P # Plot solution plot(p) interactive()