class FastInv(BaseMatrix): def __init__(self, mat, blocks1, blocks2): super(FastInv, self).__init__() self.parmat = mat self.spmat = self.parmat if mpi_world.size == 1 else self.parmat.local_mat self.inv1 = self.spmat.CreateBlockSmoother(blocks1) self.inv2 = self.spmat.CreateBlockSmoother( blocks2) if blocks2 is not None else None self.res = self.spmat.CreateColVector() self.ty = self.spmat.CreateColVector() self.tmult = Timer("FastInv") def Height(self): return self.parmat.height def Width(self): return self.parmat.width def CreateRowVector(self): return self.spmat.CreateRowVector() def CreateColVector(self): return self.spmat.CreateColVector() def MultAdd(self, scal, x, y): self.ty[:] = 0 self.Mult(x, self.ty) y.local_vec.data += scal * self.ty def Mult(self, x, y): self.tmult.Start() y.local_vec.data = self.inv1 * x.local_vec # self.res.data = x - self.spmat * y # y.local_vec.data += self.inv2 * self.res self.tmult.Stop() def MultTransAdd(self, scal, x, y): self.ty[:] = 0 self.MultTrans(x, self.ty) y.local_vec.data += scal * self.ty def MultTrans(self, x, y): self.tmult.Start() # y.local_vec.data = self.inv2.T * x.local_vec # self.res.data = x - self.spmat.T * y # y.local_vec.data += self.inv1.T * self.res y.local_vec.data = self.inv1.T * x.local_vec self.tmult.Stop() def IsComplex(self): return False
def solve_with_bramble_pasciak_cg(a_matrix, b_matrix, pre_a, pre_schur_complement, gfu, gfp, f, g, tolerance, max_steps): sol = BlockVector([gfu, gfp]) bramble_pasciak_cg_timer = Timer("BramblePasciakCG") bramble_pasciak_cg_timer.Start() (solution, errors) = bramble_pasciak_cg(a_matrix, b_matrix, None, pre_a, pre_schur_complement, f, g, sol, tolerance=tolerance, max_steps=max_steps) bramble_pasciak_cg_timer.Stop() print("Bramble Pasciak CG took", bramble_pasciak_cg_timer.time, "seconds") return (solution, errors, bramble_pasciak_cg_timer.time)
def orthonormalize(basis, tries=3): orthonormalize_timer = Timer("orthonormalization") orthonormalize_timer.Start() for tries in range(tries): for j in range(len(basis)): for i in range(j): basis[j].data -= InnerProduct(basis[i], basis[j]) / \ InnerProduct(basis[i], basis[i]) * basis[i] basis[j].data = 1 / Norm(basis[j]) * basis[j] orthonormalize_timer.Stop() return basis
def solve_with_min_res(a, b, preA, preS, gfu, gfp, f, g, tolerance, max_steps): K = BlockMatrix([[a, b.T], [b, None]]) C = BlockMatrix([[preA, None], [None, preS]]) rhs = BlockVector([f, g]) sol = BlockVector([gfu, gfp]) min_res_timer = Timer("MinRes") min_res_timer.Start() (solution, errors) = MinRes(mat=K, pre=C, rhs=rhs, sol=sol, initialize=False, tol=tolerance, maxsteps=max_steps) min_res_timer.Stop() print("MinRes took", min_res_timer.time, "seconds") return (solution, errors, min_res_timer.time)
def solve_hcurldiv(mesh, discretization, solver_factory): V, S, Q = discretization( mesh, velocity_dirichlet='wall|inlet|cyl', velocity_neumann='outlet') X = FESpace([V, S, Q]) (u, sigma, p), (v, tau, q) = X.TnT() n = specialcf.normal(mesh.dim) a = BilinearForm(X, symmetric=True) a += SymbolicBFI(InnerProduct(sigma, tau)) a += SymbolicBFI(div(sigma) * v + div(tau) * u) a += SymbolicBFI(-(sigma * n) * n * (v * n) - (tau * n) * n * (u * n), element_boundary=True) a += SymbolicBFI(div(u) * q + div(v) * p) a.Assemble() f = LinearForm(X) f += SymbolicLFI(CoefficientFunction((0, x - 0.5)) * v) f.Assemble() grid_function = GridFunction(X) uin = CoefficientFunction((1.5 * 4 * y * (0.41 - y) / (0.41 * 0.41), 0)) grid_function.components[0].Set(uin, definedon=mesh.Boundaries("inlet")) direct_timer = Timer("Direct Solver") direct_timer.Start() res = grid_function.vec.CreateVector() res.data = f.vec - a.mat * grid_function.vec inv = a.mat.Inverse(freedofs=X.FreeDofs(), inverse="umfpack") grid_function.vec.data += inv * res direct_timer.Stop() velocity = CoefficientFunction(grid_function.components[0]) pressure = CoefficientFunction(grid_function.components[2]) Draw(velocity, mesh, "velocity") Draw(pressure, mesh, "pressure") ndofs = X.ndof return (velocity, pressure, [], direct_timer.time, ndofs)
def bramble_pasciak_cg(a_matrix, b_matrix, c_matrix, pre_a, pre_schur_complement, upper_rhs, lower_rhs, solution=None, tolerance=1e-12, max_steps=1000, print_rates=True): ev_timer = Timer("eigenvalues") ev_timer.Start() eigenvalues = EigenValues_Preconditioner(mat=a_matrix, pre=pre_a) k = 1 / min(eigenvalues) + 1e-3 ev_timer.Stop() print("scale factor: ", k) print("condition number: ", max(eigenvalues) / min(eigenvalues)) scaled_pre_a = ScaledPreconditioner(k, a_matrix, pre_a) original_matrix = BlockMatrix([[a_matrix, b_matrix.T], [b_matrix, c_matrix]]) full_pre_a = BlockMatrix([[scaled_pre_a, None], [None, IdentityMatrix(b_matrix.height)]]) full_b = BlockMatrix([[IdentityMatrix(a_matrix.width), None], [b_matrix, -IdentityMatrix(b_matrix.height)]]) a_b_block_matrix = MatrixAB(a_matrix, b_matrix) full_pre_schur_complement = BlockMatrix( [[IdentityMatrix(a_matrix.width), None], [None, pre_schur_complement]]) rhs = BlockVector([upper_rhs, lower_rhs]) if not solution: solution = rhs.CreateVector() solution[:] = 0 residuum = rhs.CreateVector() temp_1 = rhs.CreateVector() full_preconditioned_residuum = rhs.CreateVector() a_preconditioned_residuum = rhs.CreateVector() temp_2 = rhs.CreateVector() temp_2.data = rhs - original_matrix * solution a_preconditioned_residuum.data = full_pre_a * temp_2 residuum.data = a_b_block_matrix * a_preconditioned_residuum \ - rhs + original_matrix * solution temp_1.data = full_pre_schur_complement @ full_b * a_preconditioned_residuum full_preconditioned_residuum.data = temp_1 current_residual_error_squared = InnerProduct(temp_1, residuum) err0 = sqrt(abs(current_residual_error_squared)) errors = [] for iteration in range(max_steps): iteration_timer = Timer("Bramble Pasciak CG Iteration " + str(iteration)) iteration_timer.Start() err = sqrt(abs(current_residual_error_squared)) if print_rates: print("\rit =", iteration, "rel err =", err / err0, "abs err =", err, " " * 20, end="") errors.append(err / err0) if err < tolerance * err0: iteration_timer.Stop() break previous_residual_error_squared = current_residual_error_squared temp_1.data = -original_matrix * full_preconditioned_residuum temp_2.data = -full_pre_a * temp_1 temp_1.data += a_b_block_matrix * temp_2 alpha = previous_residual_error_squared / \ InnerProduct(full_preconditioned_residuum, temp_1) solution.data += alpha * full_preconditioned_residuum residuum.data += (-alpha) * temp_1 a_preconditioned_residuum.data += (-alpha) * temp_2 temp_1.data = full_pre_schur_complement @ full_b * a_preconditioned_residuum current_residual_error_squared = InnerProduct(temp_1, residuum) beta = current_residual_error_squared / previous_residual_error_squared full_preconditioned_residuum *= beta full_preconditioned_residuum.data += temp_1 iteration_timer.Stop() else: print("\nWarning: CG did not converge to TOL") print("") return (solution, errors)
def CG(mat, rhs, pre=None, sol=None, tol=1e-12, maxsteps=100, printrates=True, initialize=True, conjugate=False): """preconditioned conjugate gradient method Parameters ---------- mat : Matrix The left hand side of the equation to solve. The matrix has to be spd or hermitsch. rhs : Vector The right hand side of the equation. pre : Preconditioner If provided the preconditioner is used. sol : Vector Start vector for CG method, if initialize is set False. Gets overwritten by the solution vector. If sol = None then a new vector is created. tol : double Tolerance of the residuum. CG stops if tolerance is reached. maxsteps : int Number of maximal steps for CG. If the maximal number is reached before the tolerance is reached CG stops. printrates : bool If set to True then the error of the iterations is displayed. initialize : bool If set to True then the initial guess for the CG method is set to zero. Otherwise the values of the vector sol, if provided, is used. conjugate : bool If set to True, then the complex inner product is used. Returns ------- (vector) Solution vector of the CG method. """ timer = Timer("CG-Solver") timer.Start() u = sol if sol else rhs.CreateVector() d = rhs.CreateVector() w = rhs.CreateVector() s = rhs.CreateVector() if initialize: u[:] = 0.0 d.data = rhs - mat * u w.data = pre * d if pre else d err0 = sqrt(abs(InnerProduct(d, w))) s.data = w # wdn = InnerProduct (w,d) wdn = w.InnerProduct(d, conjugate=conjugate) if wdn == 0: return u for it in range(maxsteps): w.data = mat * s wd = wdn # as_s = InnerProduct (s, w) as_s = s.InnerProduct(w, conjugate=conjugate) alpha = wd / as_s u.data += alpha * s d.data += (-alpha) * w w.data = pre * d if pre else d # wdn = InnerProduct (w, d) wdn = w.InnerProduct(d, conjugate=conjugate) beta = wdn / wd s *= beta s.data += w err = sqrt(abs(wd)) if printrates: print("it = ", it, " err = ", err) if err < tol * err0: break else: print("Warning: CG did not converge to TOL") timer.Stop() return u
def solve(initial_temperature, end_time, time_step): mesh = Mesh(unit_square.GenerateMesh(maxh=0.1)) Draw(mesh) space = H1(mesh, order=10, dirichlet='bottom|right|top|left') print(space) Draw(initial_temperature, mesh, "initial_temperature") trial_function = space.TrialFunction() test_function = space.TestFunction() diffusion_term = grad(trial_function) * grad(test_function) diffusion = BilinearForm(space) diffusion += diffusion_term * dx mass_term = trial_function * test_function mass = BilinearForm(space) mass += mass_term * dx heat = BilinearForm(space) heat += mass_term * dx + time_step * diffusion_term * dx source_term = 0 * test_function source = LinearForm(space) source += source_term * dx diffusion.Assemble() mass.Assemble() heat.Assemble() source.Assemble() temperature = GridFunction(space, "temperature") temperature.Set(initial_temperature) for i in range(space.ndof): if not space.FreeDofs()[i]: temperature.vec[i] = 0 Draw(temperature) residual = temperature.vec.CreateVector() heat_inverse = heat.mat.Inverse(space.FreeDofs()) subspace_dimension = 5 dt = time_step / subspace_dimension runge_kutta_weights = ImplicitRungeKuttaMethodWeights(10) time = 0 print(f"time={time}") with (TaskManager()): while time < end_time: time += time_step print(f"time={time}") timer = Timer("exponential integrators timer") timer.Start() subspace_basis = [temperature.vec.Copy()] initial_condition_norm = Norm(temperature.vec) subspace_basis_assembling_timer = Timer( "subspace basis assembling") subspace_basis_assembling_timer.Start() for i in range(1, subspace_dimension): residual.data = diffusion.mat * temperature.vec temperature.vec.data -= dt * heat_inverse * residual subspace_basis.append(temperature.vec.Copy()) subspace_basis = orthonormalize(subspace_basis) subspace_basis_assembling_timer.Stop() subspace_matrix_assembling_timer = Timer( "subspace matrix assembling") subspace_matrix_assembling_timer.Start() subspace_diffusion = Matrix(subspace_dimension, subspace_dimension) subspace_mass = Matrix(subspace_dimension, subspace_dimension) for col in range(subspace_dimension): residual.data = diffusion.mat * subspace_basis[col] for row in range(subspace_dimension): subspace_diffusion[row, col] = InnerProduct( subspace_basis[row], residual) residual.data = mass.mat * subspace_basis[col] for row in range(subspace_dimension): subspace_mass[row, col] = InnerProduct(subspace_basis[row], residual) subspace_mass_inverse = Matrix(subspace_dimension, subspace_dimension) subspace_mass.Inverse(subspace_mass_inverse) evolution_matrix = -subspace_mass_inverse * subspace_diffusion subspace_matrix_assembling_timer.Stop() large_timestep_timer = Timer("large timestep") large_timestep_timer.Start() subspace_temperature = Vector(subspace_dimension) subspace_temperature[:] = 0 subspace_temperature[0] = initial_condition_norm next_temperature = linear_implicit_runge_kutta_step( runge_kutta_weights, evolution_matrix, subspace_temperature, time_step) temperature.vec[:] = 0 for i, basis_vector in enumerate(subspace_basis): temperature.vec.data += next_temperature[i] * basis_vector large_timestep_timer.Stop() timer.Stop() Redraw() return (temperature, mesh, time)
def BramblePasciakCG(blfA, blfB, matC, f, g, preA_unscaled, preM, sol=None, tol=1e-6, maxsteps=100, printrates=True, initialize=True, rel_err=True): """preconditioned bramble pasciak conjugate gradient method Parameters ---------- matA : Matrix The left upper matrix of the saddle point matrix. The matrix has to be spd. matB : Matrix The B of the saddle point matrix. The matrix has to be spd. matC : Matrix The lower right of the saddle point matrix. The matrix has to be spd. f : Vector The first component right hand side of the equation. g : Vector The second component right hand side of the equation. preA_unscaled : Preconditioner of matA, preferedly BDDC. preA_unscaled : Preconditioner of matB, arbitrary (i.e. Jacobi). sol : Vector Start vector for CG method, if initialize is set False. Gets overwritten by the solution vector. If sol = None then a new vector is created. tol : double Tolerance of the residuum. BPCG stops if tolerance is reached. maxsteps : int Number of maximal steps for BPCG. If the maximal number is reached before the tolerance is reached CG stops. printrates : bool If set to True then the error of the iterations is displayed. initialize : bool If set to True then the initial guess for the BPCG method is set to zero. Otherwise the values of the vector sol, if provided, is used. conjugate : bool If set to True, then the complex inner product is used. rel_err : bool Whether to use the tolerance relative to the the initial error or not. Returns ------- (vector - call by reference) Solution vector of the BPCG method. (int) Number of iterations the BCGP took """ class myAmatrix(BaseMatrix): def __init__(self, blfA): super(myAmatrix, self).__init__() self.blfA = blfA self.mat = (IdentityMatrix() - blfA.harmonic_extension_trans) @ ( blfA.mat + blfA.inner_matrix) @ (IdentityMatrix() - blfA.harmonic_extension) def Mult(self, x, y): y.data = self.mat * x def Height(self): return self.blfA.mat.height def Width(self): return self.blfA.mat.width def CreateColVector(self): return self.blfA.mat.CreateColVector() def CreateVector(self): return self.blfA.mat.CreateColVector() if blfA.condense: matA = myAmatrix(blfA) else: matA = blfA.mat matB = blfB.mat timer_prep = Timer("BPCG-Preparation") timer_prep.Start() timer_prepev = Timer("BPCG-Preparation-EV") timer_prepev.Start() lams = EigenValues_Preconditioner(mat=matA, pre=preA_unscaled, tol=1e-3) timer_prepev.Stop() # print("min", min(lams), "max", max(lams)) k = 1. / min(lams) + 1e-3 print("condition", max(lams) / min(lams)) # print("scale factor", k) preA = k * preA_unscaled f_new = matA.CreateColVector() # tmp0.data = preA * f #tmp0 = harmonic_extension(f, blfA, preA) tmp0 = f.CreateVector() harmonic_extension(f, blfA, preA, result=tmp0) f_new.data = matA * tmp0 - f g_new = matB.CreateColVector() g_new.data = matB * tmp0 - g rhs = BlockVector([f_new, g_new]) u = sol if sol else rhs.CreateVector() if initialize: u[:] = 0.0 d = rhs.CreateVector() w = rhs.CreateVector() v = rhs.CreateVector() z = rhs.CreateVector() z_old = rhs.CreateVector() s = rhs.CreateVector() # MatOp = BP_Matrices(preA, matA, matB, preM) # MatOp.update(u) tmp0 = blfA.mat.CreateColVector() #tmp1 = preA.CreateColVector() tmp1 = blfA.mat.CreateColVector() tmp2 = blfA.mat.CreateColVector() tmp3 = matB.CreateColVector() tmp4 = tmp1.CreateVector() matA_s0 = blfA.mat.CreateColVector() matB_s1 = matB.CreateRowVector() tmp0.data = matA * u[0] + matB.T * u[1] # tmp1.data = preA * tmp0 harmonic_extension(tmp0, blfA, preA, tmp1) tmp2.data = matA * tmp1 tmp4.data = tmp1 - u[0] tmp3.data = matB * tmp4 # d.data = rhs - MatOp.K * u d[0].data = rhs[0] - (tmp2 - tmp0) d[1].data = rhs[1] - tmp3 pr = rhs.CreateVector() harmonic_extension(f, blfA, preA, pr[0]) # pr[0].data = preA * f tmp5 = matB.CreateColVector() tmp5.data = matB * pr[0] - g pr[1].data = preM * tmp5 # w.data = pr - MatOp.Cinv_K * u w[0].data = pr[0] - tmp1 w[1].data = pr[1] - preM * tmp3 wdn = InnerProduct(w, d) err0 = sqrt(abs(wdn)) print("err0", err0) s.data = w if wdn == 0: return u timer_prep.Stop() timer_its = Timer("BPCG-Iterations") timer_its.Start() matB_tranposed = matB.CreateTranspose() for it in range(maxsteps): if it == 0: matA_s0.data = matA * s[0] z[0].data = matA_s0 # A*w_0_0 else: matA_s0.data = beta * matA_s0 + z_old[0] - alpha * tmp2 matB_s1.data = matB_tranposed * s[1] tmp0.data = matA_s0 + matB_s1 # tmp1.data = preA * tmp0 harmonic_extension(f=tmp0, blfA=blfA, inverse=preA, result=tmp1) tmp2.data = matA * tmp1 tmp4.data = tmp1 - s[0] tmp3.data = matB * tmp4 z_old[0].data = z[0] # w.data = MatOp.K * s v[0].data = tmp2 - tmp0 v[1].data = tmp3 wd = wdn as_s = InnerProduct(s, v) # giving one (A,B) to the other side of the dot product # as_s = InnerProduct(matA_s0, tmp1) - InnerProduct(s[0], tmp0) + InnerProduct(matB_s1, tmp4) alpha = wd / as_s u.data += alpha * s d.data += (-alpha) * v # w.data = w_old + (-alpha) * MatOp.Cinv_K * s w[0].data = w[0] + (-alpha) * tmp1 w[1].data = w[1] + (-alpha) * preM * tmp3 wdn = InnerProduct(w, d) beta = wdn / wd z[0].data -= alpha * tmp2 s *= beta s.data += w err = sqrt(abs(wd)) if printrates: print("it = ", it, " err = ", err, " " * 20) if err < tol * (err0 if rel_err else 1): break else: print("Warning: BPCG did not converge to TOL") timer_its.Stop() print("\n") return (it, timer_its.time)