Python vtkUnstructuredGrid create vtu file with quad and triangle

Hello community,

how do create a .vtu file with quad and triangle elements? I want to convert a FEM input file to a .vtu file.

I have tried to create a python code, but I only get either a .vtu file with only quad or triangle elements. Is there a way to combine both element types to write it to the output file?

import vtk
import numpy as np

inputFile = 'mixed.inp'

with open(inputFile) as f:
    lines = f.readlines()


class Node:
    def __init__(self, nid):
        self.nid = np.uint8(nid)
        self.coordinates = []


class Element:
    def __init__(self, eid: str):
        self.eid = np.uint8(eid)
        self.attached_nodes = []


bNode = False
bElem = False

nodes, elements = {}, {}

for line in lines:
    line = line.strip()

    # NODES
    if line.lower().startswith('*node'):
        bNode = True
        bElem = False

    elif bNode and not line.startswith('*'):

        lineSplit = line.split(',')

        nid = int(lineSplit[0])-1

        nodes[nid] = Node(nid)

        x = lineSplit[1].strip()
        y = lineSplit[2].strip()
        z = lineSplit[3].strip()

        nodes[nid].coordinates = np.array([x, y, z], dtype=np.float64)

    # ELEMENTS
    elif line.lower().startswith('*element'):
        bNode = False
        bElem = True
        lineSplit = line.split(',')
        elemType: str = lineSplit[1].split('=')[1]

    elif bElem and 'S4' == elemType.upper():
        if not line.startswith('**') or not line.startswith('*'):
            lineSplit = line.split(',')

            eid = lineSplit[0]
            elements[eid] = Element(eid)

            tmp = []
            for i in [x for x in range(len(lineSplit)-1)]:
                nid = int(lineSplit[i+1])-1
                tmp.append(nid)

            elements[eid].attached_nodes = np.asarray(tmp, dtype=np.uint8)

    elif bElem and 'S3' == elemType.upper():
        if not line.startswith('**') or not line.startswith('*'):
            lineSplit = line.split(',')

            eid = lineSplit[0]
            elements[eid] = Element(eid)

            tmp = []
            for i in [x for x in range(len(lineSplit)-1)]:
                nid = int(lineSplit[i+1])-1
                tmp.append(nid)

            elements[eid].attached_nodes = np.asarray(tmp, dtype=np.uint8)

    else:
        bNode = False
        bElem = False

# for nid in nodes.keys():
#     print(nodes[nid].nid, nodes[nid].coordinates)

# for eid in elements.keys():
#     print(elements[eid].eid, elements[eid].attached_nodes)


# Define VTK Points
vtk_points = vtk.vtkPoints()
for nid in nodes.keys():
    vtk_points.InsertNextPoint(nodes[nid].coordinates[0], nodes[nid].coordinates[1], nodes[nid].coordinates[2])

vtk_quads = vtk.vtkCellArray()
vtk_trias = vtk.vtkCellArray()
for eid in elements.keys():
    # Define quad elements
    if len(elements[eid].attached_nodes) == 4:
        quad = vtk.vtkQuad()
        quad.GetPointIds().SetId(0, elements[eid].attached_nodes[0])
        quad.GetPointIds().SetId(1, elements[eid].attached_nodes[1])
        quad.GetPointIds().SetId(2, elements[eid].attached_nodes[2])
        quad.GetPointIds().SetId(3, elements[eid].attached_nodes[3])
        vtk_quads.InsertNextCell(quad)
    elif len(elements[eid].attached_nodes) == 3:
        tria = vtk.vtkTriangle()
        tria.GetPointIds().SetId(0, elements[eid].attached_nodes[0])
        tria.GetPointIds().SetId(1, elements[eid].attached_nodes[1])
        tria.GetPointIds().SetId(2, elements[eid].attached_nodes[2])
        vtk_trias.InsertNextCell(tria)


# Create unstructured grid
ugrid = vtk.vtkUnstructuredGrid()
ugrid.SetPoints(vtk_points)

# How to merge both cell types ?
ugrid.SetCells(vtk.VTK_TRIANGLE, vtk_trias)
ugrid.SetCells(vtk.VTK_QUAD, vtk_quads)

# Write to binary file
writer = vtk.vtkXMLUnstructuredGridWriter()
writer.SetInputData(ugrid)
writer.SetFileName("output2.vtu")
writer.SetDataModeToBinary()
writer.Write()

Thank you in advance :slight_smile:

This is a possible solution:

vtk_cells = vtk.vtkCellArray()
vtk_elemType = []

for eid in elements.keys():
    # Define quad elements
    if len(elements[eid].attached_nodes) == 4:
        quad = vtk.vtkQuad()
        quad.GetPointIds().SetId(0, elements[eid].attached_nodes[0])
        quad.GetPointIds().SetId(1, elements[eid].attached_nodes[1])
        quad.GetPointIds().SetId(2, elements[eid].attached_nodes[2])
        quad.GetPointIds().SetId(3, elements[eid].attached_nodes[3])
        vtk_cells.InsertNextCell(quad)
        vtk_elemType.append(9)
    # Define tria elements
    elif len(elements[eid].attached_nodes) == 3:
        tria = vtk.vtkTriangle()
        tria.GetPointIds().SetId(0, elements[eid].attached_nodes[0])
        tria.GetPointIds().SetId(1, elements[eid].attached_nodes[1])
        tria.GetPointIds().SetId(2, elements[eid].attached_nodes[2])
        vtk_cells.InsertNextCell(tria)
        vtk_elemType.append(6)

# Create unstructured grid
ugrid = vtk.vtkUnstructuredGrid()
ugrid.SetPoints(vtk_points)
ugrid.SetCells(vtk_elemType,vtk_cells)

Is there a more elegant/shorter way to do so ?