def timed_fun(*args, **kwargs): """Time an operation.""" from bempp.api import log start_time = _time.time() res = fun(*args, **kwargs) end_time = _time.time() log(fun.__qualname__ + " : {0:.3e}s".format(end_time - start_time), "timing") return res
def finalize(self): """Finalize the grid creation and return a grid object.""" from bempp.api.grid.grid import Grid from bempp.api import log grid = Grid(self._impl.finalize()) log("Created grid with %i elements, %i nodes and %i edges." % ( grid.leaf_view.entity_count(0), grid.leaf_view.entity_count(2), grid.leaf_view.entity_count(1))) return grid
def timed_fun(*args, **kwargs): """The actual timer function.""" from bempp.api import GLOBAL_PARAMETERS from bempp.api import log if not GLOBAL_PARAMETERS.verbosity.extended_verbosity: return fun(*args, **kwargs) start_time = _time.time() res = fun(*args, **kwargs) end_time = _time.time() log(message + " : {0:.3e}s".format(end_time - start_time)) return res
def __call__(self, x): from bempp.api import log self._count += 1 if self._store_residuals: if self._iteration_is_cg: res = self._rhs - self._operator * x else: res = x self._residuals.append(_np.linalg.norm(res)) log(f"GMRES Iteration {self._count} with residual {self._residuals[-1]}" ) else: log(f"GMRES Iteration {self._count}")
def p0_discontinuous_function_space( grid, support_elements=None, segments=None, swapped_normals=None, include_boundary_dofs=None, truncate_at_segment_edge=None, ): """Define a space of piecewise constant functions.""" from .space import SpaceBuilder, _process_segments if include_boundary_dofs is not None: log("Setting include_boundary_dofs has no effect on this space type.", "warning") if truncate_at_segment_edge is not None: log( "Setting truncate_at_segment_edge has no effect on this space type.", "warning", ) support, normal_multipliers = _process_segments(grid, support_elements, segments, swapped_normals) elements_in_support = _np.flatnonzero(support) support_size = len(elements_in_support) local2global = _np.zeros((grid.number_of_elements, 1), dtype="uint32") local2global[support] = _np.expand_dims( _np.arange(support_size, dtype="uint32"), 1) local_multipliers = _np.zeros((grid.number_of_elements, 1), dtype="float64") local_multipliers[support] = 1 collocation_points = _np.array([[1.0 / 3], [1.0 / 3]]) return (SpaceBuilder(grid).set_codomain_dimension(1).set_support( support).set_normal_multipliers(normal_multipliers).set_order( 0).set_is_localised(True).set_shapeset("p0_discontinuous"). set_identifier("p0_discontinuous").set_local2global(local2global). set_local_multipliers(local_multipliers).set_collocation_points( collocation_points).set_barycentric_representation( p0_barycentric_discontinuous_function_space). set_numba_surface_gradient(_numba_p0_surface_gradient).build())
def get_operator_with_space_preprocessing(op_fun, domain, range_, dual_to_range, label, symmetry, parameters, use_projection_spaces=True, assemble_only_singular_part=False): """Assemble operator after preprocessing of spaces.""" import bempp.api from bempp.api.space import rewrite_operator_spaces from bempp.api.space import project_operator if parameters is None: parameters = bempp.api.global_parameters if parameters.assembly.boundary_operator_assembly_type == 'dense': from bempp.api import log use_projection_spaces = False log("Projection space mode disabled for dense assembly.") if check_for_non_barycentric_spaces(domain, dual_to_range): return rewrite_operator_spaces( get_operator_with_space_preprocessing( op_fun, domain.non_barycentric_space, range_, dual_to_range.non_barycentric_space, label, symmetry, parameters, use_projection_spaces, assemble_only_singular_part), domain, range_, dual_to_range) if not use_projection_spaces: return op_fun(domain, range_, dual_to_range, label, symmetry, parameters, assemble_only_singular_part) else: operator = op_fun(domain.super_space, range_, dual_to_range.super_space, label, symmetry, parameters, assemble_only_singular_part) return project_operator(operator, domain=domain, range_=range_, dual_to_range=dual_to_range)
def _grid_scatter_worker(grid_id, array_proxies): """Assign a new grid on the worker.""" from bempp.api.utils import pool from bempp.api.grid.grid import Grid from bempp.api import log vertices, elements, domain_indices = pool.from_buffer(array_proxies) if not pool.has_key(grid_id): pool.insert_data( grid_id, Grid(vertices.copy(), elements.copy(), domain_indices.copy(), grid_id), ) log(f"Copied grid with id {grid_id} to worker {pool.get_id()}", "debug") else: log(f"Use cached grid with id {grid_id} on worker {pool.get_id()}", "debug")
def regular_sphere(n): """ Return a regular sphere. Parameters ---------- n : int Refinement level of the sphere. """ from bempp.core.grid import grid_from_sphere from bempp.api.grid.grid import Grid from bempp.api import log grid = Grid(grid_from_sphere(n)) log("Created grid with %i elements, %i nodes and %i edges." % (grid.leaf_view.entity_count(0), grid.leaf_view.entity_count(2), grid.leaf_view.entity_count(1))) return grid
def dual1_function_space( grid, support_elements=None, segments=None, swapped_normals=None, include_boundary_dofs=None, truncate_at_segment_edge=False, ): """Define a space of DP1 functions on the dual grid.""" from .space import SpaceBuilder, invert_local2global from scipy.sparse import coo_matrix if include_boundary_dofs is not None: log("Setting include_boundary_dofs has no effect on this space type.", "warning") coarse_space = p0_discontinuous_function_space(grid, support_elements, segments, swapped_normals) coarse_support = _np.zeros(grid.number_of_elements, dtype=_np.bool) coarse_support[coarse_space.support_elements] = True nentries = 0 for global_dof_index in range(coarse_space.global_dof_count): local_dofs = coarse_space.global2local[global_dof_index] element_index = local_dofs[0][0] # 1 at barycentre of the triangle nentries += 6 # 1/2 at the centre of each edge for e in range(3): edge = coarse_space.grid.element_edges[e][element_index] for neighbour in coarse_space.grid.edge_neighbors[edge]: if not truncate_at_segment_edge: coarse_support[neighbour] = True if coarse_support[neighbour]: nentries += 2 # 1/num_coarse_triangles_at_vertex at each vertex for v in range(3): vertex = coarse_space.grid.elements[v][element_index] start = coarse_space.grid.vertex_neighbors.indexptr[vertex] end = coarse_space.grid.vertex_neighbors.indexptr[vertex + 1] neighbours = coarse_space.grid.vertex_neighbors.indices[start:end] for neighbour in neighbours: if not truncate_at_segment_edge: coarse_support[neighbour] = True if coarse_support[neighbour]: nentries += 2 support_elements = [i for i, j in enumerate(coarse_support) if j] support_numbers = {j: i for i, j in enumerate(support_elements)} coarse_dofs = _np.zeros(nentries, dtype=_np.uint32) bary_dofs = _np.zeros(nentries, dtype=_np.uint32) values = _np.zeros(nentries, dtype=_np.float64) count = 0 for global_dof_index in range(coarse_space.global_dof_count): local_dofs = coarse_space.global2local[global_dof_index] element_index = local_dofs[0][0] # 1 at barycentre of the triangle if coarse_support[element_index]: face_n = support_numbers[element_index] for n in [1, 5, 7, 11, 13, 17]: bary_dofs[count] = 6 * 3 * face_n + n coarse_dofs[count] = global_dof_index values[count] = 1 count += 1 # 1/2 at the centre of each edge for e in range(3): edge = coarse_space.grid.element_edges[e][element_index] for neighbour in coarse_space.grid.edge_neighbors[edge]: if coarse_support[neighbour]: face_n = support_numbers[neighbour] for i, dofs in enumerate([[1, 5], [13, 17], [7, 11]]): if coarse_space.grid.element_edges[i][ neighbour] == edge: for n in dofs: bary_dofs[count] = 6 * 3 * face_n + n coarse_dofs[count] = global_dof_index values[count] = 0.5 count += 1 break # 1/num_coarse_triangles_at_vertex at each vertex for v in range(3): vertex = coarse_space.grid.elements[v][element_index] start = coarse_space.grid.vertex_neighbors.indexptr[vertex] end = coarse_space.grid.vertex_neighbors.indexptr[vertex + 1] neighbour_count = end - start neighbours = coarse_space.grid.vertex_neighbors.indices[start:end] for neighbour in neighbours: if coarse_support[neighbour]: face_n = support_numbers[neighbour] for i, dofs in enumerate([[0, 15], [3, 6], [9, 12]]): if coarse_space.grid.elements[i][neighbour] == vertex: for n in dofs: bary_dofs[count] = 6 * 3 * face_n + n coarse_dofs[count] = global_dof_index values[count] = 1 / neighbour_count count += 1 break bary_grid = grid.barycentric_refinement number_of_support_elements = len(support_elements) coarse_elements = _np.array(support_elements, dtype=_np.uint32) bary_support_elements = 6 * _np.repeat(coarse_elements, 6) + _np.tile( _np.arange(6), number_of_support_elements) bary_support_size = len(bary_support_elements) support = _np.zeros(bary_grid.number_of_elements, dtype=_np.bool) support[bary_support_elements] = True local2global = _np.zeros((bary_grid.number_of_elements, 3), dtype="uint32") local_multipliers = _np.zeros((bary_grid.number_of_elements, 3), dtype="uint32") local2global[support] = _np.arange(3 * bary_support_size).reshape( bary_support_size, 3) local_multipliers[support] = 1 global2local = invert_local2global(local2global, local_multipliers) dof_transformation = coo_matrix( (values, (bary_dofs, coarse_dofs)), shape=(3 * bary_support_size, coarse_space.global_dof_count), dtype=_np.float64, ).tocsr() return (SpaceBuilder(bary_grid).set_codomain_dimension(1).set_support( support).set_order(1).set_is_localised(True).set_is_barycentric( True).set_shapeset("p1_discontinuous").set_identifier( "p1_discontinuous").set_local2global(local2global). set_global2local(global2local).set_local_multipliers( local_multipliers).set_dof_transformation(dof_transformation). set_numba_surface_gradient(_numba_p1_surface_gradient).build())
def __init__(self, vertices, elements, domain_indices=None, grid_id=None, scatter=True): """Create a grid from a vertices and an elements array.""" from bempp.api import log from bempp.api.utils import pool from bempp.api.utils.helpers import create_unique_id self._vertices = None self._elements = None self._domain_indices = None self._edges = None self._element_edges = None self._edge_adjacency = None self._vertex_adjacency = None self._element_neighbors = None self._vertex_on_boundary = None self._edge_on_boundary = None self._edge_neighbors = None self._vertex_neighbors = None self._barycentric_grid = None if grid_id: self._id = grid_id else: self._id = create_unique_id() self._volumes = None self._normals = None self._jacobians = None self._jacobian_inverse_transposed = None self._diameters = None self._integration_elements = None self._centroids = None self._device_interfaces = {} self._element_to_vertex_matrix = None self._element_to_element_matrix = None self._normalize_and_assign_input(vertices, elements, domain_indices) self._enumerate_edges() self._get_element_adjacency_for_edges_and_vertices() self._compute_geometric_quantities() self._compute_boundary_information() self._compute_edge_neighbors() self._compute_vertex_neighbors() self._grid_data = GridData(self._vertices, self._elements, self._edges, self._element_edges, self._volumes, self._normals, self._jacobians, self._jacobian_inverse_transposed, self._diameters, self._integration_elements, self._centroids, self._domain_indices, self._vertex_on_boundary) self._is_scattered = False if scatter and pool.is_initialised() and not pool.is_worker(): self._scatter() if not pool.is_worker(): log(( f"Created grid with id {self.id}. Elements: {self.number_of_elements}. " + f"Edges: {self.number_of_edges}. Vertices: {self.number_of_vertices}" ))