From 019a71041ba4fa59bacef76d6f59a41f36a4febe Mon Sep 17 00:00:00 2001 From: Berk Geveci Date: Thu, 5 Nov 2026 21:12:09 -0500 Subject: [PATCH 1/4] test_arrays_protocol: the permutation's ids first Permutation takes (ids, base); the test gave (base, ids), so the six float values were ids into the three ids, read out of bounds alike by the host and the kernel. It passed in plain mode on the garbage both read; the suite under TACK_DEBUG=1 caught it. Now the ids come first, and the permutation's values are checked. --- packages/tack-core/tests/test_arrays_protocol.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/tack-core/tests/test_arrays_protocol.py b/packages/tack-core/tests/test_arrays_protocol.py index 417dcdd29..8e0de3bad 100644 --- a/packages/tack-core/tests/test_arrays_protocol.py +++ b/packages/tack-core/tests/test_arrays_protocol.py @@ -239,7 +239,10 @@ def test_implicit_arrays_index_from_the_host_as_arrays_do(): ta.CountingArray(5, start=3, step=2), ta.UniformCoordinates((3, 2, 2), origin=(1, 0, 0), spacing=(0.5, 1, 1)), ta.CartesianProduct([0.0, 1.0, 3.0], [0.0, 2.0]), - ta.Permutation(stored, tack.array(np.array([5, 0, 3], np.int32)))] + ta.Permutation(tack.array(np.array([5, 0, 3], np.int32)), stored)] + # Ids first: given the other way round, the floats were ids into the three + # ids, read out of bounds alike on the host and in the kernel (TACK_DEBUG). + np.testing.assert_array_equal(ta.to_host(cases[-1]), [7.5, 0.0, 4.5]) for values in cases: host = np.asarray(ta.to_host(values)) assert len(values) == host.shape[0] From 1ab40db89f391c41541da7f7d6863f5b2472b8d8 Mon Sep 17 00:00:00 2001 From: Berk Geveci Date: Thu, 5 Nov 2026 17:13:33 -0500 Subject: [PATCH 2/4] Examples 51 and 52: fetch the OpenFOAM case's directory, not its .foam _vtk_data.path fetches what it is given: the empty .foam alone left the case's other files unfetched and the reader without a case. --- packages/tack-vis/examples/51_vtk_openfoam.py | 5 ++++- packages/tack-vis/examples/52_vtk_pathlines.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/tack-vis/examples/51_vtk_openfoam.py b/packages/tack-vis/examples/51_vtk_openfoam.py index 013800839..df95880cc 100644 --- a/packages/tack-vis/examples/51_vtk_openfoam.py +++ b/packages/tack-vis/examples/51_vtk_openfoam.py @@ -29,6 +29,7 @@ """ import argparse +import os import numpy as np @@ -79,7 +80,9 @@ def area(surface): # THE CASE, WITH ITS PATCHES # ================================================================ -reader = vtkOpenFOAMReader(file_name=_vtk_data.path("OpenFOAM/squareBend/squareBend.foam")) +# The case's directory, so that every file of the case is fetched. +reader = vtkOpenFOAMReader( + file_name=os.path.join(_vtk_data.path("OpenFOAM/squareBend"), "squareBend.foam")) reader.UpdateInformation() for i in range(reader.GetNumberOfPatchArrays()): reader.SetPatchArrayStatus(reader.GetPatchArrayName(i), 1) diff --git a/packages/tack-vis/examples/52_vtk_pathlines.py b/packages/tack-vis/examples/52_vtk_pathlines.py index 202589969..af6e0fbd6 100644 --- a/packages/tack-vis/examples/52_vtk_pathlines.py +++ b/packages/tack-vis/examples/52_vtk_pathlines.py @@ -30,6 +30,7 @@ """ import argparse +import os import numpy as np @@ -77,7 +78,9 @@ def ends(lines): # THE CASE # ================================================================ -reader = vtkOpenFOAMReader(file_name=_vtk_data.path("OpenFOAM/cavity/cavity.foam")) +# The case's directory, so that every file of the case is fetched. +reader = vtkOpenFOAMReader( + file_name=os.path.join(_vtk_data.path("OpenFOAM/cavity"), "cavity.foam")) reader.UpdateInformation() steps = list(reader.GetOutputInformation(0).Get( vtkStreamingDemandDrivenPipeline.TIME_STEPS())) From 8544aaa4e66830fa4bbd9b638b2933f5676670ec Mon Sep 17 00:00:00 2001 From: Berk Geveci Date: Thu, 5 Nov 2026 17:31:51 -0500 Subject: [PATCH 3/4] Share device arrays with VTK only where VTK holds that device's memory The nightly wheels have DLPack and Viskores on the CPU alone: they list CUDA among supported_device_types() but refuse its memory when asked, so every VTK algorithm on a GPU failed handing its output back. Try a small array once per type of device, and copy to the host where VTK cannot hold it. --- .../tack-vis/src/tack/interop/vtk/__init__.py | 22 ++++++++++++++++++- packages/tack-vis/tests/test_vtk_interop.py | 6 +++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/tack-vis/src/tack/interop/vtk/__init__.py b/packages/tack-vis/src/tack/interop/vtk/__init__.py index 19a0c847c..83804a522 100644 --- a/packages/tack-vis/src/tack/interop/vtk/__init__.py +++ b/packages/tack-vis/src/tack/interop/vtk/__init__.py @@ -2059,7 +2059,27 @@ def _stored_on_device(values): _dlpack_support() except RuntimeError: return False - return True + return _vtk_holds(values.device) + + +#: Whether this VTK holds each type of device's memory in its arrays, by the type. +_holds = {} + + +def _vtk_holds(device): + """Whether this VTK makes arrays of ``device``'s memory: a four-value array is + tried once for each type of device. A VTK with DLPack whose Viskores has no such + device (the nightly wheels', on the CPU alone) still lists the type in + ``dlpack_support.supported_device_types()``, and refuses it only when asked.""" + if device.type not in _holds: + try: + # Not array_to_vtk: a Level Zero context VTK does not share is its + # error to give, where the array is made. + probe = _dlpack_support().dlpack_to_vtk(tack.zeros((4, 1), device=device)) + _holds[device.type] = _device_type(probe) == device.type + except (ImportError, RuntimeError): + _holds[device.type] = False + return _holds[device.type] def _vtk_cells(offsets, connectivity, share=False): diff --git a/packages/tack-vis/tests/test_vtk_interop.py b/packages/tack-vis/tests/test_vtk_interop.py index 4e376890f..9bd2b2a8d 100644 --- a/packages/tack-vis/tests/test_vtk_interop.py +++ b/packages/tack-vis/tests/test_vtk_interop.py @@ -368,7 +368,8 @@ def _init_device(arch): def _device_arches(): - """Backends on this machine whose memory VTK can describe.""" + """Backends on this machine whose memory VTK can describe, and holds: a VTK whose + Viskores runs on the CPU alone (the nightly wheels') has DLPack but no device.""" from tack.runtime.dispatch import get_backend found = [] @@ -377,7 +378,8 @@ def _device_arches(): _init_device(arch) except (ImportError, RuntimeError, OSError, AttributeError): continue # not built, no device, no driver, or no shared context - if get_backend().device_memory_spaces: + if get_backend().device_memory_spaces and interop._vtk_holds( + tack.get_default_device()): found.append(pytest.param(arch, space, id=arch)) tack.set_default_device("cpu") return found From 593c2251269d8fd39010bdf36e54a745caadb87f Mon Sep 17 00:00:00 2001 From: Berk Geveci Date: Thu, 5 Nov 2026 21:26:00 -0500 Subject: [PATCH 4/4] Changelog: VTK holds a device's memory only where it says it does --- changelog.d/vtk-holds-device-memory.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changelog.d/vtk-holds-device-memory.md diff --git a/changelog.d/vtk-holds-device-memory.md b/changelog.d/vtk-holds-device-memory.md new file mode 100644 index 000000000..c6841c6b1 --- /dev/null +++ b/changelog.d/vtk-holds-device-memory.md @@ -0,0 +1,8 @@ +### Fixed + +- A VTK with DLPack but without the device's memory (the nightly wheels, whose + Viskores runs on the CPU alone yet lists CUDA among + `dlpack_support.supported_device_types()`) no longer fails every VTK + algorithm on a GPU as it hands its output back: the interop tries a + four-value array once per type of device (`_vtk_holds`) and copies to the + host where VTK cannot hold the memory.