def project_vector_field(self, vel_split, vel, name): """ Project the initial conditions to remove any divergence """ sim = self.simulation sim.log.info('Projecting %s to remove divergence' % name) p = sim.data['p'].copy() p.vector().zero() self.assigner_merge.assign(vel, list(vel_split)) def mk_rhs(): rhs = self.C * vel.vector() # If there is no flux (Dirichlet type) across the boundaries then e is None if self.eqE is not None: self.E = assemble_into(self.eqE, self.E) rhs.axpy(-1.0, self.E) rhs.apply('insert') return rhs # Assemble RHS sim.log.info(' Assembling projection matrices') self.B = assemble_into(self.eqB, self.B) self.C = assemble_into(self.eqC, self.C) MinvB = matmul(self.M_unscaled_inv, self.B) rhs = mk_rhs() # Check if projection is needed norm_before = rhs.norm('l2') sim.log.info(' Divergence norm before %.6e' % norm_before) if norm_before < 1e-15: sim.log.info(' Skipping this one, there is no divergence') return # Assemble LHS CMinvB = matmul(self.C, MinvB) lhs = CMinvB sim.log.info(' Solving elliptic problem') # niter = self.pressure_solver.inner_solve(lhs, p.vector(), rhs, 0, 0) niter = dolfin.solve(lhs, p.vector(), rhs) vel.vector().axpy(-1.0, MinvB * p.vector()) vel.vector().apply('insert') self.assigner_split.assign(list(vel_split), vel) for d in range(sim.ndim): sim.data['u'][d].assign(vel_split[d]) self.velocity_postprocessor.run() for d in range(sim.ndim): vel_split[d].assign(sim.data['u'][d]) self.assigner_merge.assign(vel, list(vel_split)) rhs = mk_rhs() norm_after = rhs.norm('l2') sim.log.info(' Done in %d iterations' % niter) sim.log.info(' Divergence norm after %.6e' % norm_after)
def test_matmul(use_block_matrix): indices = numpy.array([1, 2, 4], numpy.intc) blockA = numpy.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], float) blockB = numpy.identity(3, float) A = mk_mat(block=use_block_matrix) B = mk_mat(block=use_block_matrix) A.set(blockA, indices, indices) B.set(blockB, indices, indices) A.apply('insert') B.apply('insert') dolfin.parameters['linear_algebra_backend'] = 'PETSc' A = dolfin.as_backend_type(A) B = dolfin.as_backend_type(B) print('A:\n', A.array()) print('B:\n', B.array()) C = matmul(A, B) print('C:\n', C.array()) assert A.rank() == B.rank() == C.rank() assert A.size(0) == B.size(0) == C.size(0) assert A.size(1) == B.size(1) == C.size(1) Carr = C.array() Cnpy = numpy.dot(A.array(), B.array()) assert (abs(Carr - Cnpy) < 1e-10).all()
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')
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')