def __init__( self, source_points, target_points, mode, wavenumber=None, depth=4, expansion_order=5, ncrit=400, precision="double", singular_correction=None, ): """Instantiate an Exafmm session.""" import bempp.api import os from bempp.api.utils.helpers import create_unique_id global FMM_TMP_DIR if FMM_TMP_DIR is None: FMM_TMP_DIR = os.path.join(os.getcwd(), ".exafmm") if not os.path.isdir(FMM_TMP_DIR): try: os.mkdir(FMM_TMP_DIR) except: raise FileExistsError( f"A file with the name {FMM_TMP_DIR} exists. Please delete it." ) for _ in range(10): tmp_name = create_unique_id() + ".tmp" fname = os.path.join(FMM_TMP_DIR, tmp_name) if not os.path.exists(fname): break else: raise FileExistsError( "Could not create temporary filename for Exafmm.") self._fname = fname self._singular_correction = singular_correction self._source_points = source_points self._target_points = target_points self._mode = mode if mode == "laplace": self._kernel_parameters = _np.array([], dtype="float64") elif mode == "helmholtz": self._kernel_parameters = _np.array( [_np.real(wavenumber), _np.imag(wavenumber)], dtype="float64") elif mode == "modified_helmholtz": self._kernel_parameters = _np.array([wavenumber], dtype="float64") with bempp.api.Timer(message="Initialising Exafmm."): if mode == "laplace": import exafmm.laplace self._module = exafmm.laplace sources = exafmm.laplace.init_sources( source_points, _np.zeros(len(source_points), dtype=_np.float64)) targets = exafmm.laplace.init_targets(target_points) self._fmm = exafmm.laplace.LaplaceFmm(expansion_order, ncrit, filename=fname) self._tree = exafmm.laplace.setup(sources, targets, self._fmm) elif mode == "helmholtz": import exafmm.helmholtz self._module = exafmm.helmholtz sources = exafmm.helmholtz.init_sources( source_points, _np.zeros(len(source_points), dtype=_np.float64)) targets = exafmm.helmholtz.init_targets(target_points) self._fmm = exafmm.helmholtz.HelmholtzFmm(expansion_order, ncrit, wavenumber, filename=fname) self._tree = exafmm.helmholtz.setup(sources, targets, self._fmm) elif mode == "modified_helmholtz": import exafmm.modified_helmholtz self._module = exafmm.modified_helmholtz sources = exafmm.modified_helmholtz.init_sources( source_points, _np.zeros(len(source_points), dtype=_np.float64)) targets = exafmm.modified_helmholtz.init_targets(target_points) self._fmm = exafmm.modified_helmholtz.ModifiedHelmholtzFmm( expansion_order, ncrit, wavenumber, filename=fname) self._tree = exafmm.modified_helmholtz.setup( sources, targets, self._fmm)
def __init__( self, grid, codomain_dimension, order, shapeset, local2global_map, global2local_map, local_multipliers, identifier, is_localised, support, normal_multipliers, requires_dof_transformation, is_barycentric, barycentric_representation, dof_transformation, numba_evaluator, numba_surface_gradient, collocation_points, ): """Initialize the space.""" from .shapesets import Shapeset from scipy.sparse import coo_matrix from bempp.api.utils.helpers import create_unique_id self._grid = grid self._grid_id = self._grid.id self._codomain_dimension = codomain_dimension self._order = order self._shapeset = Shapeset(shapeset) self._local2global_map = local2global_map self._global2local_map = global2local_map self._local_multipliers = local_multipliers self._identifier = identifier self._support = support self._requires_dof_transformation = requires_dof_transformation self._is_barycentric = is_barycentric self._barycentric_representation = barycentric_representation self._dof_transformation = dof_transformation self._numba_evaluate = numba_evaluator self._numba_surface_gradient = numba_surface_gradient self._normal_multipliers = normal_multipliers self._number_of_support_elements = _np.count_nonzero(self._support) self._support_elements = _np.flatnonzero( self._support).astype("uint32") self._collocation_points = collocation_points self._id = create_unique_id() self._hash_string = None self._color_map = None self._mass_matrix = None self._inverse_mass_matrix = None self._sorted_indices = None self._indexptr = None self._is_scattered = False # Number of dofs for the space defined over the grid # This is different from the global_dof_count, which # takes the dof transformation matrix into account. number_of_grid_dofs = 1 + _np.max(self._local2global_map) self._grid_dof_count = number_of_grid_dofs # Number of shape functions nshape_fun = self.number_of_shape_functions # Generate map to localised space self._map_to_localised_space = coo_matrix( ( self._local_multipliers[self._support].ravel(), ( _np.arange(nshape_fun * self._number_of_support_elements), self._local2global_map[self._support].ravel(), ), ), shape=(nshape_fun * self._number_of_support_elements, self._grid_dof_count), dtype="float64", ).tocsr() # Generate map to full grid # This works like the map to the localised grid. # But if the space is defined over a subgrid it # maps to the localised space on the full grid. self._map_to_full_grid = coo_matrix( ( self._local_multipliers[self._support].ravel(), ( nshape_fun * _np.repeat(self._support_elements, nshape_fun) + _np.tile(_np.arange(nshape_fun), self._number_of_support_elements), self._local2global_map[self._support].ravel(), ), ), shape=(nshape_fun * self._grid.number_of_elements, number_of_grid_dofs), dtype="float64", ).tocsr() # Create localised space. # First check if space is already a localised space. # For this we need that all multipliers are 1 and all global # dofs are each only associated with one local dof. if is_localised: self._localised_space = self else: # Create a new localised space self._localised_space = make_localised_space(self)
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}" ))