def run(self): sim = self.simulation sim.hooks.simulation_started() sim.hooks.new_timestep(timestep_number=1, t=1.0, dt=1.0) # Assemble system A = dolfin.assemble(self.form_lhs) b = dolfin.assemble(self.form_rhs) # Create Krylov solver solver = linear_solver_from_input(sim, 'solver/phi', 'cg') solver.set_operator(A) # The function where the solution is stored phi = self.simulation.data['phi'] # Remove null space if present (pure Neumann BCs) if self.has_null_space: null_vec = dolfin.Vector(phi.vector()) phi.function_space().dofmap().set(null_vec, 1.0) null_vec *= 1.0 / null_vec.norm("l2") null_space = dolfin.VectorSpaceBasis([null_vec]) dolfin.as_backend_type(A).set_nullspace(null_space) null_space.orthogonalize(b) # Solve the linear system using the default solver (direct LU solver) solver.solve(phi.vector(), b) sim.hooks.end_timestep() sim.hooks.simulation_ended(success=True)
def nullspace(self) -> df.VectorSpaceBasis: if self._nullspace_basis is None: null_vector = df.Vector(self.vur.vector()) self.VUR.sub(1).dofmap().set(null_vector, 1.0) null_vector *= 1.0 / null_vector.norm("l2") self._nullspace_basis = df.VectorSpaceBasis([null_vector]) return self._nullspace_basis
def pressure_correction(self): """ Solve the pressure correction equation We handle the case where only Neumann conditions are given for the pressure by taking out the nullspace, a constant shift of the pressure, by providing the nullspace to the solver """ p = self.simulation.data['p'] p_hat = self.simulation.data['p_hat'] # Temporarily store the old pressure p_hat.vector().zero() p_hat.vector().axpy(-1, p.vector()) # Assemble the A matrix only the first inner iteration if self.inner_iteration == 1: self.Ap = dolfin.as_backend_type(self.eq_pressure.assemble_lhs()) A = self.Ap b = dolfin.as_backend_type(self.eq_pressure.assemble_rhs()) # Inform PETSc about the null space if self.remove_null_space: if self.pressure_null_space is None: # Create vector that spans the null space null_vec = dolfin.Vector(p.vector()) null_vec[:] = 1 null_vec *= 1 / null_vec.norm("l2") # Create null space basis object self.pressure_null_space = dolfin.VectorSpaceBasis([null_vec]) # Make sure the null space is set on the matrix if self.inner_iteration == 1: A.set_nullspace(self.pressure_null_space) # Orthogonalize b with respect to the null space self.pressure_null_space.orthogonalize(b) # Solve for the new pressure correction self.niters_p = self.pressure_solver.inner_solve( A, p.vector(), b, in_iter=self.inner_iteration, co_iter=self.co_inner_iter) # Removing the null space of the matrix system is not strictly the same as removing # the null space of the equation, so we correct for this here if self.remove_null_space: dx2 = dolfin.dx(domain=p.function_space().mesh()) vol = dolfin.assemble(dolfin.Constant(1) * dx2) pavg = dolfin.assemble(p * dx2) / vol p.vector()[:] -= pavg # Calculate p_hat = p_new - p_old p_hat.vector().axpy(1, p.vector()) return p_hat.vector().norm('l2')
def solve_coupled(self): """ Solve the coupled equations """ # Assemble the equation system A = self.eqs.assemble_lhs() b = self.eqs.assemble_rhs() if self.fix_pressure_dof: A.ident(self.pressure_row_to_fix) elif self.remove_null_space: if self.pressure_null_space is None: # Create null space vector in Vp Space null_func = dolfin.Function(self.simulation.data['Vp']) null_vec = null_func.vector() null_vec[:] = 1 null_vec *= 1 / null_vec.norm("l2") # Convert null space vector to coupled space null_func2 = dolfin.Function(self.simulation.data['Vcoupled']) ndim = self.simulation.ndim fa = dolfin.FunctionAssigner(self.subspaces[ndim], self.simulation.data['Vp']) fa.assign(null_func2.sub(ndim), null_func) # Create the null space basis self.pressure_null_space = dolfin.VectorSpaceBasis( [null_func2.vector()]) # Make sure the null space is set on the matrix dolfin.as_backend_type(A).set_nullspace(self.pressure_null_space) # Orthogonalize b with respect to the null space self.pressure_null_space.orthogonalize(b) # Solve the equation system self.simulation.hooks.matrix_ready('Coupled', A, b) self.coupled_solver.solve(A, self.coupled_func.vector(), b) # Assign into the regular (split) functions from the coupled function funcs = [self.simulation.data[name] for name in self.subspace_names] self.assigner.assign(funcs, self.coupled_func) # If we remove the null space of the matrix system this will not be the exact same as # removing the proper null space of the equation, so we fix this here if self.fix_pressure_dof: p = self.simulation.data['p'] dx2 = dolfin.dx(domain=p.function_space().mesh()) vol = dolfin.assemble(dolfin.Constant(1) * dx2) # Perform correction multiple times due to round-of error. The first correction # can be i.e 1e14 while the next correction is around unity pavg = 1e10 while abs(pavg) > 1000: pavg = dolfin.assemble(p * dx2) / vol p.vector()[:] -= pavg
def __init__(self, V, bcs=None, objects=None, circuit=None, remove_null_space=False, eps0=1, linalg_solver='gmres', linalg_precond='hypre_amg'): if bcs == None: bcs = [] if objects == None: objects = [] if not isinstance(bcs, list): bcs = [bcs] if not isinstance(objects, list): objects = [objects] self.V = V self.bcs = bcs self.objects = objects self.circuit = circuit self.remove_null_space = remove_null_space """ One could perhaps identify the cases in which different solvers and preconditioners should be used, and by default choose the best suited for the problem. """ self.solver = df.PETScKrylovSolver(linalg_solver, linalg_precond) self.solver.parameters['absolute_tolerance'] = 1e-14 self.solver.parameters['relative_tolerance'] = 1e-12 self.solver.parameters['maximum_iterations'] = 1000 self.solver.parameters['nonzero_initial_guess'] = True phi = df.TrialFunction(V) phi_ = df.TestFunction(V) self.a = df.Constant(eps0) * df.inner(df.grad(phi), df.grad(phi_)) * df.dx A = df.assemble(self.a) for bc in bcs: bc.apply(A) for o in objects: o.apply(A) if circuit != None: A, = circuit.apply(A) if remove_null_space: phi = df.Function(V) null_vec = df.Vector(phi.vector()) V.dofmap().set(null_vec, 1.0) null_vec *= 1.0 / null_vec.norm("l2") self.null_space = df.VectorSpaceBasis([null_vec]) df.as_backend_type(A).set_nullspace(self.null_space) self.A = A self.phi_ = phi_
def pressure_correction(self, reassemble, last_piso_iter): """ PIMPLE pressure correction """ sim = self.simulation u_star = sim.data['uvw_star'] p_star = sim.data['p'] minus_p_hat = self.simulation.data['p_hat'] # Assemble only once per time step if reassemble: self.E = dolfin.as_backend_type(self.matrices.assemble_E()) # Compute LHS self.AtinvB = matmul(self.A_tilde_inv, self.B, self.AtinvB) self.CAtinvB = matmul(self.C, self.AtinvB, self.CAtinvB) # Needed for RHS self.AtinvA = matmul(self.A_tilde_inv, self.A, self.AtinvA) self.CAtinvA = matmul(self.C, self.AtinvA, self.CAtinvA) # Compute the residual divergence U = u_star.vector() div = self.C * U - self.E div_err = div.norm('l2') # The equation system lhs = self.CAtinvB rhs = div - self.CAtinvA * U + self.C * (self.A_tilde_inv * self.D) if DEBUG_RHS: # Quantify RHS contributions c0 = (self.C * U).norm('l2') c1 = (self.E).norm('l2') c2 = (self.CAtinvA * U).norm('l2') c3 = (self.C * (self.A_tilde_inv * self.D)).norm('l2') cT = max([c0, c1, c2, c3]) sim.log.info( ' Pressure RHS contributions:' ' %5.2f %5.2f %.2e %5.2f %5.2f %.2e' % (c0 / cT, c1 / cT, div_err / cT, c2 / cT, c3 / cT, cT)) # Inform PETSc about the pressure null space if self.remove_null_space: if self.pressure_null_space is None: # Create vector that spans the null space null_vec = dolfin.Vector(p_star.vector()) null_vec[:] = 1 null_vec *= 1 / null_vec.norm("l2") # Create null space basis object self.pressure_null_space = dolfin.VectorSpaceBasis([null_vec]) # Make sure the null space is set on the matrix if self.inner_iteration == 1: lhs.set_nullspace(self.pressure_null_space) # Orthogonalize b with respect to the null space self.pressure_null_space.orthogonalize(rhs) # Solve for the new pressure correction minus_p_hat.assign(p_star) self.niters_p = self.pressure_solver.inner_solve( lhs, p_star.vector(), rhs, in_iter=self.inner_iteration, co_iter=self.co_inner_iter) # Compute change from last iteration minus_p_hat.vector().axpy(-1.0, p_star.vector()) minus_p_hat.vector().apply('insert') # Removing the null space of the matrix system is not strictly the same as removing # the null space of the equation, so we correct for this here if self.remove_null_space: dx2 = dolfin.dx(domain=p_star.function_space().mesh()) vol = dolfin.assemble(dolfin.Constant(1) * dx2) pavg = dolfin.assemble(p_star * dx2) / vol p_star.vector()[:] -= pavg # Explicit relaxation if self.last_inner_iter and last_piso_iter: alpha = sim.input.get_value('solver/relaxation_p_last_iter', ALPHA_P_LAST, 'float') else: alpha = sim.input.get_value('solver/relaxation_p', ALPHA_P, 'float') if alpha != 1: p_star.vector().axpy(1 - alpha, minus_p_hat.vector()) p_star.vector().apply('insert') return minus_p_hat.vector().norm('l2'), div_err
def pressure_correction(self, piso_rhs=False): """ Solve the Navier-Stokes equations on SIMPLE form (Semi-Implicit Method for Pressure-Linked Equations) """ sim = self.simulation p_hat = sim.data['p_hat'] if self.solver_type == SOLVER_SIMPLE: alpha = sim.input.get_value('solver/relaxation_p', ALPHA_P, 'float') else: alpha = 1.0 # Compute the LHS = C⋅Ãinv⋅B if self.inner_iteration == 1: C, Ainv, B = self.C, self.A_tilde_inv, self.B self.mat_AinvB = matmul(Ainv, B, self.mat_AinvB) self.mat_CAinvB = matmul(C, self.mat_AinvB, self.mat_CAinvB) self.LHS_pressure = dolfin.as_backend_type(self.mat_CAinvB.copy()) LHS = self.LHS_pressure # Compute the RHS if not piso_rhs: # Standard SIMPLE pressure correction # Compute the divergence of u* and the rest of the right hand side uvw_star = sim.data['uvw_star'] RHS = self.matrices.assemble_E_star(uvw_star) self.niters_p = 0 else: # PISO pressure correction (the second pressure correction) # Compute the RHS = - C⋅Ãinv⋅(Ãinv - A)⋅û C, Ainv, A = self.C, self.A_tilde_inv, self.A if self.inner_iteration == 1: self.mat_AinvA = matmul(Ainv, A, self.mat_AinvA) self.mat_CAinvA = matmul(C, self.mat_AinvA, self.mat_CAinvA) RHS = self.mat_CAinvA * self.minus_uvw_hat - C * self.minus_uvw_hat # Inform PETSc about the null space if self.remove_null_space: if self.pressure_null_space is None: # Create vector that spans the null space null_vec = dolfin.Vector(p_hat.vector()) null_vec[:] = 1 null_vec *= 1 / null_vec.norm("l2") # Create null space basis object self.pressure_null_space = dolfin.VectorSpaceBasis([null_vec]) # Make sure the null space is set on the matrix if self.inner_iteration == 1: LHS.set_nullspace(self.pressure_null_space) # Orthogonalize b with respect to the null space self.pressure_null_space.orthogonalize(RHS) # Solve for the new pressure correction self.niters_p += self.pressure_solver.inner_solve( LHS, p_hat.vector(), RHS, in_iter=self.inner_iteration, co_iter=self.co_inner_iter, ) # Removing the null space of the matrix system is not strictly the same as removing # the null space of the equation, so we correct for this here if self.remove_null_space: dx2 = dolfin.dx(domain=p_hat.function_space().mesh()) vol = dolfin.assemble(dolfin.Constant(1) * dx2) pavg = dolfin.assemble(p_hat * dx2) / vol p_hat.vector()[:] -= pavg # Calculate p = p* + α p^ sim.data['p'].vector().axpy(alpha, p_hat.vector()) sim.data['p'].vector().apply('insert') return p_hat.vector().norm('l2')
chi_ = df.Function(S) psi = df.TestFunction(S) ds = df.Measure("ds", domain=mesh, subdomain_data=subd) F_chi = (n[0] * psi * ds(1) + df.inner(df.grad(chi), df.grad(psi)) * df.dx + Pe * psi * df.dot(U_, df.grad(chi)) * df.dx + Pe * (U_[0] - df.Constant(1.)) * psi * df.dx) a_chi, L_chi = df.lhs(F_chi), df.rhs(F_chi) solver_chi = df.PETScKrylovSolver("gmres") nullvec = df.Vector(chi_.vector()) S.dofmap().set(nullvec, 1.0) nullvec *= 1.0 / nullvec.norm("l2") nullspace = df.VectorSpaceBasis([nullvec]) problem_chi2 = df.LinearVariationalProblem(a_chi, L_chi, chi_, bcs=[]) solver_chi2 = df.LinearVariationalSolver(problem_chi2) solver_chi2.parameters["krylov_solver"]["absolute_tolerance"] = 1e-15 logPe_arr = np.linspace(args.logPe_min, args.logPe_max, args.logPe_N) if rank == 0: data = np.zeros((len(logPe_arr), 3)) for iPe, logPe in enumerate(logPe_arr): Pe_loc = 10**logPe Pe.assign(Pe_loc)
def pressure_correction(self): """ Solve the Navier-Stokes equations on SIMPLE form (Semi-Implicit Method for Pressure-Linked Equations) """ sim = self.simulation u_star = sim.data['uvw_star'] p_star = sim.data['p'] p_hat = self.simulation.data['p_hat'] # Assemble only once per time step if self.inner_iteration == 1: self.C = assemble_into(self.eqC, self.C) self.Minv = dolfin.as_backend_type(self.compute_M_inverse()) if self.eqE is not None: self.E = assemble_into(self.eqE, self.E) # Compute LHS self.MinvB = matmul(self.Minv, self.B, self.MinvB) self.CMinvB = matmul(self.C, self.MinvB, self.CMinvB) # The equation system lhs = self.CMinvB rhs = self.CMinvB * p_star.vector() rhs.axpy(1, self.C * u_star.vector()) if self.eqE is not None: rhs.axpy(-1, self.E) rhs.apply('insert') # Inform PETSc about the pressure null space if self.remove_null_space: if self.pressure_null_space is None: # Create vector that spans the null space null_vec = dolfin.Vector(p_star.vector()) null_vec[:] = 1 null_vec *= 1 / null_vec.norm("l2") # Create null space basis object self.pressure_null_space = dolfin.VectorSpaceBasis([null_vec]) # Make sure the null space is set on the matrix if self.inner_iteration == 1: lhs.set_nullspace(self.pressure_null_space) # Orthogonalize b with respect to the null space self.pressure_null_space.orthogonalize(rhs) # Temporarily store the old pressure p_hat.vector().zero() p_hat.vector().axpy(-1, p_star.vector()) # Solve for the new pressure correction self.niters_p = self.pressure_solver.inner_solve( lhs, p_star.vector(), rhs, in_iter=self.inner_iteration, co_iter=self.co_inner_iter) # Removing the null space of the matrix system is not strictly the same as removing # the null space of the equation, so we correct for this here if self.remove_null_space: dx2 = dolfin.dx(domain=p_star.function_space().mesh()) vol = dolfin.assemble(dolfin.Constant(1) * dx2) pavg = dolfin.assemble(p_star * dx2) / vol p_star.vector()[:] -= pavg # Calculate p_hat = p_new - p_old p_hat.vector().axpy(1, p_star.vector()) return p_hat.vector().norm('l2')