From 77007f4f6b9905e19d47571724feb3f1ec9d84bd Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 24 May 2021 15:46:08 -0500 Subject: [PATCH 01/11] refactor _compute_facial_adjacency_from_vertices so that face matching can be called separately --- meshmode/mesh/__init__.py | 238 +++++++++++++++++++------------------- 1 file changed, 117 insertions(+), 121 deletions(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index e1f0139a3..91f8aa7e0 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1048,41 +1048,68 @@ def _boundary_tag_bit(boundary_tags, btag_to_index, boundary_tag): # {{{ vertex-based facial adjacency -class _FlatFacialAdjacencyData: +class _FaceIDs: """ - Data structure for intermediate storage of facial adjacency data. Each attribute - is a :class:`numpy.ndarray` containing data for each stored face and its - adjacent neighbor. + Data structure for storage of a list of face identifiers (group, element, face). + Each attribute is a :class:`numpy.ndarray`. + + .. attribute:: groups + + The index of the group containing the face. .. attribute:: elements - The group-relative element index. + The group-relative index of the element containing the face. - .. attribute:: element_faces + .. attribute:: faces + + The element-relative index of face. + """ + def __init__(self, groups, elements, faces): + self.groups = groups + self.elements = elements + self.faces = faces - The index of the shared face inside the element. - .. attribute:: neighbor_groups +def _concatenate_face_ids(face_ids_list): + return _FaceIDs( + groups=np.concatenate([ids.groups for ids in face_ids_list]), + elements=np.concatenate([ids.elements for ids in face_ids_list]), + faces=np.concatenate([ids.faces for ids in face_ids_list])) - The group containing the adjacent element, or -1 if the face is not shared. - .. attribute:: neighbors +def _match_faces_by_vertices(groups, face_ids, vertex_index_map_func=None): + if vertex_index_map_func is None: + def vertex_index_map_func(vertices): + return vertices - The mesh-wide element index of the adjacent element, or boundary tag - information if the face is not shared. + from pytools import single_valued + vertex_id_dtype = single_valued(grp.vertex_indices.dtype for grp in groups) - .. attribute:: neighbor_faces + nfaces = len(face_ids.groups) - The index of the shared face inside the adjacent element, or zero if the - face is not shared. + max_face_vertices = max(len(ref_fvi) for grp in groups + for ref_fvi in grp.face_vertex_indices()) - """ - def __init__(self, nfaces, element_id_dtype, face_id_dtype): - self.elements = np.empty(nfaces, dtype=element_id_dtype) - self.element_faces = np.empty(nfaces, dtype=face_id_dtype) - self.neighbor_groups = np.empty(nfaces, dtype=np.int64) - self.neighbors = np.empty(nfaces, dtype=element_id_dtype) - self.neighbor_faces = np.empty(nfaces, dtype=face_id_dtype) + face_vertex_indices = np.empty((max_face_vertices, nfaces), + dtype=vertex_id_dtype) + face_vertex_indices[:, :] = -1 + + for igrp, grp in enumerate(groups): + for fid, ref_fvi in enumerate(grp.face_vertex_indices()): + indices, = np.where((face_ids.groups == igrp) & (face_ids.faces == fid)) + grp_fvi = grp.vertex_indices[face_ids.elements[indices], :][:, ref_fvi] + face_vertex_indices[:len(ref_fvi), indices] = ( + vertex_index_map_func(grp_fvi).T) + + # Lexicographically sort the face vertex indices, then diff the result to find + # faces with the same vertices + face_vertex_indices_increasing = np.sort(face_vertex_indices, axis=0) + order = np.lexsort(face_vertex_indices_increasing) + diffs = np.diff(face_vertex_indices_increasing[:, order], axis=1) + match_indices, = (~np.any(diffs, axis=0)).nonzero() + + return np.stack((order[match_indices], order[match_indices+1])) def _compute_facial_adjacency_from_vertices(groups, boundary_tags, @@ -1091,56 +1118,46 @@ def _compute_facial_adjacency_from_vertices(groups, boundary_tags, face_vertex_indices_to_tags=None): if not groups: return None + boundary_tag_to_index = {tag: i for i, tag in enumerate(boundary_tags)} def boundary_tag_bit(boundary_tag): return _boundary_tag_bit(boundary_tags, boundary_tag_to_index, boundary_tag) - max_faces = max([grp.nfaces for grp in groups]) - max_face_vertices = max([len(ref_fvi) for grp in groups - for ref_fvi in grp.face_vertex_indices()]) + # Match up adjacent faces according to their vertex indices - # Pre-compute size of subsequent face data lists along with group/face offsets - # into them - n_total_faces = 0 - face_nr_bases = np.empty((len(groups), max_faces), dtype=element_id_dtype) - face_nr_bases[:] = -1 + face_ids_per_group = [] for igrp, grp in enumerate(groups): - for fid in range(len(grp.face_vertex_indices())): - face_nr_bases[igrp, fid] = n_total_faces - n_total_faces += grp.nelements + indices = np.indices((grp.nfaces, grp.nelements), dtype=element_id_dtype) + face_ids_per_group.append(_FaceIDs( + groups=np.zeros(grp.nelements * grp.nfaces, dtype=int) + igrp, + elements=indices[1].flatten(), + faces=indices[0].flatten().astype(face_id_dtype))) + face_ids = _concatenate_face_ids(face_ids_per_group) - face_vertex_indices = np.empty((max_face_vertices, n_total_faces), - dtype=element_id_dtype) - face_vertex_indices[:] = -1 - # (igrp, fid) for each face - face_ids = np.empty((2, n_total_faces), dtype=element_id_dtype) - - # Fill vertex indices and face IDs - for igrp, grp in enumerate(groups): - for fid, ref_fvi in enumerate(grp.face_vertex_indices()): - face_nr_base = face_nr_bases[igrp, fid] - grp_fvi = grp.vertex_indices[:, ref_fvi] - istart = face_nr_base - iend = face_nr_base + grp.nelements - face_vertex_indices[:len(ref_fvi), istart:iend] = grp_fvi.T - face_ids[0, istart:iend] = igrp - face_ids[1, istart:iend] = fid + matches = _match_faces_by_vertices(groups, face_ids) del igrp del grp - # Lexicographically sort the face vertex indices, then diff the result to find - # faces with the same vertices - face_vertex_indices_increasing = np.sort(face_vertex_indices, axis=0) - order = np.lexsort(face_vertex_indices_increasing) - diffs = np.diff(face_vertex_indices_increasing[:, order], axis=1) - match_indices, = (~np.any(diffs, axis=0)).nonzero() - matching_faces = (order[match_indices], order[match_indices+1]) - adjacent_face_indices = np.empty(n_total_faces, dtype=element_id_dtype) - adjacent_face_indices[:] = -1 - adjacent_face_indices[matching_faces[0]] = matching_faces[1] - adjacent_face_indices[matching_faces[1]] = matching_faces[0] + # Get ((grp#, elem#, face#), (neighbor grp#, neighbor elem#, neighbor face#)) + # for every face (both ways) + + matches_both_ways = np.stack(( + np.concatenate((matches[0, :], matches[1, :])), + np.concatenate((matches[1, :], matches[0, :])))) + order = np.lexsort((matches_both_ways[1, :], matches_both_ways[0, :])) + sorted_matches = matches_both_ways[:, order] + + face_id_pairs = ( + _FaceIDs( + groups=face_ids.groups[sorted_matches[0, :]], + elements=face_ids.elements[sorted_matches[0, :]], + faces=face_ids.faces[sorted_matches[0, :]]), + _FaceIDs( + groups=face_ids.groups[sorted_matches[1, :]], + elements=face_ids.elements[sorted_matches[1, :]], + faces=face_ids.faces[sorted_matches[1, :]])) # {{{ build facial_adjacency_groups data structure @@ -1149,74 +1166,53 @@ def boundary_tag_bit(boundary_tag): facial_adjacency_groups = [] for igrp, grp in enumerate(groups): grp_map = {} - # Flat adjacency data storage for all of the group's faces - grp_adj = _FlatFacialAdjacencyData(grp.nelements*grp.nfaces, - element_id_dtype=element_id_dtype, face_id_dtype=face_id_dtype) - for fid, ref_fvi in enumerate(grp.face_vertex_indices()): - face_nr_base = face_nr_bases[igrp, fid] - # Flat adjacency data storage for the current face - adj = _FlatFacialAdjacencyData(grp.nelements, - element_id_dtype=element_id_dtype, face_id_dtype=face_id_dtype) - adj.elements = np.indices((grp.nelements,), dtype=element_id_dtype) - adj.element_faces[:] = fid - adj.neighbor_groups[:] = -1 - adj.neighbor_faces[:] = 0 - adj_indices = adjacent_face_indices[face_nr_base: - face_nr_base+grp.nelements] - has_neighbor = adj_indices >= 0 - # Fill adjacency information for matched faces - neighbor_adj_indices = adj_indices[has_neighbor] - neighbor_igrps = face_ids[0, neighbor_adj_indices] - neighbor_fids = face_ids[1, neighbor_adj_indices] - adj.neighbor_groups[has_neighbor] = neighbor_igrps - adj.neighbor_faces[has_neighbor] = neighbor_fids - adj.neighbors[has_neighbor] = neighbor_adj_indices - face_nr_bases[ - neighbor_igrps, neighbor_fids] - # Add boundary information for non-matched faces - adj.neighbors[~has_neighbor] = -( - boundary_tag_bit(BTAG_ALL) - | boundary_tag_bit(BTAG_REALLY_ALL)) + + face_has_neighbor = np.empty((grp.nfaces, grp.nelements), dtype=bool) + face_has_neighbor[:, :] = False + + is_grp_adj = face_id_pairs[0].groups == igrp + connected_groups = np.unique(face_id_pairs[1].groups[is_grp_adj]) + for i_neighbor_grp in connected_groups: + is_neighbor_adj = ( + is_grp_adj & (face_id_pairs[1].groups == i_neighbor_grp)) + grp_map[i_neighbor_grp] = FacialAdjacencyGroup( + igroup=igrp, + ineighbor_group=i_neighbor_grp, + elements=face_id_pairs[0].elements[is_neighbor_adj], + element_faces=face_id_pairs[0].faces[is_neighbor_adj], + neighbors=face_id_pairs[1].elements[is_neighbor_adj], + neighbor_faces=face_id_pairs[1].faces[is_neighbor_adj]) + face_has_neighbor[ + face_id_pairs[0].faces[is_neighbor_adj], + face_id_pairs[0].elements[is_neighbor_adj]] = True + + has_bdry = not np.all(face_has_neighbor) + if has_bdry: + faces, elements = np.where(~face_has_neighbor) + element_faces = faces.astype(face_id_dtype) + neighbors = np.empty(len(elements), dtype=element_id_dtype) + neighbors[:] = -( + boundary_tag_bit(BTAG_ALL) + | boundary_tag_bit(BTAG_REALLY_ALL)) if face_vertex_indices_to_tags is not None: - for iel in range(grp.nelements): - if has_neighbor[iel]: - continue - fvi = frozenset(grp.vertex_indices[iel, ref_fvi]) + for i in range(len(elements)): + ref_fvi = grp.face_vertex_indices()[element_faces[i]] + fvi = frozenset(grp.vertex_indices[elements[i], ref_fvi]) tags = face_vertex_indices_to_tags.get(fvi, None) if tags is not None: tag_mask = 0 for tag in tags: tag_mask |= boundary_tag_bit(tag) - adj.neighbors[iel] = -((-adj.neighbors[iel]) | tag_mask) - # Insert into the group-wide list - istart = fid*grp.nelements - iend = (fid+1)*grp.nelements - grp_adj.elements[istart:iend] = adj.elements - grp_adj.element_faces[istart:iend] = adj.element_faces - grp_adj.neighbor_groups[istart:iend] = adj.neighbor_groups - grp_adj.neighbors[istart:iend] = adj.neighbors - grp_adj.neighbor_faces[istart:iend] = adj.neighbor_faces - # Filter group-wide list by neighbor group and create adjacency groups - unique_neighbor_groups = np.unique(grp_adj.neighbor_groups) - has_bdry = unique_neighbor_groups[0] == -1 - connected_groups = unique_neighbor_groups[unique_neighbor_groups >= 0] - if has_bdry: - is_bdry = grp_adj.neighbor_groups == -1 + neighbors[i] = -((-neighbors[i]) | tag_mask) + neighbor_faces = np.zeros(len(elements), dtype=face_id_dtype) grp_map[None] = FacialAdjacencyGroup( - igroup=igrp, - ineighbor_group=None, - elements=grp_adj.elements[is_bdry], - element_faces=grp_adj.element_faces[is_bdry], - neighbors=grp_adj.neighbors[is_bdry], - neighbor_faces=grp_adj.neighbor_faces[is_bdry]) - for i_neighbor_grp in connected_groups: - is_neighbor_adj = grp_adj.neighbor_groups == i_neighbor_grp - grp_map[i_neighbor_grp] = FacialAdjacencyGroup( - igroup=igrp, - ineighbor_group=i_neighbor_grp, - elements=grp_adj.elements[is_neighbor_adj], - element_faces=grp_adj.element_faces[is_neighbor_adj], - neighbors=grp_adj.neighbors[is_neighbor_adj], - neighbor_faces=grp_adj.neighbor_faces[is_neighbor_adj]) + igroup=igrp, + ineighbor_group=None, + elements=elements, + element_faces=element_faces, + neighbors=neighbors, + neighbor_faces=neighbor_faces) + facial_adjacency_groups.append(grp_map) # }}} From 745aba695a0a1597fa3d8a1bf50c24c969a1c652 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Fri, 4 Jun 2021 11:05:11 -0500 Subject: [PATCH 02/11] document array shapes in _FaceIDs --- meshmode/mesh/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index 91f8aa7e0..c27bdafa3 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1051,7 +1051,7 @@ def _boundary_tag_bit(boundary_tags, btag_to_index, boundary_tag): class _FaceIDs: """ Data structure for storage of a list of face identifiers (group, element, face). - Each attribute is a :class:`numpy.ndarray`. + Each attribute is a :class:`numpy.ndarray` of shape (nfaces,). .. attribute:: groups From 47b1d6918ab0ac10707a3e291c4db2ce3adb552a Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Fri, 4 Jun 2021 11:10:28 -0500 Subject: [PATCH 03/11] document _match_faces_by_vertices --- meshmode/mesh/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index c27bdafa3..7bbb6195f 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1079,6 +1079,13 @@ def _concatenate_face_ids(face_ids_list): def _match_faces_by_vertices(groups, face_ids, vertex_index_map_func=None): + """ + Return matching faces in *face_ids*, where two faces match if they have the + same vertices. Expressed as a :class:`np.ndarray` of shape (2,nmatches) of + indices into *face_ids*. *vertex_index_map_func* is used to map vertices to + other vertices; it must accept (possibly multidimensional) numpy arrays of + vertex indices. + """ if vertex_index_map_func is None: def vertex_index_map_func(vertices): return vertices From 28528526e30705412ee60402dfda1fd1ba8009cd Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Fri, 4 Jun 2021 11:11:08 -0500 Subject: [PATCH 04/11] change "matches" to "face_index_pairs" --- meshmode/mesh/__init__.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index 7bbb6195f..023fa8bbc 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1142,7 +1142,7 @@ def boundary_tag_bit(boundary_tag): faces=indices[0].flatten().astype(face_id_dtype))) face_ids = _concatenate_face_ids(face_ids_per_group) - matches = _match_faces_by_vertices(groups, face_ids) + face_index_pairs = _match_faces_by_vertices(groups, face_ids) del igrp del grp @@ -1150,21 +1150,29 @@ def boundary_tag_bit(boundary_tag): # Get ((grp#, elem#, face#), (neighbor grp#, neighbor elem#, neighbor face#)) # for every face (both ways) - matches_both_ways = np.stack(( - np.concatenate((matches[0, :], matches[1, :])), - np.concatenate((matches[1, :], matches[0, :])))) - order = np.lexsort((matches_both_ways[1, :], matches_both_ways[0, :])) - sorted_matches = matches_both_ways[:, order] + face_index_pairs_both_ways = np.stack(( + np.concatenate(( + face_index_pairs[0, :], + face_index_pairs[1, :])), + np.concatenate(( + face_index_pairs[1, :], + face_index_pairs[0, :])))) + # Sort by group, then neighbor group. Sort face indices because face_ids is + # already ordered by group + order = np.lexsort(( + face_index_pairs_both_ways[1, :], + face_index_pairs_both_ways[0, :])) + face_index_pairs_both_ways_sorted = face_index_pairs_both_ways[:, order] face_id_pairs = ( _FaceIDs( - groups=face_ids.groups[sorted_matches[0, :]], - elements=face_ids.elements[sorted_matches[0, :]], - faces=face_ids.faces[sorted_matches[0, :]]), + groups=face_ids.groups[face_index_pairs_both_ways_sorted[0, :]], + elements=face_ids.elements[face_index_pairs_both_ways_sorted[0, :]], + faces=face_ids.faces[face_index_pairs_both_ways_sorted[0, :]]), _FaceIDs( - groups=face_ids.groups[sorted_matches[1, :]], - elements=face_ids.elements[sorted_matches[1, :]], - faces=face_ids.faces[sorted_matches[1, :]])) + groups=face_ids.groups[face_index_pairs_both_ways_sorted[1, :]], + elements=face_ids.elements[face_index_pairs_both_ways_sorted[1, :]], + faces=face_ids.faces[face_index_pairs_both_ways_sorted[1, :]])) # {{{ build facial_adjacency_groups data structure From 0e7dbe6bee37a7b2ff46e7b70ef564ec1d139600 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Fri, 4 Jun 2021 11:14:09 -0500 Subject: [PATCH 05/11] simplify some array initializations --- meshmode/mesh/__init__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index 023fa8bbc..af4b13f88 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1137,7 +1137,7 @@ def boundary_tag_bit(boundary_tag): for igrp, grp in enumerate(groups): indices = np.indices((grp.nfaces, grp.nelements), dtype=element_id_dtype) face_ids_per_group.append(_FaceIDs( - groups=np.zeros(grp.nelements * grp.nfaces, dtype=int) + igrp, + groups=np.full(grp.nelements * grp.nfaces, igrp), elements=indices[1].flatten(), faces=indices[0].flatten().astype(face_id_dtype))) face_ids = _concatenate_face_ids(face_ids_per_group) @@ -1182,8 +1182,7 @@ def boundary_tag_bit(boundary_tag): for igrp, grp in enumerate(groups): grp_map = {} - face_has_neighbor = np.empty((grp.nfaces, grp.nelements), dtype=bool) - face_has_neighbor[:, :] = False + face_has_neighbor = np.full((grp.nfaces, grp.nelements), False) is_grp_adj = face_id_pairs[0].groups == igrp connected_groups = np.unique(face_id_pairs[1].groups[is_grp_adj]) @@ -1205,10 +1204,10 @@ def boundary_tag_bit(boundary_tag): if has_bdry: faces, elements = np.where(~face_has_neighbor) element_faces = faces.astype(face_id_dtype) - neighbors = np.empty(len(elements), dtype=element_id_dtype) - neighbors[:] = -( - boundary_tag_bit(BTAG_ALL) - | boundary_tag_bit(BTAG_REALLY_ALL)) + neighbors = np.full(len(elements), + -(boundary_tag_bit(BTAG_ALL) + | boundary_tag_bit(BTAG_REALLY_ALL)), + dtype=element_id_dtype) if face_vertex_indices_to_tags is not None: for i in range(len(elements)): ref_fvi = grp.face_vertex_indices()[element_faces[i]] From fe50f623b4082aa12d1ef05fd545e688ed1ba507 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 8 Jun 2021 11:07:44 -0500 Subject: [PATCH 06/11] temporarily restore _FlatFacialAdjacencyData --- meshmode/mesh/__init__.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index af4b13f88..288de1237 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1048,6 +1048,43 @@ def _boundary_tag_bit(boundary_tags, btag_to_index, boundary_tag): # {{{ vertex-based facial adjacency +class _FlatFacialAdjacencyData: + """ + Data structure for intermediate storage of facial adjacency data. Each attribute + is a :class:`numpy.ndarray` containing data for each stored face and its + adjacent neighbor. + + .. attribute:: elements + + The group-relative element index. + + .. attribute:: element_faces + + The index of the shared face inside the element. + + .. attribute:: neighbor_groups + + The group containing the adjacent element, or -1 if the face is not shared. + + .. attribute:: neighbors + + The mesh-wide element index of the adjacent element, or boundary tag + information if the face is not shared. + + .. attribute:: neighbor_faces + + The index of the shared face inside the adjacent element, or zero if the + face is not shared. + + """ + def __init__(self, nfaces, element_id_dtype, face_id_dtype): + self.elements = np.empty(nfaces, dtype=element_id_dtype) + self.element_faces = np.empty(nfaces, dtype=face_id_dtype) + self.neighbor_groups = np.empty(nfaces, dtype=np.int64) + self.neighbors = np.empty(nfaces, dtype=element_id_dtype) + self.neighbor_faces = np.empty(nfaces, dtype=face_id_dtype) + + class _FaceIDs: """ Data structure for storage of a list of face identifiers (group, element, face). From d67a7ee0cf7fdd2625eaa57d1ba911d8e79a8c53 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Wed, 9 Jun 2021 16:22:13 -0500 Subject: [PATCH 07/11] fix docstrings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andreas Klöckner --- meshmode/mesh/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index 288de1237..8f1d5a973 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1088,7 +1088,7 @@ def __init__(self, nfaces, element_id_dtype, face_id_dtype): class _FaceIDs: """ Data structure for storage of a list of face identifiers (group, element, face). - Each attribute is a :class:`numpy.ndarray` of shape (nfaces,). + Each attribute is a :class:`numpy.ndarray` of shape ``(nfaces,)``. .. attribute:: groups @@ -1118,7 +1118,7 @@ def _concatenate_face_ids(face_ids_list): def _match_faces_by_vertices(groups, face_ids, vertex_index_map_func=None): """ Return matching faces in *face_ids*, where two faces match if they have the - same vertices. Expressed as a :class:`np.ndarray` of shape (2,nmatches) of + same vertices. Expressed as a :class:`np.ndarray` of shape ``(2, nmatches)`` of indices into *face_ids*. *vertex_index_map_func* is used to map vertices to other vertices; it must accept (possibly multidimensional) numpy arrays of vertex indices. From 6e62d50e5942c585d91463f0d4beefe521f1a977 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Wed, 9 Jun 2021 16:23:11 -0500 Subject: [PATCH 08/11] add comment explaining initial sorting of vertex indices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andreas Klöckner --- meshmode/mesh/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index 8f1d5a973..b16701d0a 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1146,9 +1146,10 @@ def vertex_index_map_func(vertices): face_vertex_indices[:len(ref_fvi), indices] = ( vertex_index_map_func(grp_fvi).T) + # Normalize vertex-based "face identifiers" by sorting + face_vertex_indices_increasing = np.sort(face_vertex_indices, axis=0) # Lexicographically sort the face vertex indices, then diff the result to find # faces with the same vertices - face_vertex_indices_increasing = np.sort(face_vertex_indices, axis=0) order = np.lexsort(face_vertex_indices_increasing) diffs = np.diff(face_vertex_indices_increasing[:, order], axis=1) match_indices, = (~np.any(diffs, axis=0)).nonzero() From f2c6ce2de9e607232db7371efb759b8a6d5ae1df Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Wed, 9 Jun 2021 16:24:03 -0500 Subject: [PATCH 09/11] reword comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andreas Klöckner --- meshmode/mesh/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index b16701d0a..5f108fc00 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1195,8 +1195,9 @@ def boundary_tag_bit(boundary_tag): np.concatenate(( face_index_pairs[1, :], face_index_pairs[0, :])))) - # Sort by group, then neighbor group. Sort face indices because face_ids is - # already ordered by group + # Accomplish a sort by group, then neighbor group. This is done by sorting by + # the indices in face_ids. Realize that those are already ordered by group by + # construction. order = np.lexsort(( face_index_pairs_both_ways[1, :], face_index_pairs_both_ways[0, :])) From a47023f4b0009a5635ff695c1cf2bb4f525ad6f9 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Wed, 9 Jun 2021 16:41:20 -0500 Subject: [PATCH 10/11] improve documentation of _match_faces_by_vertices --- meshmode/mesh/__init__.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index 5f108fc00..e5ec38ca5 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1117,11 +1117,19 @@ def _concatenate_face_ids(face_ids_list): def _match_faces_by_vertices(groups, face_ids, vertex_index_map_func=None): """ - Return matching faces in *face_ids*, where two faces match if they have the - same vertices. Expressed as a :class:`np.ndarray` of shape ``(2, nmatches)`` of - indices into *face_ids*. *vertex_index_map_func* is used to map vertices to - other vertices; it must accept (possibly multidimensional) numpy arrays of - vertex indices. + Return matching faces in *face_ids* (expressed as pairs of indices into + *face_ids*), where two faces match if they have the same vertices. + + :arg groups: A list of :class:`~meshmode.mesh.MeshElementGroup` to which the + faces in *face_ids* belong. + :arg face_ids: A :class:`~meshmode.mesh._FaceIDs` containing the faces selected + for matching. + :arg vertex_index_map_func: An optional function that maps a set of vertex + indices (stored in a :class:`numpy.ndarray`) to another set of vertex + indices. Must accept multidimensional arrays as input and return an array + of the same shape. + :returns: A :class:`numpy.ndarray` of shape ``(2, nmatches)`` of indices into + *face_ids*. """ if vertex_index_map_func is None: def vertex_index_map_func(vertices): From 22b76d95d174109fc6f02ac2d34879a9b13a7b22 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Wed, 9 Jun 2021 18:00:15 -0500 Subject: [PATCH 11/11] Revert "temporarily restore _FlatFacialAdjacencyData" This reverts commit fe50f623b4082aa12d1ef05fd545e688ed1ba507. --- meshmode/mesh/__init__.py | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index e5ec38ca5..583536863 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -1048,43 +1048,6 @@ def _boundary_tag_bit(boundary_tags, btag_to_index, boundary_tag): # {{{ vertex-based facial adjacency -class _FlatFacialAdjacencyData: - """ - Data structure for intermediate storage of facial adjacency data. Each attribute - is a :class:`numpy.ndarray` containing data for each stored face and its - adjacent neighbor. - - .. attribute:: elements - - The group-relative element index. - - .. attribute:: element_faces - - The index of the shared face inside the element. - - .. attribute:: neighbor_groups - - The group containing the adjacent element, or -1 if the face is not shared. - - .. attribute:: neighbors - - The mesh-wide element index of the adjacent element, or boundary tag - information if the face is not shared. - - .. attribute:: neighbor_faces - - The index of the shared face inside the adjacent element, or zero if the - face is not shared. - - """ - def __init__(self, nfaces, element_id_dtype, face_id_dtype): - self.elements = np.empty(nfaces, dtype=element_id_dtype) - self.element_faces = np.empty(nfaces, dtype=face_id_dtype) - self.neighbor_groups = np.empty(nfaces, dtype=np.int64) - self.neighbors = np.empty(nfaces, dtype=element_id_dtype) - self.neighbor_faces = np.empty(nfaces, dtype=face_id_dtype) - - class _FaceIDs: """ Data structure for storage of a list of face identifiers (group, element, face).