face_index#

iskra.topology.face_index(data: Tensor, faces: Tensor, *, face_ndim: int = 2, squeeze: bool = True) → Tensor[SOURCE]#

Gathers subface values onto the faces that contain them.

This function collects, i.e., gathers, data (scalar, vector, tensor, etc.) defined on subfaces onto the faces they belong to. So, e.g., triangle faces can gather 3 edge-based values or 3 vertex-based values onto themselves. Alternatively, one can see this function as using the subface indices stored in faces to index into data: given data associated with each subface and a tensor of subface indices, this function outputs a tensor that contains data entries in positions defined by the indices.

Tip

This function is one of the three building blocks of iskra’s scatter-gather framework, together with iskra.topology.get_subfaces() which constructs the face hierarchy and iskra.topology.reduce_on_subface(), which performs the scatter-reduce operation.

This function moves data up the face hierarchy.

See Scatter-Gather Dataflow in iskra ✨ for an explanation of iskra’s tensor-based scatter-gather framework.

Parameters:
  • data (Tensor[DType, [Bs, S, Ds]]) – Data stored on each subface.

  • faces (Tensor[Int64, [Bs, F] | [Bs, F, FS] | [Bs, F, FSs (Shape of (nested) subfaces in a face. E.g., a tetrahedron’s side-triangles’ corners (`[F, FSs] = [Tets, FSs]) has FSs=[4, 3].)`]]) – Face-to-subface indices, possibly nested. See examples below.

  • face_ndim (int) – The dimensionality of the face index. By default the function assumes it is working with (possibly batched) face-vertex relations, which are stored as 2D tensors. Hence face_ndim=2 by default. If we were, e.g., gathering from vertices using a nested tet-tri-vert index we would specify face_ndim=3 and for a 1D list of vertices face_ndim=1.

  • squeeze (bool) – If faces shape is [Bs, F, FS] and FS == 1 (i.e. the list of faces is a 1D list of vertices), squeeze dictates whether the output will have the size-1 dimension corresponding to the face vertices squeezed. If faces shape is already [Bs, F], squeeze does nothing. Default: True.

Raises:
  • ValueError – Throws an exception if faces.ndim is smaller than face_ndim. Everything to the left of the face index is read as the batch, so, e.g., handing a 1D list of vertices to the default face_ndim=2 would ask for -1 batch dimensions. We have to specify face_ndim=1 instead.

  • ValueError – Throws an exception if the batch dimensions of faces and data disagree. It is face_ndim that decides where the batch stops and the face index starts, so this usually means face_ndim is wrong.

Returns:

​ (An Tensor with the shape [:abbr:`Bs (Batch shape (multiple dimensions).), F, FSs (Shape of (nested) subfaces in a face. E.g., a tetrahedron’s side-triangles’ corners (`[F, FSs] = [Tets, FSs]) has FSs=[4, 3].)`, Ds]`.)

Example

values.shape

faces.shape

result.shape

Gathering

[V, 2]

[Tris, 3]

[Tris, 3, 2]

2D triangle positions

[V, 3]

[Tris, 3]

[Tris, 3, 3]

3D triangle positions

[V, 3]

[Tets, 4]

[Tets, 4, 3]

3D tet positions

[V, 4]

[Tets, 4]

[Tets, 4, 4]

4D tet positions

[E, 2, 2]

[Tets, 4, 3]

[Tets, 4, 3, 2, 2]

2x2 tet-tri-edge matrices.

Next, assume a face tensor with the size of [8, 4]. There is no way to know whether this is 8 polygons with 4 corners each or whether an batch of 8 1D vectors of vertex indices. This function always assumes the first, i.e., it assumes it is working with a face-vertex input. To change the default and have the function interpret 8 as a batch dimension, we have to be explicit and specify the face-index is 1D, i.e., face_ndim=1.