Changes in VTK lagacy format from V3.0 to V5.0 - causes cell offset error

I’m trying to write a legacy VTK file for an unstructured tetrahedral grid. All of the VTK documentation I can find suggests that the cell linkage block should have a header as follows
CELLS <# cells> <total number of points in all cells + # points in each cell array>
e.g.
CELLS 2233592 11167960
4 51509 26695 53716 54650
4 225124 286911 289432 347607
If I do that with a VTK header of # vtk DataFile Version 5.0
I get an error on import to Paraview
ERROR: In vtkDataReader.cxx, line 3335
vtkUnstructuredGridReader (0x6a6e0480): Error reading cell array offset header.
If I change the VTK header to # vtk DataFile Version 3.0 the file loads correctly
Taking some hints from the xml format documentation and AI I tried changing the cell header line to
CELLS <# cells>
e.g.
CELLS 2233592 8934368
51509 26695 53716 54650
225124 286911 289432 347607
but that just gave me the same array offset read error.

All but one of the example Unstructured grid VTK files in the PV download are for Version <=3. The one example with Version 5 is binary. If I import it and export to ascii from PV it looks like I’m missing the OFFSET and CONNECTIVITY arrays but the CELLS line format looks like it should be

CELLS <# cells+1>

It also appears that if you have n cells then you have n+1 elements in the OFFSET array?

so the CELLS header format could be

CELLS

What is the correct syntax for V5.0 files, please? Better still can someone point me to a VTK file format manual which describes it, preferably with an example.

Also it appears that Paraview is limited to 256 characters on a line in an ASCII VTK. Is that a VTK limitation of one imposed by the Paraview reader?

Hello @KimF ,

tbh, if it’s possible for you, I recommend you to write it in HDF5, there is multiple examples showing how to do that following the VTKHDF File Format.

Lucas

That would require rewriting over a thousand lines of code in my vtk library which would take me a day or two so I’d rather avoid it if I can. Also the only HDF5 writers I have been able to find are in python and so either require continual maintenance or won’t work in a years time as the support libraries change without backward compatibility. I’m only really interested in solutions that will still work in 20 years time. A stretch I know but I’m still compiling code that was written in the 70s and runs perfectly. Also my VTKs are typically not huge (a few gig at most) and thus not warranting the compaction of an HDF5 container. If size becomes an issue I could switch to binary relatively easily as I made allowances for that when I wrote the libraries, just need to add a few lines of code to do the work.

The current web based VTK manual documents the pre V5 approach for ASCII legacy files. Is there anywhere where the V5 ASCII format is documented?

The editor has removed some of the variables after CELLS so as displayed it does not make sense sorry. There are two integers after CELLS but the second has been dropped from the top two examples and both are missing from the last, not sure what is going on there but hopefully it doesn’t destroy the meaning

@spyridon97 Is this related to cell array changes?

This is related to the vtkCellArray changes introduced by Allison Vacanti back in 2019.

What happened back then, the cell array rather than being

numPoints, point_1…point_n
numPoints, point_1…point_n
numPoints, point_1…point_n
numPoints, point_1…point_n

it has a connectivity array

point_1…point_n
point_1…point_n
point_1…point_n

and an offset array

0, n, n2, n3…

Theoretically speaking, the vtkUnstructuredGridReader.cxx

as you can see below has a path between old and new versions,
so nothing stops you from reading an old file.

      else if (!strncmp(line, "cells", 5))
      {
        if (this->FileMajorVersion >= 5)
        {
          // Just read all of the cells. The legacy path goes through the
          // streaming API, but hardcodes piece / numpieces to 0/1...
          vtkSmartPointer<vtkCellArray> tmpCells;
          if (!this->ReadCells(tmpCells))
          {
            this->CloseVTKFile();
            return 1;
          }

          cells = tmpCells;
          cells->Register(nullptr);
        }
        else // maj vers >= 5
        {    // we still want to support the pre-5.x cell format:
          piece = 0;
          numPieces = 1;
          if (!(this->Read(&ncells) && this->Read(&size)))
          {
            vtkErrorMacro(<< "Cannot read cells!");
            this->CloseVTKFile();
            return 1;
          }

          // the number of ints to read before we get to the piece.
          skip1 = piece * ncells / numPieces;
          // the number of ints to read as part of piece.
          read2 = ((piece + 1) * ncells / numPieces) - skip1;
          // the number of ints after the piece
          skip3 = ncells - skip1 - read2;

          const std::size_t connSize = static_cast<std::size_t>(size);
          std::vector<int> tempArray(connSize);
          std::vector<vtkIdType> idArray(static_cast<std::size_t>(size));

          if (!this->ReadCellsLegacy(size, tempArray.data(), skip1, read2, skip3))
          {
            this->CloseVTKFile();
            return 1;
          }

          for (std::size_t connIdx = 0; connIdx < connSize; connIdx++)
          {
            idArray[connIdx] = static_cast<vtkIdType>(tempArray[connIdx]);
          }

          cells = vtkCellArray::New();
          cells->ImportLegacyFormat(idArray.data(), size);
        }

        // Update the dataset
        if (cells && types)
        {
          output->SetCells(types, cells);
        }
      }

The best way to create an unstructured grid with the legacy reader is to create an unstructured cells source and save it as a vtk file with ASCII so that you can see a new version example.

This is a version 5.1 example.
2hexes.vtk (644 Bytes)

This is a version 4.1 example.
2hexes_old.vtk (594 Bytes)

Thanks for the examples Spiros.

My current workaround is, as you suggest above, to set the VTK version to 2.0.

ELSE IF (MODE.EQ.2) THEN
IF (ITYPE.EQ.0) THEN
IF(IVTKDATST==4 .OR. IVTKDATST==3) THEN
HLINE=‘# vtk DataFile Version 2.0’
ELSE
HLINE=‘# vtk DataFile Version 5.0’
END IF
WRITE(LUH,‘(A)’,IOSTAT=ISTAT)TRIM(HLINE)

apologies if the editor has stripped the loop indenting at your end, I couldn’t see how I added a code box as you’ve done

However that requires that someone at kitware in 10 years time doesn’t decide that pre V5 is too hard to support and drops it. If I can support the current version then it would be good to do so.

With your examples and those I have created from PV I think I now have enough to work with to be able to knock something up.

It would be great if at some point the VTK manual reflected these options too :smiley: