Programmable filter threshold on cell/point data

Hi.

I am trying to create a programmable filter right after a Connectivity filter which splits my surface into N different regions (they get different Region Ids). These are available as cell/point data.

I would like to filter the Connectivity dataset by Region, and was thinking to simply use the Threshold function (i.e. threshold min/max RegionId == 1 ).

The reason I want to automate into a programmable filter is to do this in a for loop to go over every N regions.

Any thoughts? Thanks!!

1 Like

Hi @caladoa and welcome to discourse ! Thanks for starting a new thread for your issue.

Can you please describe more your intended use ? Do you want to extract the connected components with a script and execute some pipeline on them ? Or you are looking have all of them available as separate objects in ParaView pipeline browser ?

I want to compute the volume separately of each of these closed surfaces (using tetrahedra sum as exemplified in the tutorials). I was thinking if I can somehow filter inputs[0] by region id, then I can just have a for loop to compute the volume for each of these separately in a np array.

Yes it is possible! Using the programmable filter you need to figure out what is your output and then how to populate it. From what I understand you will be generating a table with the volumes ? If this is the case here is a mockup where we apply threshold on a dataset to extract cells by object_id and populate a table with the number of points for each group.

And here is the script

import vtk
import numpy as np

input0 = inputs[0] 
results = []
for i in range(50):
    f = vtk.vtkThreshold()
    f.SetInputData(input0.VTKObject)
    f.SetThresholdFunction(vtk.vtkThreshold.THRESHOLD_BETWEEN)
    f.SetLowerThreshold(i)
    f.SetUpperThreshold(i)
    f.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS, 'object_id')
    f.Update()
    results.append(f.GetOutput().GetNumberOfPoints())

output.RowData.append(np.array(results),"npoints")

For more ideas and hints check these examples .

1 Like

Thanks! I think this is close, however I am still getting some errors. Attached is a screenshot of the volumes, you can see they have different ‘RegionId’ comming from the ‘Connectivity’ Filter.

I am trying to loop for different RegionId and store the volume for each one, so I can compute after with a Python Calculator. Below is the code of the programmable filter I am trying.

import vtk
import numpy as np

k=5 # number of regions total

input0 = inputs[0] 

for i in range(k):
    f = vtk.vtkThreshold()
    f.SetInputData(input0.VTKObject)
    f.SetThresholdFunction(vtk.vtkThreshold.THRESHOLD_BETWEEN)
    f.SetLowerThreshold(i)
    f.SetUpperThreshold(i)
    f.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS, 'RegionId')
    f.Update()

    numTriangles = f.GetNumberOfCells()
    volumeArray = np.empty([numTriangles,k], dtype=np.float64)
    for j in range(numTriangles):
       cell = f.GetCell(j)
       p1 = f.GetPoint(cell.GetPointId(0))
       p2 = f.GetPoint(cell.GetPointId(1))
       p3 = f.GetPoint(cell.GetPointId(2))
       p4 = [1.058105,2.47113,-0.209546] # use barycenter of surface for better results.
       #p4 = [1.0,1.0,-1.0]
       volumeArray[j,i] = vtk.vtkTetra.ComputeVolume(p1,p2,p4,p3)

    output.CellData.append(volumeArray[:,i], "Bub_Vol")

What kind of errors are you getting ?

Also

 numTriangles = f.GetNumberOfCells()

this should be f.GetOutput().GetNumberOfCells()

f is the filter itself . Same for GetCell() you need to apply it on the output of the filter f.

Thanks, almost there I think :slight_smile:

I tweaked the code based on your suggestion.

import vtk
import numpy as np

k=5 # number of regions total

input0 = inputs[0] 

for i in range(k):
    f = vtk.vtkThreshold()
    f.SetInputData(input0.VTKObject)
    f.SetThresholdFunction(vtk.vtkThreshold.THRESHOLD_BETWEEN)
    f.SetLowerThreshold(i)
    f.SetUpperThreshold(i)
    f.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS, 'RegionId')
    f.Update()

    input1 = f.GetOutput()

    numTriangles = input1.GetNumberOfCells()
    volumeArray = np.empty([numTriangles,k], dtype=np.float64)
    for j in range(numTriangles):
       cell = input1.GetCell(j)
       p1 = input1.GetPoint(cell.GetPointId(0))
       p2 = input1.GetPoint(cell.GetPointId(1))
       p3 = input1.GetPoint(cell.GetPointId(2))
       p4 = [1.058105,2.47113,-0.209546] # use barycenter of surface for better results.
       #p4 = [1.0,1.0,-1.0]
       volumeArray[j,i] = vtk.vtkTetra.ComputeVolume(p1,p2,p4,p3)

    output.CellData.append(volumeArray[:,i], "Bub_Vol")



It appears I am getting something when I look at spreadsheet view (see below), but it doesnt make sense. the rows are all the same. In the end I just want an array of the total volumes (5 values in my case), I dont really care about the individual contributions, just need the sum of the array if this makes sense.

this is the error.

Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 17, in RequestData
  File "/Applications/ParaView-5.10.1.app/Contents/Python/vtkmodules/numpy_interface/dataset_adapter.py", line 130, in __getattr__
    return getattr(self.VTKObject, name)
AttributeError: 'vtkmodules.vtkCommonDataModel.vtkUnstructuredGrid' object has no attribute 'RowData'
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 17, in RequestData
  File "/Applications/ParaView-5.10.1.app/Contents/Python/vtkmodules/numpy_interface/dataset_adapter.py", line 130, in __getattr__
    return getattr(self.VTKObject, name)
AttributeError: 'vtkmodules.vtkCommonDataModel.vtkUnstructuredGrid' object has no attribute 'RowData'
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 17, in RequestData
  File "/Applications/ParaView-5.10.1.app/Contents/Python/vtkmodules/numpy_interface/dataset_adapter.py", line 130, in __getattr__
    return getattr(self.VTKObject, name)
AttributeError: 'vtkmodules.vtkCommonDataModel.vtkUnstructuredGrid' object has no attribute 'RowData'
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 17, in RequestData
  File "/Applications/ParaView-5.10.1.app/Contents/Python/vtkmodules/numpy_interface/dataset_adapter.py", line 130, in __getattr__
    return getattr(self.VTKObject, name)
AttributeError: 'vtkmodules.vtkCommonDataModel.vtkUnstructuredGrid' object has no attribute 'RowData'
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 17, in RequestData
  File "/Applications/ParaView-5.10.1.app/Contents/Python/vtkmodules/numpy_interface/dataset_adapter.py", line 130, in __getattr__
    return getattr(self.VTKObject, name)
AttributeError: 'vtkmodules.vtkCommonDataModel.vtkUnstructuredGrid' object has no attribute 'RowData'
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 18, in RequestData
AttributeError: 'vtkmodules.vtkFiltersCore.vtkThreshold' object has no attribute 'GetNumberOfCells'
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 18, in RequestData
AttributeError: 'vtkmodules.vtkFiltersCore.vtkThreshold' object has no attribute 'GetNumberOfCells'
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 18, in RequestData
AttributeError: 'vtkmodules.vtkFiltersCore.vtkThreshold' object has no attribute 'GetNumberOfCells'
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 18, in RequestData
AttributeError: 'vtkmodules.vtkFiltersCore.vtkThreshold' object has no attribute 'GetNumberOfCells'
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1
    sum(
       ^
SyntaxError: unexpected EOF while parsing
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 18, in RequestData
AttributeError: 'vtkmodules.vtkFiltersCore.vtkThreshold' object has no attribute 'GetNumberOfCells'
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1
    sum(
       ^
SyntaxError: unexpected EOF while parsing
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 18, in RequestData
AttributeError: 'vtkmodules.vtkFiltersCore.vtkThreshold' object has no attribute 'GetNumberOfCells'
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
ERROR: In /opt/glr/paraview/paraview-ci/build/superbuild/paraview/src/VTK/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx, line 760
vtkPVDataRepresentationPipeline (0x600005de00f0): Input for connection index 0 on input port index 0 for algorithm vtkGeometryRepresentationWithFaces(0x16fd06fa0) is of type vtkTable, but a vtkDataSet is required.

ERROR: In /opt/glr/paraview/paraview-ci/build/superbuild/paraview/src/VTK/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx, line 760
vtkPVDataRepresentationPipeline (0x600005d0ab20): Input for connection index 0 on input port index 0 for algorithm vtkGeometryRepresentation(0x12fe67250) is of type vtkTable, but a vtkDataSet is required.

ERROR: In /opt/glr/paraview/paraview-ci/build/superbuild/paraview/src/VTK/Common/ExecutionModel/vtkDemandDrivenPipeline.cxx, line 760
vtkPVDataRepresentationPipeline (0x600005d05680): Input for connection index 0 on input port index 0 for algorithm vtkPVGridAxes3DRepresentation(0x164245290) is of type vtkTable, but a vtkCompositeDataSet is required.

Traceback (most recent call last):
  File "<string>", line 22, in <module>
  File "<string>", line 18, in RequestData
AttributeError: 'vtkmodules.vtkFiltersCore.vtkThreshold' object has no attribute 'GetNumberOfCells'
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
0.004158140694556545
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
nan
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
1.71592883706e-312
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined
Traceback (most recent call last):
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 203, in execute
    retVal = compute(inputs, expression, ns=variables)
  File "/Applications/ParaView-5.10.1.app/Contents/Python/paraview/detail/calculator.py", line 146, in compute
    retVal = eval(subEx, globals(), mylocals)
  File "<string>", line 1, in <module>
NameError: name 'Bub_Vol' is not defined```

UPDATE:

I think I have managed to get it working. Although I am a bit suspicious of the final total volume. While the larger bodies seem to have relatively larger volume, the magnitude seems exaggerated…

I think I am not filtering by the correct metric ??? If I compare this result, with another one where I do the single volume computation on the already thresholded dataset, I get different values, and the one with the manual threshold seems to be the correct one… The strange thing is if I look at the computed xmin/xmax from the bounds, they seem to match the correct threshold so I am very confused :confused:

I also managed to compute the barycenter of each volume from the GetBounds() function which should in theory help with the integration.

Below is the current code. If you spot any errors please let me know :wink:

import vtk
import numpy as np

k=6 # number of regions total

input0 = inputs[0] 

for i in range(k):
    #print(i)
    f = vtk.vtkThreshold()
    f.SetInputData(input0.VTKObject)
    f.SetThresholdFunction(vtk.vtkThreshold.THRESHOLD_BETWEEN)
    f.SetLowerThreshold(i)
    f.SetUpperThreshold(i)
    f.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS, 'RegionId')
    f.Update()

    input1 = f.GetOutput()
    [xmin,xmax,ymin,ymax,zmin,zmax] = input1.GetBounds()
    #print(xmin)
    # compute centroid

    xavg = xmin*0.5 + xmax*0.5
    yavg = ymin*0.5 + ymax*0.5
    zavg = zmin*0.5 + zmax*0.5

    numTriangles = input1.GetNumberOfCells()
    volumeArray = np.empty(numTriangles, dtype=np.float64)
    for j in range(numTriangles):
       cell = input1.GetCell(j)
       p1 = input1.GetPoint(cell.GetPointId(0))
       p2 = input1.GetPoint(cell.GetPointId(1))
       p3 = input1.GetPoint(cell.GetPointId(2))
       #p4 = [1.058105,2.47113,-0.209546] # use barycenter of surface for better results.
       p4 = [xavg,yavg,zavg]
       volumeArray[j] = vtk.vtkTetra.ComputeVolume(p1,p2,p4,p3)

    output.CellData.append(volumeArray, f"Bub_Vol_{i}")
    ```

Not sure what might be wrong. An important detail of this method of integration is that all triangles need to be orientated the same way, i.e. pointing outwards/inwards from the volume using the right-hand rule. Use Normal Glyphs to inspect the orientation . You can also save the threshold output to a mesh for further inspection using vtkPolyDataWriter.

Indeed it is very confusing because the number if triangles is OK and the computed centroid… but the total is orders of magnitude larger than it should be. Maybe the sum is incorrect?

Is there an alternative to store the output as a table vs cell data?

I feel like I am very close to the solution.

Thanks so much.

Figured out the issue. It seems the Cell Data array that is generated is a larger table than it should be :confused: so there are duplicates of rows. I think the total table is going to use the largest number of cells??? I was able to solve the issue in the Python calculator by summing only a single row (the first one) and all columns as:

sum(Bub_Vol_5[0,:])

This now gives me the same result as the manual threshold :slight_smile:

I would still appreciate any suggestions to eliminate the redundancy, or how can I get a Table for the row-sum of all the different bubbles. Right now I have to go to the Python Calculator and manually change the number (1, 2, 3, …) to get the result. I was thinking of using the += operator so we dont store the intermediate volumes.

Here is the screenshot of the current programmable filter with thresholding. We should have just 8 rows (8 cells) for the Bubble 0 but we get a bunch more!

And the manual threshold for single volume - 8 rows only

so there are duplicates of rows.

Since you are calculating a whole row each time why not use

output.RowData.append(volumeArray, f"Bub_Vol_{i}")

instead of CellData.append ?

It looks from the screenshots that you are creating a table of arrays which is not what you want if I understand correctly.

1 Like

Correct :slight_smile: Thanks so much for the help!!

I switched to Row Data and am using += as a simple scalar instead of the empty array which is not needed.

import vtk
from vtk.numpy_interface import algorithms as algs
import numpy as np

k=6 # number of regions total

input0 = inputs[0]
#input0 = self.GetInputDataObject(0, 0) # Safer method
results=np.zeros(k)

for i in range(k):
    #print(i)
    f = vtk.vtkThreshold()
    f.SetInputData(input0.VTKObject)
    #f.SetInput(input0)
    #f.Scalars = ("POINTS","RegionId")
    f.SetThresholdFunction(vtk.vtkThreshold.THRESHOLD_BETWEEN)
    f.SetLowerThreshold(i)
    f.SetUpperThreshold(i)
    f.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS, "RegionId")
    f.Update()


    input1 = f.GetOutput()
    [xmin,xmax,ymin,ymax,zmin,zmax] = input1.GetBounds()
    #print(xmin)
    # compute centroid

    xavg = xmin*0.5 + xmax*0.5
    yavg = ymin*0.5 + ymax*0.5
    zavg = zmin*0.5 + zmax*0.5
    #print(xavg)

    numTriangles = input1.GetNumberOfCells()
    #print(numTriangles)
    volumeArray = np.empty(numTriangles, dtype=np.float64)
    sumVol = 0.
    for j in range(numTriangles):
        cell = input1.GetCell(j)
        p1 = input1.GetPoint(cell.GetPointId(0))
        p2 = input1.GetPoint(cell.GetPointId(1))
        p3 = input1.GetPoint(cell.GetPointId(2))
        #p4 = [1.058105,2.47113,-0.209546] # use barycenter of surface for better results.
        p4 = [xavg,yavg,zavg]
        #print(p4)
        #volumeArray += vtk.vtkTetra.ComputeVolume(p1,p2,p4,p3)
        #volumeArray[j] = vtk.vtkTetra.ComputeVolume(p1,p2,p4,p3)
        sumVol += vtk.vtkTetra.ComputeVolume(p1,p2,p4,p3)

        #results[i] += vtk.vtkTetra.ComputeVolume(p1,p2,p4,p3)
        #results.append(volumeArray)

    #output.CellData.append(volumeArray, f"Bub_Vol_{i}")
    output.RowData.append(sumVol, f"Total_Vol_{i}")

    





1 Like

Final question: how would I get the total surface area? Is there a function to compute the individual triangle area of p1,p2,p3 so I can sum them all up?

Cheers

Given your setup I think the easiest is to use the static function similar to the one for vtkTetra see here
https://vtk.org/doc/nightly/html/classvtkTriangle.html#a6d9f2b0ec8159cd34da2b168a7db5575

Thanks. I used vtk.vtkTriangle.ComputeArea(cell) and seems to work fine!

Bumping this again. I noticed that if running paraview in parallel (say 20 cores), I get 20 rows (one for each process) in the area/volume stats. It seems to me that to get the correct total value, one should sum up for all the rows (this is assuming that Paraview splits the domain in disjointed regions, so no overlap). Can anyone confirm this to be the case?

@Christos_Tsolakis I noticed an issue recently when using this script to compute the total volume / area of a surface: I have the data in blocks from my XMF file, so the pipeline uses Merge Blocks > Ghost Cell Generator > Cell data to point data > Contour > Connectivity

I noticed I get different values of area / volume depending on the number of ghost cells used. I looked at the simple shape of a sphere, and noticed that I need to have at least 2 ghost cells to get a single region (typically I use 3 to be safe).

I noticed for the area that the values are always overestimated and grow linearly with number of ghost cells… I am summing the values in all rows for same column, since I am using Paraview in parallel (over multiple machines).

The plot below shows that as number of ghost cells grows, the sphere area grows linearly. This is very strange behavior, I suspect the ghost cells are also being thrown into the integration and so misleading the final result? Any ideas how I can fix or account for this? Thanks!

image

FYI , apparently issue is solved if after “Merge Blocks” you add the “Aggregate Dataset” and reduce to single processor.

1 Like

Back to this topic: now I am trying to access point data on the threshold surface (polygonal mesh).

How would I access interpolated data? I tried the following but am getting an error:

within the “i” loop I have

vortvector = input1.PointData[‘Vorticity’]

AttributeError: ‘vtkmodules.vtkCommonDataModel.vtkUnstructuredGrid’ object has no attribute ‘PointData’