diff --git a/changelog.d/vtk-holds-device-memory.md b/changelog.d/vtk-holds-device-memory.md new file mode 100644 index 00000000..c6841c6b --- /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. diff --git a/packages/tack-core/tests/test_arrays_protocol.py b/packages/tack-core/tests/test_arrays_protocol.py index 417dcdd2..8e0de3ba 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] diff --git a/packages/tack-vis/examples/51_vtk_openfoam.py b/packages/tack-vis/examples/51_vtk_openfoam.py index 01380083..df95880c 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 20258996..af6e0fbd 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())) diff --git a/packages/tack-vis/src/tack/interop/vtk/__init__.py b/packages/tack-vis/src/tack/interop/vtk/__init__.py index 19a0c847..83804a52 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 4e376890..9bd2b2a8 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