reduce_on_subface#
- iskra.topology.reduce_on_subface(data: Tensor, faces: Tensor, n_subfaces: int, reduce: Literal['sum', 'prod', 'mean', 'amax', 'amin'], *, face_ndim: int = 2, data_ndim: int | None = None) Tensor[SOURCE]#
Scatter-reduce data from faces onto constitutive subfaces.
This function distributes, i.e., scatters, data (scalar, vector, tensor, etc.) defined on faces to each face’s subfaces. A naive scatter would result in each subface receiving multiple competing values, one for each face that contains it. Therefore, each operation is actually a scatter-reduce (see
reduceargument). So, e.g., a triangle face can scatter its value to its 3 edges. That value is then, e.g., averaged onto each edge with the values of the other triangles that share that same edge.The
facestensor is a (batched) tensor of (nested) faces, where the number of face dimension equalsface_ndim, and the number of batch dimensions is equal tobatch_ndim = faces.ndim - face_ndim.The function slices up the data tensor into three parts: [Bs, Fs, Ds], where Bs is the batch shape, Ds are the data payload dimensions; Fs represents the “domain” of the data. On a triangle mesh, Fs = [F] implies one data payload per triangle, whereas Fs = [F, 3] implies one data payload per triangle-corner or triangle-side.
Length of Ds is specified by
data_ndim. Data dimensions are greedy whendata_ndimisNone: by default (and at minimum), we assume Fs = [F] and that everything to the right of Fs is the data payload. Ifdata_ndimis specified, Fs can expand. E.g., if data.shape = [B, F, 3, 3] and Fs = [F], we have one data payload per face, whereas if we setdata_ndim=1, then Fs = [F, 3] and we have one data payload per triangle-corner. See more concrete examples below.Tip
Fs represents the size of the source domain where the data is defined, whereas
facestells us how that source domain connects to the target domain below it, andfaces_ndimis simply there to disambiguate batch dimensions.Caution
Note that Fs is not the same as
face_ndim,face_ndimspecifies how many dimensions corresponding to the mesh domain there are infaces, and therefore implicitly the number of batch dimensions asbatch_ndim = faces.ndim - face_ndim. This is preferrable to specifyingbatch_ndimas an argument because it lets the function work both on batched and unbatched inputs.Tip
This function is one of the three building blocks of
iskra’s scatter-gather framework, together withiskra.topology.get_subfaces()which constructs the face hierarchy andiskra.topology.face_index(), which performs the gather operation.This function moves data down 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, Fs, Ds]]) – Data defined on mesh faces (Fs=[F]) or face-subfaces (Fs=[F, FS]).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]) hasFSs=[4, 3].)`]]) – Face-subface indices, possibly nested.n_subfaces (int) – Total number of subfaces in the mesh (e.g., total number of vertices or edges in a triangle mesh).
reduce (Literal["sum", "prod", "mean", "amax", "amin"]) – Reduction operation, see
torch.scatter_reduce()for more details.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
2Dtensors. Henceface_ndim=2by default. If we were, e.g., gathering from vertices using a nested tet-tri-vert index we would specifyface_ndim=3and for a 1D list of verticesface_ndim=1.data_ndim (int | None) – Num. dimensions of the per-face (or per-face-subface) payload. See above for more details on default behavior.
- Raises:
ValueError – Throws an exception if
faces.ndimis smaller thanface_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 defaultface_ndim=2would ask for-1batch dimensions. We have to specifyface_ndim=1instead.ValueError – Throws an exception if the batch dimensions of
facesanddatadisagree. It isface_ndimthat decides where the batch stops and the face index starts, so this usually meansface_ndimis wrong.ValueError – Throws error if
data_ndimis negative, or big enough to leave no face dimension indata.
- Returns:
(
Tensor[DType, [Bs, S, Ds]]) – Data reduced onto the S subfaces, (where
:abbr:`S (Number of subfaces in the mesh. Usually S is documented together with some F as point of reference. E.g., if F is the number of triangles, S could be the number of edges or vertices in the mesh.)`equals the value of the argumentn_subfaces.)
Example
data.shapefaces.shapedata_ndimresult.shapeExample: from → to
[B, F]
[B, F, 3]
0[B, V]
face scalars → vertices
[B, F, 3]
[B, F, 3]
0[B, V]
corner scalars → vertices
[B, F, 2]
[B, F, 3]
1|None[B, V, 2]
face 2-vectors → vertices
[B, F, 3, 2]
[B, F, 3]
1[B, V, 2]
corner 2-vectors → vertices
[B, F, 3]
[B, F, 3]
1|None[B, V, 3]
face normals → vertices
[B, F, 3, 3]
[B, F, 3]
1[B, V, 3]
corner normals → vertices
[B, F, 3, 3]
[B, F, 3]
2|None[B, V, 3, 3]
face covariances → vertices