Problem generating non-orthogonal structured grid in Python plugin with temporal data

Hi ParaView developers,

I’m developing a Python plugin for ParaView that produces time-evolving datasets. The goal is to generate a structured 3D grid with full control of the coordinates (x, y, z), where:

  • x and y can have non-uniform spacing (non-orthogonal curvilinear grid),

  • z can have non-constant vertical spacing.

I currently have a working plugin that produces a rectilinear grid with temporal scalar fields, inspired by the TemporalTestDataSource.py example from ParaView:
https://gitlab.kitware.com/paraview/paraview/-/blob/master/Examples/Plugins/TemporalTestDataSource/TemporalTestDataSource.py

Here’s a summary of what I have implemented:

  1. Time management:

    • ***_get_timesteps()*** returns an array of time values.

    • ***RequestInformation()*** sets the time steps and time range in ParaView.

  2. Grid generation in RequestData():

    • Currently using ***vtkRectilinearGrid***.

    • Scalars are generated per cell, one array per ***_numOfArrays***.

    • FieldData stores the current time.

  3. Controls exposed as sliders in the ParaView GUI:

    • TimeSteps: number of timesteps.

    • z Scale: scaling factor applied to the vertical coordinate.

    • ArrayCount: number of scalar arrays per timestep.

    • Value Offset: arbitrary offset added to the generated scalar values.

  4. Observed problem:

    • When trying to switch to ***vtkStructuredGrid*** and fully control x, y, z coordinates, the grid appears empty in ParaView — no points, no cells, no outline.

    • I have tried various approaches:

      • Assigning coordinates as ***vtkPoints*** and using ***SetDimensions()***.

      • Using ***numpy.meshgrid*** with different flattening orders (C vs F).

      • Attempting ***vtkStructuredGrid*** with cell-centered scalars.

      • Trying ***vtkUnstructuredGrid*** with manually defined hexahedral cells.

      • Attempting ***vtkPolyData*** with explicit quads for each voxel.

None of these approaches produces a visible grid in ParaView when embedded in the plugin pipeline. Standalone scripts work fine (e.g., generating a structured grid in pure Python and writing to .vts), but embedding it in ***RequestData()*** fails.

Goal: I want a fully working structured grid plugin that:

  • Has time-dependent scalar fields.

  • Allows arbitrary x, y, z coordinates (non-uniform / curvilinear).

  • Works immediately in ParaView without external files.


Plugin sliders explanation:

  • TimeSteps: controls how many time steps the source produces.

  • z Scale: scales the vertical dimension (useful to exaggerate vertical spacing).

  • ArrayCount: number of independent scalar arrays generated per timestep.

  • Value Offset: shifts the scalar values globally.


Question to the community:

  • How can I properly generate a vtkStructuredGrid with arbitrary curvilinear coordinates inside RequestData(), so that it is visible and renders correctly in ParaView?

  • Are there special ordering rules for points or dimensions when using structured grids in a plugin?

  • Any working minimal example of time-dependent structured grid with non-uniform x/y/z in a Python plugin would be greatly appreciated.

Thanks!

Plugin code skeleton (rectilinear working version):

from paraview.util.vtkAlgorithm import *
import vtk
import numpy as np
from vtkmodules.util import numpy_support

@smproxy.source(name=“TimeSourceStructured”,
label=“Time Source Structured Grid with Scalars”)
class TimeSourceStructured(VTKPythonAlgorithmBase):
def init(self):
VTKPythonAlgorithmBase.init(self,
nInputPorts=0, nOutputPorts=1,
outputType=‘vtkRectilinearGrid’)

    self.nx, self.ny, self.nz = 115, 85, 32
    self._numOfValues = 10
    self._numOfArrays = 2
    self._valueOffset = 0.0
    self._zscale = 1.0

def _get_timesteps(self):
    return np.arange(self._numOfValues, dtype=np.float64)

def RequestInformation(self, request, inInfoVec, outInfoVec):
    outInfo = outInfoVec.GetInformationObject(0)
    timesteps = self._get_timesteps()
    outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_STEPS(), timesteps, len(timesteps))
    outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_RANGE(), [timesteps[0], timesteps[-1]], 2)
    return 1

def _get_current_time(self, outInfo):
    timesteps = self._get_timesteps()
    if outInfo.Has(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP()):
        return outInfo.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP())
    else:
        return timesteps[0]

def RequestData(self, request, inInfoVec, outInfoVec):
    outInfo = outInfoVec.GetInformationObject(0)
    nx, ny, nz = self.nx, self.ny, self.nz

    x_coords = np.linspace(0, nx-1, nx, dtype=np.float32)
    y_coords = np.linspace(0, ny-1, ny, dtype=np.float32)
    z_coords = self._zscale * np.linspace(0, nz-1, nz, dtype=np.float32)

    vtk_x = numpy_support.numpy_to_vtk(x_coords, deep=True)
    vtk_y = numpy_support.numpy_to_vtk(y_coords, deep=True)
    vtk_z = numpy_support.numpy_to_vtk(z_coords, deep=True)

    grid = vtk.vtkRectilinearGrid()
    grid.SetDimensions(nx, ny, nz)
    grid.SetXCoordinates(vtk_x)
    grid.SetYCoordinates(vtk_y)
    grid.SetZCoordinates(vtk_z)

    t = self._get_current_time(outInfo)

    gx, gy, gz = np.meshgrid(x_coords[:-1]+0.5, y_coords[:-1]+0.5, z_coords[:-1]+0.5, indexing='ij')
    gx, gy, gz = gx.ravel(), gy.ravel(), gz.ravel()

    for k in range(self._numOfArrays):
        arr = np.sin(0.2*t + 0.1*k + 0.05*gx + 0.05*gy + 0.05*gz)
        vtkarr = numpy_support.numpy_to_vtk(arr, deep=True)
        vtkarr.SetName(f"Array_{k}")
        grid.GetCellData().AddArray(vtkarr)
        if k == 0:
            grid.GetCellData().SetScalars(vtkarr)

    time_array = vtk.vtkDoubleArray()
    time_array.SetName("TIME")
    time_array.SetNumberOfTuples(1)
    time_array.SetValue(0, t)
    grid.GetFieldData().AddArray(time_array)

    output = self.GetOutputData(outInfoVec, 0)
    output.ShallowCopy(grid)
    output.GetInformation().Set(output.DATA_TIME_STEP(), t)
    return 1

@smproperty.intvector(name="TimeSteps", default_values=10)
@smdomain.intrange(min=1, max=50)
def SetSteps(self, x):
    self._numOfValues = x
    self.Modified()

@smproperty.doublevector(name="z Scale", default_values=1)
@smdomain.doublerange(min=1.e-6, max=2)
def SetScale(self, x):
    self._zscale = x
    self.Modified()

@smproperty.intvector(name="ArrayCount", default_values=2)
@smdomain.intrange(min=1, max=10)
def SetArrayCount(self, x):
    self._numOfArrays = x
    self.Modified()

@smproperty.doublevector(name="Value Offset", default_values=0.0)
def SetOffset(self, x):
    self._valueOffset = x
    self.Modified()

@smproperty.doublevector(name="TimestepValues", information_only="1", si_class="vtkSITimeStepsProperty")
def GetTimestepValues(self):
    return self._get_timesteps()

Hi Marco

Just create a correct vtkStructuredGrid and it will show up correctly in ParaView

Are there special ordering rules for points or dimensions when using structured grids

Points should indeed be ordered correctly:

Hi Mathieu,

Thank you so much for replying at the speed of light and for the link.
I have followed that and have come across a almost python-ready example:

https://examples.vtk.org/site/Python/StructuredPoints/Vol/

I have simplified down the code to a bare minimum for testing. Now my RequestData is:

def RequestData(self, request, inInfoVec, outInfoVec):
    from vtkmodules.vtkCommonDataModel import vtkStructuredGrid
    from vtkmodules.vtkCommonCore import vtkPoints, vtkDoubleArray

    # --- Get the output from ParaView ---
    output = vtkStructuredGrid.GetData(outInfoVec, 0)

    # --- Grid dimensions ---
    dims = [13, 11, 11]
    output.SetDimensions(dims)

    # --- Points and scalars ---
    numPts = dims[0]*dims[1]*dims[2]
    points = vtkPoints()
    points.SetNumberOfPoints(numPts)         # Must set number of points
    scalars = vtkDoubleArray()
    scalars.SetName("MyScalars")
    scalars.SetNumberOfTuples(numPts)

    # --- Fill grid ---
    offset = 0
    for k in range(dims[2]):
        z = -1.0 + k
        for j in range(dims[1]):
            y = -1.0 + j
            for i in range(dims[0]):
                x = -1.0 + i
                points.SetPoint(offset, x, y, z)
                scalars.SetTuple1(offset, x**2 + y**2 + z**2)
                offset += 1

    # --- Assign to output ---
    output.SetPoints(points)
    output.GetPointData().SetScalars(scalars)


    return 1

As before, the output when initializing is indeed a vtkstructuredGrid:

def __init__(self): VTKPythonAlgorithmBase.__init__(self, nInputPorts=0, nOutputPorts=1, outputType='vtkStructuredGrid')

I see the actual variable and variable name in the dropdown menu.
I can see the colorbar showing up, with right value range.
The inspector tab is telling me everything looks nice, but when debugging:

Number Of Points: 0
Bounds: Xmin,Xmax: (1e+299, -1e+299)

What am I doing wrong?
I tried all day without managing to get a grip on what I might be doing wrong, but I smell I am very close…

When is NumberOfPoints 0 ?

You seem to fill the points correcty.