Generating streamlines for random vector fields with Python

I’m trying to build a visualization tool for 3D vector fields (could be any vector field). All I know is that the vector field is discretized in a cubic region.

import k3d
import vtk
from vtk.util import numpy_support
import numpy as np

n = 20
x = y = z = np.linspace(-5, 5, n)
xx, yy, zz = np.meshgrid(x, y, z)
# very simply vector field
uu, vv, ww = zz, yy, xx

xx, yy, zz, uu, vv, ww = [t.flatten().astype(np.float32) for t
                    in [xx, yy, zz, uu, vv, ww]]
plot = k3d.plot(camera_auto_fit=False)

magnitude = np.sqrt(uu**2 + vv**2 + ww**2)
scale = 0.1
vectors = np.array((uu, vv, ww)).T * scale
origins = np.array((xx, yy, zz)).T
colors = k3d.helpers.map_colors(magnitude, k3d.basic_color_maps.CoolWarm, [])
vec_colors = np.zeros(2 * len(colors))
for i, c in enumerate(colors):
    vec_colors[2 * i] = c
    vec_colors[2 * i + 1] = c

vec = k3d.vectors(
    origins = origins - vectors / 2,
    vectors = vectors,
    colors = vec_colors.astype(np.uint32),
)

plot += vec
plot.display()

This is the picture I get:

Clearly, this is hardly understandable, so I would like to create streamlines. Since I don’t know the vector field expressions, I thought I could use a random cloud of seed points for the streamlines.

vector_field = np.array([uu, vv, ww]).T
vtk_vector_field = numpy_support.numpy_to_vtk(num_array=vector_field, deep=True, array_type=vtk.VTK_FLOAT)
vtk_vector_field.SetName("vector_field")

# Grid Generation
points = vtk.vtkPoints()
points.SetNumberOfPoints(n**3)
for i, (x, y, z) in enumerate(zip(xx, yy, zz)):
    points.SetPoint(i, [x, y, z])
    
grid = vtk.vtkStructuredGrid()
grid.SetDimensions([n, n, n])
grid.SetPoints(points)
grid.GetPointData().SetVectors( vtk_vector_field )

# Seeds for the streamlines
seeds = vtk.vtkPointSource()
seeds.SetRadius(5)
seeds.SetCenter(0, 0, 0)
seeds.SetNumberOfPoints(1000)

streamer = vtk.vtkStreamTracer()
streamer.SetInputData(grid)
streamer.SetSourceConnection(seeds.GetOutputPort())
streamer.SetIntegrationDirectionToForward()
streamer.SetComputeVorticity(0)
streamer.SetIntegrator(vtk.vtkRungeKutta4())
streamer.Update()

streamline = streamer.GetOutput()
streamlines_points = numpy_support.vtk_to_numpy(streamline.GetPoints().GetData())
streamlines_velocity = numpy_support.vtk_to_numpy(streamline.GetPointData().GetArray('vector_field'))
streamlines_speed = np.linalg.norm(streamlines_velocity, axis=1)

vtkLines = streamline.GetLines()
vtkLines.InitTraversal()
point_list = vtk.vtkIdList()

lines = []
lines_attributes = []

while vtkLines.GetNextCell(point_list):
    start_id = point_list.GetId(0)
    end_id = point_list.GetId(point_list.GetNumberOfIds() - 1)
    l= []
    v= []

    for i in range(start_id, end_id):
        l.append(streamlines_points[i])
        v.append(streamlines_speed[i])

    lines.append(np.array(l))
    lines_attributes.append(np.array(v))

At this point, I have generated almost 1000 lines, each one having 3 points (2 segments). There is one major problem with this approach: while K3D is great at visualizing single meshes (having thousands of cells), it is extremely slow at loading them. In this case, visualizing a 1000 lines is not going to work (it takes forever). This is what the streamlines looks like after loading 400 lines:

We can actually start to see the pattern.

The question is: is there a better strategy to chose seeds for streamlines? What would you do differently?