`vtkDataSetTriangleFilter` changes connectivity ordering

tl;dr: vtkDataSetTriangleFilter causes cell connectivity ordering to change for pure vtk unstructured grid but not in a pyvista uns grid. Why? Does it matter? What am I missing to replicate the latter effect given that pyvista wraps vtk?

I have a pyvista fn that creates a tetrahedron unstructured grid and I translated it to pure vtk as such:

from vtkmodules.vtkCommonDataModel import (
    VTK_HEXAHEDRON,
    VTK_TETRA,
    VTK_TRIANGLE,
    vtkDataSet,
    vtkPolyData,
    vtkUnstructuredGrid,
)
from vtkmodules.util import numpy_support
import numpy as np
import pyvista as pv
from vtkmodules.vtkCommonCore import vtkIdList, vtkPoints, vtkStringArray
from vtkmodules.vtkFiltersGeneral import vtkDataSetTriangleFilter

# pyvista:
def get_tetra(cell_num: int = 2):
    points_per_cell = 4
    points = np.empty((points_per_cell * cell_num, 3), dtype=np.float32)
    for i in range(cell_num):
        points[i*points_per_cell : (i+1)*points_per_cell] = \
            [[i, 0, 0],[i+0.5, 0, 0],[i+0.5, 0.5, 0.5],[i, 0, 0.5]]  # fmt: skip
    cells = np.arange(len(points)).reshape([cell_num, points_per_cell])
    grid = pv.UnstructuredGrid({VTK_TETRA: cells}, points)
    return grid
pv_tetra = get_tetra()

# pure vtk:
def get_unstructured_grid(cell_num: int = 2):
    grid = vtkUnstructuredGrid()
    points = vtkPoints()
    point_idx = 0

    def insert_cell(cell_type_id, point_ids):
        id_list = vtkIdList()
        for pid in point_ids:
            id_list.InsertNextId(pid)
        grid.InsertNextCell(cell_type_id, id_list)


    for i in range(cell_num):
        cell_points = np.array(
            [
                [i, 0, 0],
                [i + 0.5, 0, 0],
                [i + 0.5, 0.5, 0.5],
                [i, 0, 0.5],
            ],
            dtype=np.float32,
        )
        ids = []
        for pt in cell_points:
            points.InsertNextPoint(pt)
            ids.append(point_idx)
            point_idx += 1

        id_list = vtkIdList()
        for pid in ids:
            id_list.InsertNextId(pid)
        grid.InsertNextCell(VTK_TETRA, id_list)

    grid.SetPoints(points)
    return grid
vtk_ugrid = get_unstructured_grid()

These two seem identical:

However, using the vtkDataSetTriangleFilter on them yields different results in connectivity ordering:

print(numpy_support.vtk_to_numpy(pv_tetra.GetCells().GetData()))
print(numpy_support.vtk_to_numpy(vtk_ugrid.GetCells().GetData()))

trifilter = vtkDataSetTriangleFilter(input_data=pv_tetra)
trifilter.Update()
new_pv_tetra = trifilter.GetOutput()

trifilter1 = vtkDataSetTriangleFilter(input_data=vtk_ugrid)
trifilter1.Update()
new_vtk_ugrid = trifilter1.GetOutput()

print(numpy_support.vtk_to_numpy(new_pv_tetra.GetCells().GetData()))
print(numpy_support.vtk_to_numpy(new_vtk_ugrid.GetCells().GetData()))

yields

[4 0 1 2 3 4 4 5 6 7]
[4 0 1 2 3 4 4 5 6 7]
[4 0 1 2 3 4 4 5 6 7]
[4 1 2 0 3 4 5 6 4 7]

Why does the connectivity ordering change in the pure vtk implementation but not the pyvista implementation? What can I do to replicate the latter? What are the implications of the connectivity ordering changing?

Thanks!

I don’t have the answers to your questions about vtkDataSetTriangleFilter, but a comprehensive comparison of pv_tetra vs. vtk_ugrid can be done by saving both to ASCII files and then doing a diff,

writer = vtkUnstructuredGridWriter()
writer.SetFileTypeToASCII()

Thanks for the suggestion @dgobbi
It seems like that their ASCII representations are identical; the diff yields nothing.
Just including the code as sanity check:

import numpy as np
from vtkmodules.vtkCommonCore import vtkIdList, vtkPoints, vtkStringArray
from vtkmodules.vtkCommonDataModel import (
    VTK_HEXAHEDRON,
    VTK_TETRA,
    VTK_TRIANGLE,
    vtkDataSet,
    vtkPolyData,
    vtkUnstructuredGrid,
)
from vtkmodules.vtkFiltersCore import vtkCleanPolyData
from vtkmodules.vtkFiltersSources import vtkCubeSource
from vtkmodules.vtkIOLegacy import vtkUnstructuredGridWriter

grid = vtkUnstructuredGrid()
points = vtkPoints()
point_idx = 0

def insert_cell(cell_type_id, point_ids):
    id_list = vtkIdList()
    for pid in point_ids:
        id_list.InsertNextId(pid)
    grid.InsertNextCell(cell_type_id, id_list)
    
for i in range(2):
    cell_points = np.array(
        [
            [i, 0, 0],
            [i + 0.5, 0, 0],
            [i + 0.5, 0.5, 0.5],
            [i, 0, 0.5],
        ],
        dtype=np.float32,
    )
    ids = []
    for pt in cell_points:
        points.InsertNextPoint(pt)
        ids.append(point_idx)
        point_idx += 1
    insert_cell(VTK_TETRA, ids)

grid.SetPoints(points)
vtk_writer = vtkUnstructuredGridWriter(file_name="vtk_tetra.vtk", input_data=grid)
vtk_writer.SetFileTypeToASCII()
vtk_writer.Update()
vtk_writer.Write()

import pyvista as pv
points_per_cell = 4
cell_num = 2
pv_points = np.empty((points_per_cell * cell_num, 3), dtype=np.float32)
for i in range(cell_num):
    pv_points[i*points_per_cell : (i+1)*points_per_cell] = \
        [[i, 0, 0],[i+0.5, 0, 0],[i+0.5, 0.5, 0.5],[i, 0, 0.5]]  # fmt: skip
cells = np.arange(len(pv_points)).reshape([cell_num, points_per_cell])
pv_grid = pv.UnstructuredGrid({VTK_TETRA: cells}, pv_points)
vtk_writer = vtkUnstructuredGridWriter(file_name="pv_tetra.vtk", input_data=pv_grid)
vtk_writer.SetFileTypeToASCII()
vtk_writer.Update()
vtk_writer.Write()

I took a closer look and found a bug in vtkDataSetTriangleFilter, there is a place where it is looping over the reserved size of an array when it is supposed to be looping over the number of values actually stored in the array. That must be where pv_tetra and vtk_ugrid differ: pyvista probably pre-allocates the grid before storing data in it, whereas vtk_ugrid grows the arrays as needed, ending up with a reserved size that is larger than the number of values.

Here is a work-around until I fix the bug:

    grid = vtkUnstructuredGrid()
    grid.AllocateExact(cell_num, 4*cell_num)

That’s it. Just pre-allocate the grid to exactly the size it will need once it is built. Then the bug in vtkDataSetTriangleFilter won’t be triggerred.