Given a list of vertices, elements and boundary edges, computes the element connectivity matrix and corresponding edge properties. Args: vertices: `Tensor` representing list (instance) of vectors (channel) elements: Sparse matrix listing all elements (instance). Each entry
(vertices: Tensor, # (vertices:i, vector)
elements: Tensor, # (elements:i, ~vertices)
boundaries: Dict[str, Sequence], # vertex pairs
element_rank: int,
periodic: Sequence[str], # periodic dim names
vertex_mean: Tensor,
face_format: str)
| 713 | |
| 714 | |
| 715 | def build_faces(vertices: Tensor, # (vertices:i, vector) |
| 716 | elements: Tensor, # (elements:i, ~vertices) |
| 717 | boundaries: Dict[str, Sequence], # vertex pairs |
| 718 | element_rank: int, |
| 719 | periodic: Sequence[str], # periodic dim names |
| 720 | vertex_mean: Tensor, |
| 721 | face_format: str): |
| 722 | """ |
| 723 | Given a list of vertices, elements and boundary edges, computes the element connectivity matrix and corresponding edge properties. |
| 724 | |
| 725 | Args: |
| 726 | vertices: `Tensor` representing list (instance) of vectors (channel) |
| 727 | elements: Sparse matrix listing all elements (instance). Each entry represents a vertex (dual) belonging to an element. |
| 728 | boundaries: Named sequences of edges (vertex pairs). |
| 729 | element_rank: Spatial rank of the elements (currently only 2 is supported) |
| 730 | periodic: Which dims are periodic. |
| 731 | vertex_mean: Mean vertex position for each element. |
| 732 | face_format: Sparse matrix format to use for the element-element matrices. |
| 733 | """ |
| 734 | n_v = instance(vertices).size |
| 735 | n_e = instance(elements).size |
| 736 | # --- Periodic: map vertices of boundary+ to the corresponding vertex in boundary- --- |
| 737 | vertex_id = np.arange(instance(vertices).size) |
| 738 | periodic = {dim[:-len('[::-1]')] if dim.endswith('[::-1]') else dim: dim.endswith('[::-1]') for dim in periodic} |
| 739 | for dim, is_flipped in periodic.items(): |
| 740 | vertex_id[np.concatenate(boundaries[dim+'+'])] = vertex_id[np.concatenate(boundaries[dim+'-'])[::-1] if is_flipped else np.concatenate(boundaries[dim+'-'])] |
| 741 | is_periodic = dim_mask(vertices.vector.item_names, tuple(periodic)) |
| 742 | # --- element-facet and facet-vertex matrix. A facet describes a single oriented face of an element, i.e. shared faces get two entries. --- |
| 743 | v_count = dsum(elements).numpy() # number of vertices per element |
| 744 | v1 = stored_indices(elements).index[dual(elements).name].numpy() |
| 745 | if element_rank == 2: # edges are the lines between neighbor vertices in the vertex lists + the edge last-to-first |
| 746 | v1 = vertex_id[v1] |
| 747 | n_f = v1.size # total number of facets (excluding boundaries) |
| 748 | ptr = np.cumsum(v_count) |
| 749 | roll = np.arange(v1.size) + 1 |
| 750 | roll[ptr - 1] = ptr - v_count |
| 751 | v12 = np.stack([v1, v1[roll]], -1).flatten() |
| 752 | f_idx = np.arange(v1.size, dtype=v1.dtype) |
| 753 | f_idx2 = f_idx.repeat(2) |
| 754 | f_v = coo_matrix((np.ones(n_f*2, np.int32), (f_idx2, v12)), shape=(n_f, n_v)) # facet-vertex matrix |
| 755 | e_idx = np.arange(instance(elements).size).repeat(v_count) |
| 756 | e_f = coo_matrix((np.ones(n_f, bool), (e_idx, f_idx)), shape=(n_e, n_f)) # element-facet matrix |
| 757 | # --- Compute facet properties: center, normal, area --- |
| 758 | f_v_pos = vertices[reshaped_tensor(v12, [instance('facets') + dual(pair=2)], convert=False)] # vertex positions of every (inner) facet |
| 759 | if periodic: # map v_pos: closest to cell_center |
| 760 | cell_center = vertex_mean[wrap(e_idx, 'facets:i')] |
| 761 | bounds = bounding_box(vertices) |
| 762 | delta = PERIODIC.shortest_distance(cell_center - bounds.lower, f_v_pos - bounds.lower, bounds.size) |
| 763 | f_v_pos = where(is_periodic, cell_center + delta, f_v_pos) |
| 764 | f_center = dmean(f_v_pos) |
| 765 | edge_dir = f_v_pos.pair.dual[1] - f_v_pos.pair.dual[0] |
| 766 | area = vec_length(edge_dir) |
| 767 | normal = vec_normalize(stack([-edge_dir[1], edge_dir[0]], channel(edge_dir))) |
| 768 | elif element_rank == 3: |
| 769 | v3d, c3d = element_types_3d() |
| 770 | n_v_per_f = [c3d[v] for v in v_count] |
| 771 | n_f_per_e = [len(v) for v in n_v_per_f] |
| 772 | n_fv_per_e = [sum(v) for v in n_v_per_f] |
no test coverage detected