def inverse_mass_matrix(self): """Return the inverse mass matrix for this space.""" from bempp.api.assembly.discrete_boundary_operator import ( InverseSparseDiscreteBoundaryOperator, ) if self._inverse_mass_matrix is None: self._inverse_mass_matrix = InverseSparseDiscreteBoundaryOperator( self.mass_matrix()) return self._inverse_mass_matrix
def discrete_coefficient_projection(space, new_space): """Discrete operator that projects coefficients from space to new_space.""" from bempp.api.operators.boundary.sparse import identity from bempp.api.assembly.discrete_boundary_operator import \ InverseSparseDiscreteBoundaryOperator ident1 = identity(space, new_space, new_space).weak_form() ident2 = identity(new_space, new_space, new_space).weak_form() return InverseSparseDiscreteBoundaryOperator(ident2) * ident1
def get_inverse_mass_matrix(domain, dual_to_range): """ Quickly get an inverse mass matrix operator. Uses the cached version from a function space if possible. """ from bempp.api.assembly.discrete_boundary_operator import ( InverseSparseDiscreteBoundaryOperator, ) if domain == dual_to_range: return domain.inverse_mass_matrix() else: return InverseSparseDiscreteBoundaryOperator( get_mass_matrix(domain, dual_to_range))
def strong_form(self): """Return a discrete operator that maps into the range space.""" if self._range_map is None: # This is the most frequent case and we cache the mass # matrix from the space object. if self.range == self.dual_to_range: self._range_map = self.dual_to_range.inverse_mass_matrix() else: from bempp.api.assembly.discrete_boundary_operator import ( InverseSparseDiscreteBoundaryOperator, ) from bempp.api.operators.boundary.sparse import identity self._range_map = InverseSparseDiscreteBoundaryOperator( identity(self.range, self.range, self.dual_to_range).weak_form()) return self._range_map * self.weak_form()
def strong_form(self): """Return a discrete operator that maps into the range space. Parameters ---------- recompute : bool Usually the strong form is cached. If this parameter is set to `true` the strong form is recomputed. """ if self._range_map is None: nrows = len(self.range_spaces) _range_ops = _np.empty((nrows, nrows), dtype="O") for index in range(nrows): # This is the most frequent case and we cache the mass # matrix from the space object. if self.range_spaces[index] == self.dual_to_range_spaces[index]: _range_ops[index, index] = self.dual_to_range_spaces[ index ].inverse_mass_matrix() else: from bempp.api.operators.boundary.sparse import identity from bempp.api.assembly.discrete_boundary_operator import ( InverseSparseDiscreteBoundaryOperator, ) _range_ops[index, index] = InverseSparseDiscreteBoundaryOperator( identity( self.range_spaces[index], self.range_spaces[index], self.dual_to_range_spaces[index], ).weak_form() ) self._range_map = BlockedDiscreteOperator(_range_ops) return self._range_map * self.weak_form()
def coefficients(self): """Return coefficient vector.""" if self._coefficients is None: from bempp.api.operators.boundary.sparse import identity from bempp.api.assembly.discrete_boundary_operator import ( InverseSparseDiscreteBoundaryOperator, ) op = InverseSparseDiscreteBoundaryOperator( identity( self.space, self.space, self.dual_space, parameters=self.parameters, ) .weak_form() .A.tocsc() ) self._coefficients = op @ self._projections self._representation = "primal" return self._coefficients
def strong_form(self, recompute=False): """Return a discrete operator that maps into the range space. Parameters ---------- recompute : bool Usually the strong form is cached. If this parameter is set to `true` the strong form is recomputed. """ if recompute is True: self._range_map = None if self._range_map is None: _range_ops = _np.empty((self.ndims[0], self.ndims[1]), dtype='O') for index in range(self.ndims[0]): # This is the most frequent case and we cache the mass # matrix from the space object. if self.range_spaces[index] == \ self.dual_to_range_spaces[index]: _range_ops[index, index] = \ self.dual_to_range_spaces[index].inverse_mass_matrix() else: from bempp.api.operators.boundary.sparse import identity from bempp.api.assembly.discrete_boundary_operator import \ InverseSparseDiscreteBoundaryOperator _range_ops[index, index] = \ InverseSparseDiscreteBoundaryOperator( identity( self.range_spaces[index], self.range_spaces[index], self.dual_to_range_spaces[index]).weak_form()) self._range_map = BlockedDiscreteOperator(_range_ops) return self._range_map * self.weak_form(recompute)
blocked = BlockedDiscreteOperator(np.array(blocks)) # Next, we solve the system, then split the solution into the parts assosiated with u and λ. For an efficient solve, preconditioning is required. # In[28]: from bempp.api.assembly.discrete_boundary_operator import InverseSparseDiscreteBoundaryOperator from scipy.sparse.linalg import LinearOperator # Compute the sparse inverse of the Helmholtz operator # Although it is not a boundary operator we can use # the SparseInverseDiscreteBoundaryOperator function from # BEM++ to turn its LU decomposition into a linear operator. P1 = InverseSparseDiscreteBoundaryOperator( blocked[0,0].A.tocsc()) # For the Laplace slp we use a simple mass matrix preconditioner. # This is sufficient for smaller low-frequency problems. P2 = InverseSparseDiscreteBoundaryOperator( bempp.api.operators.boundary.sparse.identity( bempp_space, bempp_space, bempp_space).weak_form()) # Create a block diagonal preconditioner object using the Scipy LinearOperator class def apply_prec(x): """Apply the block diagonal preconditioner""" m1 = P1.shape[0] m2 = P2.shape[0] n1 = P1.shape[1] n2 = P2.shape[1]