def make_same_mesh_connection(to_discr, from_discr): from meshmode.discretization.connection import ( InterpolationBatch, DiscretizationConnectionElementGroup, DirectDiscretizationConnection) if from_discr.mesh is not to_discr.mesh: raise ValueError("from_discr and to_discr must be based on " "the same mesh") assert to_discr.cl_context == from_discr.cl_context with cl.CommandQueue(to_discr.cl_context) as queue: groups = [] for igrp, (fgrp, tgrp) in enumerate(zip(from_discr.groups, to_discr.groups)): all_elements = cl.array.arange(queue, fgrp.nelements, dtype=np.intp).with_queue(None) ibatch = InterpolationBatch(from_group_index=igrp, from_element_indices=all_elements, to_element_indices=all_elements, result_unit_nodes=tgrp.unit_nodes, to_element_face=None) groups.append(DiscretizationConnectionElementGroup([ibatch])) return DirectDiscretizationConnection(from_discr, to_discr, groups, is_surjective=True)
def make_opposite_face_connection(volume_to_bdry_conn): """Given a boundary restriction connection *volume_to_bdry_conn*, return a :class:`DirectDiscretizationConnection` that performs data exchange across opposite faces. """ vol_discr = volume_to_bdry_conn.from_discr vol_mesh = vol_discr.mesh bdry_discr = volume_to_bdry_conn.to_discr # make sure we were handed a volume-to-boundary connection for i_tgrp, conn_grp in enumerate(volume_to_bdry_conn.groups): for batch in conn_grp.batches: assert batch.from_group_index == i_tgrp assert batch.to_element_face is not None ngrps = len(volume_to_bdry_conn.groups) assert ngrps == len(vol_discr.groups) assert ngrps == len(bdry_discr.groups) # One interpolation batch in this connection corresponds # to a key (i_tgt_grp,) (i_src_grp, i_face_tgt,) with cl.CommandQueue(vol_discr.cl_context) as queue: # a list of batches for each group groups = [[] for i_tgt_grp in range(ngrps)] for i_src_grp in range(ngrps): src_grp_el_lookup = _make_el_lookup_table(queue, volume_to_bdry_conn, i_src_grp) for i_tgt_grp in range(ngrps): vbc_tgt_grp_batches = volume_to_bdry_conn.groups[ i_tgt_grp].batches adj_grp = vol_mesh.facial_adjacency_groups[i_tgt_grp][ i_src_grp] for i_face_tgt in range(vol_mesh.groups[i_tgt_grp].nfaces): vbc_tgt_grp_face_batch = _find_ibatch_for_face( vbc_tgt_grp_batches, i_face_tgt) groups[i_tgt_grp].extend( _make_cross_face_batches(queue, vol_discr, bdry_discr, i_tgt_grp, i_src_grp, i_face_tgt, adj_grp, vbc_tgt_grp_face_batch, src_grp_el_lookup)) from meshmode.discretization.connection import ( DirectDiscretizationConnection, DiscretizationConnectionElementGroup) return DirectDiscretizationConnection( from_discr=bdry_discr, to_discr=bdry_discr, groups=[ DiscretizationConnectionElementGroup(batches=batches) for batches in groups ], is_surjective=True)
def make_refinement_connection(actx, refiner, coarse_discr, group_factory): """Return a :class:`meshmode.discretization.connection.DiscretizationConnection` connecting `coarse_discr` to a discretization on the fine mesh. :arg refiner: An instance of :class:`meshmode.mesh.refinement.Refiner` :arg coarse_discr: An instance of :class:`meshmode.discretization.Discretization` associated with the mesh given to the refiner :arg group_factory: An instance of :class:`meshmode.discretization.poly_element.ElementGroupFactory`. Used for discretizing the fine mesh. """ from meshmode.discretization.connection import ( DiscretizationConnectionElementGroup, DirectDiscretizationConnection) coarse_mesh = refiner.get_previous_mesh() fine_mesh = refiner.get_current_mesh() if coarse_discr.mesh != coarse_mesh: raise ValueError( "coarse_discr does not live on the same mesh given to the refiner") from meshmode.discretization import Discretization fine_discr = Discretization( actx, fine_mesh, group_factory, real_dtype=coarse_discr.real_dtype) groups = [] for group_idx, (coarse_discr_group, fine_discr_group, record) in \ enumerate(zip(coarse_discr.groups, fine_discr.groups, refiner.group_refinement_records)): groups.append( DiscretizationConnectionElementGroup( list(_build_interpolation_batches_for_group( actx, group_idx, coarse_discr_group, fine_discr_group, record)))) return DirectDiscretizationConnection( from_discr=coarse_discr, to_discr=fine_discr, groups=groups, is_surjective=True)
def _build_boundary_connection(queue, vol_discr, bdry_discr, connection_data, per_face_groups): from meshmode.discretization.connection import ( InterpolationBatch, DiscretizationConnectionElementGroup, DirectDiscretizationConnection) ibdry_grp = 0 batches = [] connection_groups = [] for igrp, vol_grp in enumerate(vol_discr.groups): mgrp = vol_grp.mesh_el_group for face_id in range(mgrp.nfaces): bdry_grp = bdry_discr.groups[ibdry_grp] data = connection_data[igrp, face_id] bdry_unit_nodes_01 = (bdry_grp.unit_nodes + 1) * 0.5 result_unit_nodes = (np.dot(data.A, bdry_unit_nodes_01).T + data.b).T batches.append( InterpolationBatch( from_group_index=igrp, from_element_indices=cl.array.to_device( queue, data.group_source_element_indices).with_queue(None), to_element_indices=cl.array.to_device( queue, data.group_target_element_indices).with_queue(None), result_unit_nodes=result_unit_nodes, to_element_face=face_id)) is_last_face = face_id + 1 == mgrp.nfaces if per_face_groups or is_last_face: connection_groups.append( DiscretizationConnectionElementGroup(batches)) batches = [] ibdry_grp += 1 assert ibdry_grp == len(bdry_discr.groups) return DirectDiscretizationConnection(vol_discr, bdry_discr, connection_groups, is_surjective=True)
def make_face_to_all_faces_embedding(faces_connection, all_faces_discr, from_discr=None): """Return a :class:`meshmode.discretization.connection.DiscretizationConnection` connecting a discretization containing some faces of a discretization to one containing all faces. :arg faces_connection: must be the (connection) result of calling :func:`meshmode.discretization.connection.make_face_restriction` with :class:`meshmode.discretization.connection.FACE_RESTR_INTERIOR` or a boundary tag. :arg all_faces_discr: must be the (discretization) result of calling :func:`meshmode.discretization.connection.make_face_restriction` with :class:`meshmode.discretization.connection.FACE_RESTR_ALL` for the same volume discretization as the one from which *faces_discr* was obtained. :arg from_discr: Allows substituting in a different origin discretization for the returned connection. This discretization must use the same mesh as ``faces_connection.to_discr``. """ vol_discr = faces_connection.from_discr faces_discr = faces_connection.to_discr if from_discr is None: from_discr = faces_discr assert from_discr.mesh is faces_discr.mesh per_face_groups = (len(vol_discr.groups) != len(faces_discr.groups)) if len(faces_discr.groups) != len(all_faces_discr.groups): raise ValueError("faces_discr and all_faces_discr must have the " "same number of groups") if len(faces_connection.groups) != len(all_faces_discr.groups): raise ValueError("faces_connection and all_faces_discr must have the " "same number of groups") from meshmode.discretization.connection import ( DirectDiscretizationConnection, DiscretizationConnectionElementGroup, InterpolationBatch) i_faces_grp = 0 with cl.CommandQueue(vol_discr.cl_context) as queue: groups = [] for ivol_grp, vol_grp in enumerate(vol_discr.groups): batches = [] nfaces = vol_grp.mesh_el_group.nfaces for iface in range(nfaces): all_faces_grp = all_faces_discr.groups[i_faces_grp] if per_face_groups: assert len( faces_connection.groups[i_faces_grp].batches) == 1 else: assert (len( faces_connection.groups[i_faces_grp].batches) == nfaces ) assert np.array_equal( from_discr.groups[i_faces_grp].unit_nodes, all_faces_grp.unit_nodes) # {{{ find src_batch src_batches = faces_connection.groups[i_faces_grp].batches if per_face_groups: src_batch, = src_batches else: src_batch = src_batches[iface] del src_batches # }}} if per_face_groups: to_element_indices = src_batch.from_element_indices else: assert all_faces_grp.nelements == nfaces * vol_grp.nelements to_element_indices = ( vol_grp.nelements * iface + src_batch.from_element_indices.with_queue(queue) ).with_queue(None) batches.append( InterpolationBatch( from_group_index=i_faces_grp, from_element_indices=src_batch.to_element_indices, to_element_indices=to_element_indices, result_unit_nodes=all_faces_grp.unit_nodes, to_element_face=None)) is_last_face = iface + 1 == nfaces if per_face_groups or is_last_face: groups.append( DiscretizationConnectionElementGroup(batches=batches)) batches = [] i_faces_grp += 1 return DirectDiscretizationConnection(from_discr, all_faces_discr, groups, is_surjective=False)
def make_partition_connection(local_bdry_conn, i_local_part, remote_bdry, remote_adj_groups, remote_from_elem_faces, remote_from_elem_indices): """ Connects ``local_bdry_conn`` to a neighboring partition. :arg local_bdry_conn: A :class:`DiscretizationConnection` of the local partition. :arg i_local_part: The partition number of the local partition. :arg remote_adj_groups: A list of :class:`InterPartitionAdjacency`` of the remote partition. :arg remote_bdry: A :class:`Discretization` of the boundary of the remote partition. :arg remote_from_elem_faces: `remote_from_elem_faces[igrp][idx]` gives the face that batch `idx` interpolates from in group `igrp`. :arg remote_from_elem_indices: `remote_from_elem_indices[igrp][idx]` gives a :class:`np.array` of element indices that batch `idx` interpolates from in group `igrp`. :returns: A :class:`DirectDiscretizationConnection` that performs data exchange across faces from the remote partition to partition `i_local_part`. .. versionadded:: 2017.1 .. warning:: Interface is not final. """ from meshmode.mesh.processing import find_group_indices from meshmode.discretization.connection import ( DirectDiscretizationConnection, DiscretizationConnectionElementGroup) local_bdry = local_bdry_conn.to_discr local_groups = local_bdry_conn.from_discr.mesh.groups part_batches = [[] for _ in local_groups] with cl.CommandQueue(local_bdry_conn.cl_context) as queue: for i_remote_grp, adj in enumerate(remote_adj_groups): indices = (i_local_part == adj.neighbor_partitions) if not np.any(indices): # Skip because i_remote_grp is not connected to i_local_part. continue i_remote_faces = adj.element_faces[indices] i_local_meshwide_elems = adj.global_neighbors[indices] i_local_faces = adj.neighbor_faces[indices] i_local_grps = find_group_indices(local_groups, i_local_meshwide_elems) for i_local_grp in np.unique(i_local_grps): elem_base = local_groups[i_local_grp].element_nr_base local_el_lookup = _make_bdry_el_lookup_table( queue, local_bdry_conn, i_local_grp) for i_remote_face in i_remote_faces: index_flags = np.logical_and( i_local_grps == i_local_grp, i_remote_faces == i_remote_face) if not np.any(index_flags): continue remote_bdry_indices = None for idxs, face in zip( remote_from_elem_indices[i_remote_grp], remote_from_elem_faces[i_remote_grp]): if face == i_remote_face: remote_bdry_indices = idxs break assert remote_bdry_indices is not None elems = i_local_meshwide_elems[index_flags] - elem_base faces = i_local_faces[index_flags] local_bdry_indices = local_el_lookup[elems, faces] batches = _make_cross_face_batches( queue, local_bdry, remote_bdry, i_local_grp, i_remote_grp, local_bdry_indices, remote_bdry_indices) part_batches[i_local_grp].extend(batches) return DirectDiscretizationConnection( from_discr=remote_bdry, to_discr=local_bdry, groups=[ DiscretizationConnectionElementGroup(batches=grp_batches) for grp_batches in part_batches ], is_surjective=True)
def make_opposite_face_connection(volume_to_bdry_conn): """Given a boundary restriction connection *volume_to_bdry_conn*, return a :class:`DirectDiscretizationConnection` that performs data exchange across opposite faces. """ vol_discr = volume_to_bdry_conn.from_discr vol_mesh = vol_discr.mesh bdry_discr = volume_to_bdry_conn.to_discr # make sure we were handed a volume-to-boundary connection for i_tgrp, conn_grp in enumerate(volume_to_bdry_conn.groups): for batch in conn_grp.batches: assert batch.from_group_index == i_tgrp assert batch.to_element_face is not None ngrps = len(volume_to_bdry_conn.groups) assert ngrps == len(vol_discr.groups) assert ngrps == len(bdry_discr.groups) # One interpolation batch in this connection corresponds # to a key (i_tgt_grp,) (i_src_grp, i_face_tgt,) with cl.CommandQueue(vol_discr.cl_context) as queue: # a list of batches for each group groups = [[] for i_tgt_grp in range(ngrps)] for i_src_grp in range(ngrps): src_grp_el_lookup = _make_bdry_el_lookup_table( queue, volume_to_bdry_conn, i_src_grp) for i_tgt_grp in range(ngrps): vbc_tgt_grp_batches = volume_to_bdry_conn.groups[ i_tgt_grp].batches adj = vol_mesh.facial_adjacency_groups[i_tgt_grp][i_src_grp] for i_face_tgt in range(vol_mesh.groups[i_tgt_grp].nfaces): vbc_tgt_grp_face_batch = _find_ibatch_for_face( vbc_tgt_grp_batches, i_face_tgt) # {{{ index wrangling # Assert that the adjacency group and the restriction # interpolation batch and the adjacency group have the same # element ordering. adj_tgt_flags = adj.element_faces == i_face_tgt assert (np.array_equal( adj.elements[adj_tgt_flags], vbc_tgt_grp_face_batch.from_element_indices.get( queue=queue))) # find to_element_indices tgt_bdry_element_indices = ( vbc_tgt_grp_face_batch.to_element_indices.get( queue=queue)) # find from_element_indices src_vol_element_indices = adj.neighbors[adj_tgt_flags] src_element_faces = adj.neighbor_faces[adj_tgt_flags] src_bdry_element_indices = src_grp_el_lookup[ src_vol_element_indices, src_element_faces] # }}} # {{{ visualization (for debugging) if 0: print("TVE", adj.elements[adj_tgt_flags]) print("TBE", tgt_bdry_element_indices) print("FVE", src_vol_element_indices) from meshmode.mesh.visualization import draw_2d_mesh import matplotlib.pyplot as pt draw_2d_mesh(vol_discr.mesh, draw_element_numbers=True, set_bounding_box=True, draw_vertex_numbers=False, draw_face_numbers=True, fill=None) pt.figure() draw_2d_mesh(bdry_discr.mesh, draw_element_numbers=True, set_bounding_box=True, draw_vertex_numbers=False, draw_face_numbers=True, fill=None) pt.show() # }}} groups[i_tgt_grp].extend( _make_cross_face_batches(queue, bdry_discr, bdry_discr, i_tgt_grp, i_src_grp, tgt_bdry_element_indices, src_bdry_element_indices)) from meshmode.discretization.connection import ( DirectDiscretizationConnection, DiscretizationConnectionElementGroup) return DirectDiscretizationConnection( from_discr=bdry_discr, to_discr=bdry_discr, groups=[ DiscretizationConnectionElementGroup(batches=batches) for batches in groups ], is_surjective=True)
def flatten_chained_connection(queue, connection): """Collapse a connection into a direct connection. If the given connection is already a :class:`~meshmode.discretization.connection.DirectDiscretizationConnection` nothing is done. However, if the connection is a :class:`~meshmode.discretization.connection.ChainedDiscretizationConnection`, a new direct connection is constructed that transports from :attr:`connection.from_discr` to :attr:`connection.to_discr`. The new direct connection will have a number of groups and batches that is, at worse, the product of all the connections in the chain. For example, if we consider a connection between a discretization and a two-level refinement, both levels will have :math:`n` groups and :math:`m + 1` batches per group, where :math:`m` is the number of subdivisions of an element (exact number depends on implementation details in :func:`~meshmode.discretization.connection.make_refinement_connection`). However, a direct connection from level :math:`0` to level :math:`2` will have at worst :math:`n^2` groups and each group will have :math:`(m + 1)^2` batches. .. warning:: If a large number of connections is chained, the number of groups and batches can become very large. :arg queue: An instance of :class:`pyopencl.CommandQueue`. :arg connection: An instance of :class:`~meshmode.discretization.connection.DiscretizationConnection`. :return: An instance of :class:`~meshmode.discretization.connection.DirectDiscretizationConnection`. """ from meshmode.discretization.connection import ( DirectDiscretizationConnection, DiscretizationConnectionElementGroup, make_same_mesh_connection) if not hasattr(connection, 'connections'): return connection if not connection.connections: return make_same_mesh_connection(connection.to_discr, connection.from_discr) # recursively build direct connections connections = connection.connections direct_connections = [] for conn in connections: direct_connections.append(flatten_chained_connection(queue, conn)) # merge all the direct connections from_conn = direct_connections[0] for to_conn in direct_connections[1:]: el_table = _build_element_lookup_table(queue, from_conn) grp_to_grp, batch_info = _build_new_group_table(from_conn, to_conn) # distribute the indices to new groups and batches from_bins = [[np.empty(0, dtype=np.int) for _ in g] for g in batch_info] to_bins = [[np.empty(0, dtype=np.int) for _ in g] for g in batch_info] for (igrp, ibatch), (_, from_batch) in _iterbatches(from_conn.groups): from_to_element_indices = from_batch.to_element_indices.get(queue) for (jgrp, jbatch), (_, to_batch) in _iterbatches(to_conn.groups): igrp_new, ibatch_new = grp_to_grp[igrp, ibatch, jgrp, jbatch] jfrom = to_batch.from_element_indices.get(queue) jto = to_batch.to_element_indices.get(queue) mask = np.isin(jfrom, from_to_element_indices) from_bins[igrp_new][ibatch_new] = \ np.hstack([from_bins[igrp_new][ibatch_new], el_table[igrp][jfrom[mask]]]) to_bins[igrp_new][ibatch_new] = \ np.hstack([to_bins[igrp_new][ibatch_new], jto[mask]]) # build new groups groups = [] for igrp, (from_bin, to_bin) in enumerate(zip(from_bins, to_bins)): groups.append( DiscretizationConnectionElementGroup( list( _build_batches(queue, from_bin, to_bin, batch_info[igrp])))) from_conn = DirectDiscretizationConnection( from_discr=from_conn.from_discr, to_discr=to_conn.to_discr, groups=groups, is_surjective=connection.is_surjective) return from_conn
def make_partition_connection(actx, *, local_bdry_conn, i_local_part, remote_bdry_discr, remote_group_infos): """ Connects ``local_bdry_conn`` to a neighboring partition. :arg local_bdry_conn: A :class:`DiscretizationConnection` of the local partition. :arg i_local_part: The partition number of the local partition. :arg remote_bdry_discr: A :class:`~meshmode.discretization.Discretization` of the boundary of the remote partition. :arg remote_group_infos: An array of :class:`meshmode.distributed.RemoteGroupInfo` instances, one per remote volume element group. :returns: A :class:`DirectDiscretizationConnection` that performs data exchange across faces from the remote partition to partition `i_local_part`. .. versionadded:: 2017.1 .. warning:: Interface is not final. """ from meshmode.mesh.processing import find_group_indices from meshmode.discretization.connection import ( DirectDiscretizationConnection, DiscretizationConnectionElementGroup) local_vol_groups = local_bdry_conn.from_discr.mesh.groups part_batches = [[] for _ in local_vol_groups] assert len(local_vol_groups) == len(local_bdry_conn.to_discr.groups) # We need a nested loop over remote and local groups here. # The code assumes that there is the same number of volume and surface groups. # # A weak reason to choose remote as the outer loop is because # InterPartitionAdjacency refers to neighbors by global volume element # numbers, and we only have enough information to resolve those to (group, # group_local_el_nr) for local elements (whereas we have no information # about remote volume elements). # # (See the find_group_indices below.) for rgi in remote_group_infos: rem_ipag = rgi.inter_partition_adj_group indices = (i_local_part == rem_ipag.neighbor_partitions) if not np.any(indices): # Skip because remote group is not connected to i_local_part. continue i_remote_vol_elems = rem_ipag.elements[indices] i_remote_faces = rem_ipag.element_faces[indices] i_local_vol_elems = rem_ipag.partition_neighbors[indices] i_local_faces = rem_ipag.neighbor_faces[indices] del indices i_local_grps = find_group_indices(local_vol_groups, i_local_vol_elems) # {{{ make remote_vol_to_bdry remote_approx_vol_nelements = np.max(rgi.vol_elem_indices) + 1 remote_approx_nfaces = np.max(rgi.bdry_faces) + 1 remote_vol_to_bdry = np.full( (remote_approx_vol_nelements, remote_approx_nfaces), -1, dtype=remote_bdry_discr.mesh.element_id_dtype) remote_vol_to_bdry[rgi.vol_elem_indices, rgi.bdry_faces] = \ rgi.bdry_elem_indices # }}} for i_local_grp in np.unique(i_local_grps): # {{{ come up with matched_{local,remote}_bdry_el_indices local_vol_to_bdry = _make_bdry_el_lookup_table( actx, local_bdry_conn, i_local_grp) local_indices = np.where(i_local_grps == i_local_grp)[0] if len(local_indices) == 0: continue local_grp_vol_elems = ( i_local_vol_elems[local_indices] - local_vol_groups[i_local_grp].element_nr_base) # These are group-local. remote_grp_vol_elems = i_remote_vol_elems[local_indices] matched_local_bdry_el_indices = local_vol_to_bdry[ local_grp_vol_elems, i_local_faces[local_indices]] assert (matched_local_bdry_el_indices >= 0).all() matched_remote_bdry_el_indices = remote_vol_to_bdry[ remote_grp_vol_elems, i_remote_faces[local_indices]] assert (matched_remote_bdry_el_indices >= 0).all() # }}} grp_batches = _make_cross_face_batches( actx, local_bdry_conn.to_discr, remote_bdry_discr, i_local_grp, rem_ipag.igroup, matched_local_bdry_el_indices, matched_remote_bdry_el_indices) part_batches[i_local_grp].extend(grp_batches) return DirectDiscretizationConnection( from_discr=remote_bdry_discr, to_discr=local_bdry_conn.to_discr, groups=[ DiscretizationConnectionElementGroup(batches=grp_batches) for grp_batches in part_batches ], is_surjective=True)
def make_opposite_face_connection(actx, volume_to_bdry_conn): """Given a boundary restriction connection *volume_to_bdry_conn*, return a :class:`DirectDiscretizationConnection` that performs data exchange across opposite faces. """ vol_discr = volume_to_bdry_conn.from_discr vol_mesh = vol_discr.mesh bdry_discr = volume_to_bdry_conn.to_discr # make sure we were handed a volume-to-boundary connection for i_tgrp, conn_grp in enumerate(volume_to_bdry_conn.groups): for batch in conn_grp.batches: assert batch.from_group_index == i_tgrp assert batch.to_element_face is not None ngrps = len(volume_to_bdry_conn.groups) assert ngrps == len(vol_discr.groups) assert ngrps == len(bdry_discr.groups) # One interpolation batch in this connection corresponds # to a key (i_tgt_grp,) (i_src_grp, i_face_tgt,) # a list of batches for each group groups = [[] for i_tgt_grp in range(ngrps)] for i_src_grp in range(ngrps): src_grp_el_lookup = _make_bdry_el_lookup_table(actx, volume_to_bdry_conn, i_src_grp) for i_tgt_grp in range(ngrps): vbc_tgt_grp_batches = volume_to_bdry_conn.groups[i_tgt_grp].batches adj = vol_mesh.facial_adjacency_groups[i_tgt_grp][i_src_grp] for i_face_tgt in range(vol_mesh.groups[i_tgt_grp].nfaces): vbc_tgt_grp_face_batch = _find_ibatch_for_face( vbc_tgt_grp_batches, i_face_tgt) # {{{ index wrangling # The elements in the adjacency group will be a subset of # the elements in the restriction interpolation batch: # Imagine an inter-group boundary. The volume-to-boundary # connection will include all faces as targets, whereas # there will be separate adjacency groups for intra- and # inter-group connections. adj_tgt_flags = adj.element_faces == i_face_tgt adj_els = adj.elements[adj_tgt_flags] if adj_els.size == 0: # NOTE: this case can happen for inter-group boundaries # when all elements are adjacent on the same face # index, so all other ones will be empty continue vbc_els = thaw_to_numpy( actx, vbc_tgt_grp_face_batch.from_element_indices) if len(adj_els) == len(vbc_els): # Same length: assert (below) that the two use the same # ordering. vbc_used_els = slice(None) else: # Genuine subset: figure out an index mapping. vbc_els_sort_idx = np.argsort(vbc_els) vbc_used_els = vbc_els_sort_idx[np.searchsorted( vbc_els, adj_els, sorter=vbc_els_sort_idx)] assert np.array_equal(vbc_els[vbc_used_els], adj_els) # find to_element_indices tgt_bdry_element_indices = thaw_to_numpy( actx, vbc_tgt_grp_face_batch.to_element_indices)[vbc_used_els] # find from_element_indices src_vol_element_indices = adj.neighbors[adj_tgt_flags] src_element_faces = adj.neighbor_faces[adj_tgt_flags] src_bdry_element_indices = src_grp_el_lookup[ src_vol_element_indices, src_element_faces] # }}} # {{{ visualization (for debugging) if 0: print("TVE", adj.elements[adj_tgt_flags]) print("TBE", tgt_bdry_element_indices) print("FVE", src_vol_element_indices) from meshmode.mesh.visualization import draw_2d_mesh import matplotlib.pyplot as pt draw_2d_mesh(vol_discr.mesh, draw_element_numbers=True, set_bounding_box=True, draw_vertex_numbers=False, draw_face_numbers=True, fill=None) pt.figure() draw_2d_mesh(bdry_discr.mesh, draw_element_numbers=True, set_bounding_box=True, draw_vertex_numbers=False, draw_face_numbers=True, fill=None) pt.show() # }}} batches = _make_cross_face_batches(actx, bdry_discr, bdry_discr, i_tgt_grp, i_src_grp, tgt_bdry_element_indices, src_bdry_element_indices) groups[i_tgt_grp].extend(batches) from meshmode.discretization.connection import ( DirectDiscretizationConnection, DiscretizationConnectionElementGroup) return DirectDiscretizationConnection( from_discr=bdry_discr, to_discr=bdry_discr, groups=[ DiscretizationConnectionElementGroup(batches=batches) for batches in groups ], is_surjective=True)