Skip to content

Commit 716b357

Browse files
committed
nanovdb/python: fix writeGrids dropping all but the last grid
The Python binding for nanovdb.io.writeGrids iterated the handle list and called io::writeGrid(fileName, ...) per element. That file-level overload opens the path with std::ios::trunc, so every iteration wiped the previously written grid and the resulting .nvdb only ever contained the last handle in the list (same story for deviceWriteGrids on the CUDA side). Mirror io::writeGrids(fileName, handles, codec, verbose) from nanovdb/io/IO.h directly in the wrapper: open the output stream once with trunc, then call the ostream overload of writeGrid for each handle cast from the nb::list. We can't simply forward to io::writeGrids because GridHandle<BufferT> is move-only (see GridHandle.h) and the nb::list holds references we must not invalidate. Tests (nanovdb/python/test/TestNanoVDB.py): - TestGridHandleExchange.test_list_to_vector now reads the written file back via readGridMetaData and readGrids and asserts the full handle count is preserved. - Added TestDeviceReadWriteGrids.test_device_write_grids_multi as a CUDA-guarded regression for deviceWriteGrids with multiple handles. Signed-off-by: Jonathan Swartz <jonathan@jswartz.info>
1 parent f4d8702 commit 716b357

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

nanovdb/nanovdb/python/PyIO.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,15 @@ template<typename BufferT> nb::list readGrids(const std::string& fileName, int v
6969

7070
template<typename BufferT> void writeGrids(const std::string& fileName, nb::list handles, io::Codec codec, int verbose)
7171
{
72+
std::ofstream os(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
73+
if (!os.is_open()) {
74+
throw std::ios_base::failure("Unable to open file named \"" + fileName + "\" for output");
75+
}
7276
for (size_t i = 0; i < handles.size(); ++i) {
73-
nanovdb::io::writeGrid(fileName, nb::cast<const GridHandle<BufferT>&>(handles[i]), codec, verbose);
77+
nanovdb::io::writeGrid(os, nb::cast<const GridHandle<BufferT>&>(handles[i]), codec);
78+
}
79+
if (verbose) {
80+
std::cout << "Wrote " << handles.size() << " nanovdb::Grid(s) to file named \"" << fileName << "\"" << std::endl;
7481
}
7582
}
7683

nanovdb/nanovdb/python/test/TestNanoVDB.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,17 @@ def test_list_to_vector(self):
343343
dstFile.close()
344344
try:
345345
nanovdb.io.writeGrids(dstFile.name, handles)
346+
# Verify that both grids actually made it into the file. Previously
347+
# writeGrids re-opened the file per handle with std::ios::trunc, so
348+
# only the final grid was retained on disk.
349+
metadata = nanovdb.io.readGridMetaData(dstFile.name)
350+
self.assertEqual(len(metadata), len(handles))
351+
readHandles = nanovdb.io.readGrids(dstFile.name)
352+
self.assertEqual(len(readHandles), len(handles))
353+
for readHandle in readHandles:
354+
self.assertEqual(readHandle.gridCount(), 1)
355+
self.assertEqual(readHandle.gridType(0), nanovdb.GridType.Double)
356+
self.assertIsNotNone(readHandle.doubleGrid())
346357
finally:
347358
os.unlink(dstFile.name)
348359

@@ -510,6 +521,22 @@ def test_read_write_grids(self):
510521
except RuntimeError:
511522
print("ZIP compression codec not supported. Skipping...")
512523

524+
def test_device_write_grids_multi(self):
525+
# Regression test for deviceWriteGrids: all grids in the list must end
526+
# up in the output file, not just the last one.
527+
handle = nanovdb.tools.cuda.createLevelSetSphere(
528+
nanovdb.GridType.Float, name=self.gridName
529+
)
530+
handles = [handle, handle]
531+
nanovdb.io.deviceWriteGrids(self.dstFile.name, handles)
532+
metadata = nanovdb.io.readGridMetaData(self.dstFile.name)
533+
self.assertEqual(len(metadata), len(handles))
534+
readHandles = nanovdb.io.deviceReadGrids(self.dstFile.name)
535+
self.assertEqual(len(readHandles), len(handles))
536+
for readHandle in readHandles:
537+
self.assertEqual(readHandle.gridCount(), 1)
538+
self.assertEqual(readHandle.gridType(0), nanovdb.GridType.Float)
539+
513540

514541
@unittest.skipIf(
515542
not nanovdb.isCudaAvailable(), "nanovdb module was compiled without CUDA support"

0 commit comments

Comments
 (0)