Problems using vtkFloatArray?

Ok - I read in some data from a file using:

import numpy as np
with open("binary_stuff.dat", "rb") as f:
    d = np.fromfile(f, dtype=">f4", count=1024)

# verify no NaNs, etc:
assert not np.any(np.isnan(d))
print(f"(min, max): ({d.min()}, {d.max()})")

# which returns:
# -769.35077, 142235120000.0

Note: the datafile is big endian, while my computer is little endian.
I had been trying to add this to a VTK dataset using something like this:

a = vtk.vtkFloatArray()
a.SetNumberOfComponents(1)
a.SetNumberOfTuples(d.size)
a.SetName("Weirdness")
for s in d.ravel():
    a.InsertNextValue(s)

print(a.GetValueRange())
(-1.9957235928163046e+38, 1.3434351138279377e+38)

# I also tried:
# a = numpy_to_vtk(d.ravel())

I’m sure whatever it is that is messing me up, is likely simple … I hope.

It’s because you call SetNumberOfTuples() before calling InsertNextValue().

Calling SetNumberOfTuples(n) will set the size of the array to n. All n values in the array will be left uninitialized. Then, InsertNextValue(x) will add x to the end of the array, giving an array size of n+1.

Here are two simple ways to fix this:

  1. remove SetNumberOfTuples(), or
  2. use for i,s in enumerate(d.ravel()): a.SetValue(i, s)

That worked! Thanks!

Any idea why a = numpy_to_vtk(d.ravel()) didn’t work?

Probably because d.ravel() is a temporary array. The purpose of numpy_to_vtk() is to create VTK array whose contents are just a pointer to the internal buffer of the numpy array. So if the numpy array is deleted or resized, then the VTK array ends up with a dangling pointer.

It should work if you do this, which will force VTK to make a copy of the array memory:

    a = numpy_to_vtk(d.ravel(), deep=True)

Ok - the first method you mentioned, worked, but I tried three different ways with numpy_to_vtk:

Just using deep copy:

a = numpy_to_vtk(d.ravel(), deep=True)
DEBUG:root:b'e': min:-0.4677600860595703, max:1000000.0
DEBUG:root:vtk b'e': min:-3.385234763601098e+38, max:3.3478228449666822e+38

Storing a reference to the ravel-ed array:

b = d.ravel()
a = numpy_to_vtk(b)
DEBUG:root:b'e': min:-0.4677600860595703, max:1000000.0
DEBUG:root:vtk b'e': min:-3.385234763601098e+38, max:3.3478228449666822e+38

Storing a reference to the ravel-ed array and deep copying:

b = d.ravel()
a = numpy_to_vtk(b, deep=True)
DEBUG:root:b'e': min:-0.46879827976226807, max:1282962.25
DEBUG:root:vtk b'e': min:-3.388351155836699e+38, max:3.324693596351062e+38

Also, even with the first working(?) example, when I pull up the resulting file in ParaView, the ranges are all +/- 1e38 …

Try with a simple hand-built array to see if that works:

b = np.array([1, 2], dtype=np.float32)
a = numpy_to_vtk(b)
print(a.GetValue(0), a.GetValue(1))

Yours works fine:

rexthor@workstation:~ $ python3
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> from vtk.util.numpy_support import numpy_to_vtk
>>> b = np.array([1, 2], dtype=np.float32)
>>> a = numpy_to_vtk(b)
>>> a.GetValue(0)
1.0
>>> a.GetValue(1)
2.0
>>> a.GetValueRange()
(1.0, 2.0)

However, when you throw big-endian stuff into the mix:

import struct
buffer = struct.pack(">ff", 1, 2)
a = np.frombuffer(buffer, dtype=">f4")
c = numpy_to_vtk(a)
c.GetValueRange()

(8.96831017167883e-44, 4.600602988224807e-41)

Try byteswapping the numpy array before converting to a VTK array.

Wow - I didn’t even know numpy did that …

import numpy as np
from vtk.util.numpy_support import numpy_to_vtk
import struct
buffer = struct.pack(">ff", 1, 2)
a = np.frombuffer(buffer, dtype=">f4").byteswap()
c = numpy_to_vtk(a)
print(c.GetValueRange())

That gives the correct value range, and, fixes another issue I was having … Thanks!