VTKHDF partitioned dataset visualization issues

I am trying to visualize data from my CFD solver using the VTKHDF file format and ParaView. The solver is MPI parallel. Until now I resorted to assembling the mesh in memory and then writing a single partition to the output file. The resulting output file worked as expected, as in when I use filters such as CellDataToPointData or Contour, I get the expected output.

For very large datasets, however, this strategy seems to not work, as I was getting some allocation failure from ParaView when opening the dataset (very big dataset >64GB). I checked the documentation again for VTKHDF and am now trying to write partitioned datasets instead. That way I can run ParaView on the distributed dataset (probably as intended).

The issue I have now is that when I visualize my data, I can see imprints of the individual partitions. I would like to get rid of these, for obvious reasons. Per rank, I am writing the points (includes duplicates from other ranks) and all inner cells (no duplicates). When I use the Contour filter, for example, the output is “closed”. What I tried was to mark the duplicated points with a vtkGhostType array and add the GlobalNodeId for all the points. Then I tried to use the AddGhostCells filter to have ParaView add ghost cells so that the contour output would be closed. This, however, crashed my ParaView. After some Googling, I found a thread that mentioned that you have to run RedistributeMesh before generating ghost cells. This, however, also does not produce the desired result.

I have attached sample data that illustrates my problem. Could someone please tell me what I am doing wrong in this case.

Steps to reproduce:

  1. Unzip data
  2. Load navier_stokes_00011.vtkhdf in ParaView
  3. Contour filter for density (Value=2)
  4. Output is not smooth but has imprints of partitions

data.zip (4.0 MB)

@Louis_Gombert @Lucas_Givord

I modified my code to now also include the halo cells in the output to test if this would fix the issue. For a different test case (too large to share, but basically forward facing step) this is what I get if I don’t write the halo region:

I created a contour plot and then colored the iso-surfaces by the partition index. As you can see, there is a gap between partitions. This is the gap I would like to get rid of.

If I include the halo region, this is what get:

As you can see, this does not really fix the issue, it at most hides it. Any idea on what to do?

I think I found the fix myself. I write the halo region, but importantly, mark the halo cells as vtkGhostType (!!). Then the picture looks like this:

I am not sure if marking the ghost points as vtkGhostType and including the global node Ids is necessary, for this final image, they were included in the output.

1 Like

For future reference, this is what you need to do to make ParaView read the data correctly:

  1. The output needs to include the halo cells
  2. The halo cells have to be full/valid 3D cells (initially this was not the case for me)
  3. The halo cells have to be marked in the vtkGhostType dataset (write this yourself; don’t let ParaView try to determine this on its own). If you want to include boundary condition cells in your output (useful for computing gradients in ParaView), these also have to be marked in the vtkGhostType array, however with a different value (check this enum for reference: VTK: vtkDataSetAttributes Class Reference ). In my case, halo cells should be marked as DUPLICATECELL (=1) and ghost/periodic cells should be marked as HIDDENCELL (=32). There is also EXTERIORCELL (=16), which, from the comment, I would assume should be the correct type, but if you only select that, ParaView will still show the ghost/periodic cells. I ended up marking them as EXTERIORCELL | HIDDENCELL (=48), which produced the desired result.
  4. Marking the halo/ghost points as vtkGhostType or including GlobalNodeId in the output file appears to be not necessary (I assume this is only true for cell-centered simulation data). When I tried to put it in, it did not improve the output in any way that I could determine, so I left it out.

If you do this, all the usual filters should work as expected; no need to do anything special before using them. I’ll attach the correct version of my initially submitted data for reference, since it is hard to come by such data.

data.zip (5.2 MB)

Plotting old and new data next to each other, you can see that there are no more gaps in the output. It is still not perfect, but I suspect that this is now a resolution issue and not an issue in the output data. The gradient filter also works much better with the new dataset, still not perfect, but again, I think this is due to the resolution.

Sorry to reopen this after marking it as solved.

It seems like my “fix” is not enough for big datasets. I still get the gaps that you see in the screenshot above. Maybe someone could clarify, on a conceptual level, what needs to be done in order for ParaView to be able to stitch together the individual iso-surfaces into one. Is this even something that ParaView can do for partitioned datasets?

Here is an example from a Taylor-Green vortex; as you can see, the gaps are quite distracting:

This is data from a 1024-core simulation; the grid is 256^3. The resolution should be fine to get high-quality output. I would also be fine with running some filters first to get rid of the gaps. So far I have tried “Redistribute DataSet” and “Merge Blocks”, unsuccessfully.

Here is another screenshot that maybe illustrates the issue more clearly:

At partition boundaries, there is not only a gap, but the surface itself is computed incorrectly. This also happens for data that is not based on gradients (density), although there the error in the surface is less severe:

Maybe it helps; this is what I get when I run the “Extract Ghost Cells“ filter:

Maybe this is actually a problem with ParaView itself and not with the input format. The CGNS documentation has some example mesh files, which are also partitioned; for example, this one. If I load this into ParaView, select Density, and then add a bunch of iso-surfaces, this is what I get:

The same gaps that I had in my input data. Maybe a tiny comment from someone who works on this stuff would be helpful at this point…

Your datasets are just missing ghost cells: 4. Visualizing Large Models — ParaView Documentation 6.0.0 documentation

This is also what I figured. I did try writing my halo region as ghost cells, which almost worked, but not completely (see posts above). My suspicion is that filters like contour work on point data, so internally ParaView will run something like cell data to point data to compute the iso surfaces with something like marching cubes.

My halo region is meant for CFD, where you only need the face neighbors. For the point values to match up across partition boundaries, the ghost cells need to include all the cells that are connected to the points on the outer surface of the domain partition. Increasing the halo region in my code would mean significant code changes. However, I am pretty sure that I could write a program that reads a one-partition VTKHDF file and produces an n-partition VTKHDF file with the appropriate ghost cells.

Could you maybe confirm if my suspicions about the ParaView internals are correct? If yes, I will implement a splitting function and test again.

Side note: I continued reading through that wiki that you mentioned, and 4. Visualizing Large Models — ParaView Documentation 6.0.0 documentation mentions that ParaView should be able to generate ghost cells on its own. If I select that filter on the CGNS example data, ParaView crashes with this backtrace:

/usr/include/c++/15.2.1/bits/stl_vector.h:1263: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](size_type) [with _Tp = char; _Alloc = std::allocator<char>; reference = char&; size_type = long unsigned int]: Assertion '__n < this->size()' failed.
[x1carbon:197634] *** Process received signal ***
[x1carbon:197634] Signal: Aborted (6)
[x1carbon:197634] Signal code:  (-6)
[x1carbon:197634] [ 0] /usr/bin/../lib/libc.so.6(+0x3e4d0) [0x7fcf7243e4d0]
[x1carbon:197634] [ 1] /usr/bin/../lib/libc.so.6(+0x9890c) [0x7fcf7249890c]
[x1carbon:197634] [ 2] /usr/bin/../lib/libc.so.6(gsignal+0x20) [0x7fcf7243e3a0]
[x1carbon:197634] [ 3] /usr/bin/../lib/libc.so.6(abort+0x26) [0x7fcf7242557a]
[x1carbon:197634] [ 4] /usr/bin/../lib/libstdc++.so.6(+0x9a41f) [0x7fcf7289a41f]
[x1carbon:197634] [ 5] /usr/lib/libvtkParallelDIY.so.1(+0x120e8) [0x7fcf690e30e8]
[x1carbon:197634] [ 6] /usr/lib/libvtkParallelDIY.so.1(+0xf7e65) [0x7fcf691c8e65]
[x1carbon:197634] [ 7] /usr/lib/libvtkParallelDIY.so.1(_ZN15vtkDIYUtilities4SaveERN7vtkdiy212BinaryBufferEP12vtkDataArray+0x2ce) [0x7fcf691cc06e]
[x1carbon:197634] [ 8] /usr/lib/libvtkParallelDIY.so.1(+0x55cd7) [0x7fcf69126cd7]
[x1carbon:197634] [ 9] /usr/lib/libvtkParallelDIY.so.1(_ZN20vtkDIYGhostUtilities13EnqueueGhostsERKN7vtkdiy26Master13ProxyWithLinkERKNS0_7BlockIDEP17vtkStructuredGridPNS_5BlockINS_28StructuredGridBlockStructureENS_25StructuredGridInformationEEE+0x164) [0x7fcf69134074]
[x1carbon:197634] [10] /usr/lib/libvtkParallelDIY.so.1(+0xad28c) [0x7fcf6917e28c]
[x1carbon:197634] [11] /usr/lib/libvtkParallelDIY.so.1(+0xf62c8) [0x7fcf691c72c8]
[x1carbon:197634] [12] /usr/lib/libvtkParallelDIY.so.1(+0x30505) [0x7fcf69101505]
[x1carbon:197634] [13] /usr/lib/libvtkParallelDIY.so.1(+0x33259) [0x7fcf69104259]
[x1carbon:197634] [14] /usr/lib/libvtkParallelDIY.so.1(+0x9b9ef) [0x7fcf6916c9ef]
[x1carbon:197634] [15] /usr/lib/libvtkParallelDIY.so.1(+0x84525) [0x7fcf69155525]
[x1carbon:197634] [16] /usr/lib/libvtkFiltersParallelDIY2.so.1(_ZN22vtkGhostCellsGenerator18GenerateGhostCellsEP13vtkDataObjectS1_ib+0x11e0) [0x7fcf6d5b50a0]
[x1carbon:197634] [17] /usr/lib/libvtkFiltersParallelDIY2.so.1(_ZN22vtkGhostCellsGenerator7ExecuteEP13vtkDataObjectP20vtkInformationVector+0x160) [0x7fcf6d5b56c0]
[x1carbon:197634] [18] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN12vtkExecutive13CallAlgorithmEP14vtkInformationiPP20vtkInformationVectorS3_+0x60) [0x7fcf77cf7d20]
[x1carbon:197634] [19] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN23vtkDemandDrivenPipeline11ExecuteDataEP14vtkInformationPP20vtkInformationVectorS3_+0x42) [0x7fcf77ce9ed2]
[x1carbon:197634] [20] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN24vtkCompositeDataPipeline11ExecuteDataEP14vtkInformationPP20vtkInformationVectorS3_+0x92) [0x7fcf77ce9c62]
[x1carbon:197634] [21] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN23vtkDemandDrivenPipeline14ProcessRequestEP14vtkInformationPP20vtkInformationVectorS3_+0x423) [0x7fcf77cf2083]
[x1carbon:197634] [22] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN32vtkStreamingDemandDrivenPipeline14ProcessRequestEP14vtkInformationPP20vtkInformationVectorS3_+0x414) [0x7fcf77d8f684]
[x1carbon:197634] [23] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN32vtkStreamingDemandDrivenPipeline6UpdateEiP20vtkInformationVector+0x12a) [0x7fcf77d9330a]
[x1carbon:197634] [24] /usr/lib/libvtkPVVTKExtensionsFiltersParallelDIY2.so.1(_ZN24vtkPVGhostCellsGenerator42GhostCellsGeneratorUsingSuperclassInstanceEP13vtkDataObjectS1_+0x247) [0x7fcf6dcd6557]
[x1carbon:197634] [25] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN12vtkExecutive13CallAlgorithmEP14vtkInformationiPP20vtkInformationVectorS3_+0x60) [0x7fcf77cf7d20]
[x1carbon:197634] [26] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN23vtkDemandDrivenPipeline11ExecuteDataEP14vtkInformationPP20vtkInformationVectorS3_+0x42) [0x7fcf77ce9ed2]
[x1carbon:197634] [27] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN24vtkCompositeDataPipeline11ExecuteDataEP14vtkInformationPP20vtkInformationVectorS3_+0x92) [0x7fcf77ce9c62]
[x1carbon:197634] [28] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN23vtkDemandDrivenPipeline14ProcessRequestEP14vtkInformationPP20vtkInformationVectorS3_+0x423) [0x7fcf77cf2083]
[x1carbon:197634] [29] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN32vtkStreamingDemandDrivenPipeline14ProcessRequestEP14vtkInformationPP20vtkInformationVectorS3_+0x414) [0x7fcf77d8f684]
[x1carbon:197634] *** End of error message ***
zsh: IOT instruction (core dumped)  paraview

Please provide data and steps to reproduce.

  1. Data is the HDF backend data set from here: Examples — CGNS Documentation
  2. Open in ParaView (6.0.1)
  3. Select and apply the Ghost Cells filter (with default parameters)
/usr/include/c++/15.2.1/bits/stl_vector.h:1263: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](size_type) [with _Tp = char; _Alloc = std::allocator<char>; reference = char&; size_type = long unsigned int]: Assertion '__n < this->size()' failed.
[x1carbon:380647] *** Process received signal ***
[x1carbon:380647] Signal: Aborted (6)
[x1carbon:380647] Signal code:  (-6)
[x1carbon:380647] [ 0] /usr/bin/../lib/libc.so.6(+0x3e4d0) [0x7f07f943e4d0]
[x1carbon:380647] [ 1] /usr/bin/../lib/libc.so.6(+0x9890c) [0x7f07f949890c]
[x1carbon:380647] [ 2] /usr/bin/../lib/libc.so.6(gsignal+0x20) [0x7f07f943e3a0]
[x1carbon:380647] [ 3] /usr/bin/../lib/libc.so.6(abort+0x26) [0x7f07f942557a]
[x1carbon:380647] [ 4] /usr/bin/../lib/libstdc++.so.6(+0x9a41f) [0x7f07f989a41f]
[x1carbon:380647] [ 5] /usr/lib/libvtkParallelDIY.so.1(+0x120e8) [0x7f07efee30e8]
[x1carbon:380647] [ 6] /usr/lib/libvtkParallelDIY.so.1(+0xf7e65) [0x7f07effc8e65]
[x1carbon:380647] [ 7] /usr/lib/libvtkParallelDIY.so.1(_ZN15vtkDIYUtilities4SaveERN7vtkdiy212BinaryBufferEP12vtkDataArray+0x2ce) [0x7f07effcc06e]
[x1carbon:380647] [ 8] /usr/lib/libvtkParallelDIY.so.1(+0x55cd7) [0x7f07eff26cd7]
[x1carbon:380647] [ 9] /usr/lib/libvtkParallelDIY.so.1(_ZN20vtkDIYGhostUtilities13EnqueueGhostsERKN7vtkdiy26Master13ProxyWithLinkERKNS0_7BlockIDEP17vtkStructuredGridPNS_5BlockINS_28StructuredGridBlockStructureENS_25StructuredGridInformationEEE+0x164) [0x7f07eff34074]
[x1carbon:380647] [10] /usr/lib/libvtkParallelDIY.so.1(+0xad28c) [0x7f07eff7e28c]
[x1carbon:380647] [11] /usr/lib/libvtkParallelDIY.so.1(+0xf62c8) [0x7f07effc72c8]
[x1carbon:380647] [12] /usr/lib/libvtkParallelDIY.so.1(+0x30505) [0x7f07eff01505]
[x1carbon:380647] [13] /usr/lib/libvtkParallelDIY.so.1(+0x33259) [0x7f07eff04259]
[x1carbon:380647] [14] /usr/lib/libvtkParallelDIY.so.1(+0x9b9ef) [0x7f07eff6c9ef]
[x1carbon:380647] [15] /usr/lib/libvtkParallelDIY.so.1(+0x84525) [0x7f07eff55525]
[x1carbon:380647] [16] /usr/lib/libvtkFiltersParallelDIY2.so.1(_ZN22vtkGhostCellsGenerator18GenerateGhostCellsEP13vtkDataObjectS1_ib+0x11e0) [0x7f07f43fb0a0]
[x1carbon:380647] [17] /usr/lib/libvtkFiltersParallelDIY2.so.1(_ZN22vtkGhostCellsGenerator7ExecuteEP13vtkDataObjectP20vtkInformationVector+0x160) [0x7f07f43fb6c0]
[x1carbon:380647] [18] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN12vtkExecutive13CallAlgorithmEP14vtkInformationiPP20vtkInformationVectorS3_+0x60) [0x7f07fecf7d20]
[x1carbon:380647] [19] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN23vtkDemandDrivenPipeline11ExecuteDataEP14vtkInformationPP20vtkInformationVectorS3_+0x42) [0x7f07fece9ed2]
[x1carbon:380647] [20] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN24vtkCompositeDataPipeline11ExecuteDataEP14vtkInformationPP20vtkInformationVectorS3_+0x92) [0x7f07fece9c62]
[x1carbon:380647] [21] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN23vtkDemandDrivenPipeline14ProcessRequestEP14vtkInformationPP20vtkInformationVectorS3_+0x423) [0x7f07fecf2083]
[x1carbon:380647] [22] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN32vtkStreamingDemandDrivenPipeline14ProcessRequestEP14vtkInformationPP20vtkInformationVectorS3_+0x414) [0x7f07fed8f684]
[x1carbon:380647] [23] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN32vtkStreamingDemandDrivenPipeline6UpdateEiP20vtkInformationVector+0x12a) [0x7f07fed9330a]
[x1carbon:380647] [24] /usr/lib/libvtkPVVTKExtensionsFiltersParallelDIY2.so.1(_ZN24vtkPVGhostCellsGenerator42GhostCellsGeneratorUsingSuperclassInstanceEP13vtkDataObjectS1_+0x247) [0x7f07f5134557]
[x1carbon:380647] [25] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN12vtkExecutive13CallAlgorithmEP14vtkInformationiPP20vtkInformationVectorS3_+0x60) [0x7f07fecf7d20]
[x1carbon:380647] [26] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN23vtkDemandDrivenPipeline11ExecuteDataEP14vtkInformationPP20vtkInformationVectorS3_+0x42) [0x7f07fece9ed2]
[x1carbon:380647] [27] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN24vtkCompositeDataPipeline11ExecuteDataEP14vtkInformationPP20vtkInformationVectorS3_+0x92) [0x7f07fece9c62]
[x1carbon:380647] [28] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN23vtkDemandDrivenPipeline14ProcessRequestEP14vtkInformationPP20vtkInformationVectorS3_+0x423) [0x7f07fecf2083]
[x1carbon:380647] [29] /usr/bin/../lib/libvtkCommonExecutionModel.so.1(_ZN32vtkStreamingDemandDrivenPipeline14ProcessRequestEP14vtkInformationPP20vtkInformationVectorS3_+0x414) [0x7f07fed8f684]
[x1carbon:380647] *** End of error message ***

Unable to reproduce using the “Constricting channel” data using ParaView 6.0.1 binary release on Linux.

Ok, I am sorry, you are right, it does work with the binary release. I was using the package from my package manager. It seems like the problem is their fault.

1 Like

I’ve had some more time to experiment with different ways of writing the output format, and with some help from @mwestphal, I think I have figured out what I was doing wrong back in December and how to fix it.

My initial problem: When writing partitioned datasets, each partition is processed individually. For some filters to work correctly, information about cells from neighboring partitions is needed. One such filter is the Contour filter. If only the data from one partition is processed, the resulting isosurface might not be continuous across partitions. This is because the filter averages cell data to the points of the mesh to interpolate the surface. To get the same value at points shared between partitions, all surrounding cell data values are needed. This information is provided through ghost cells, which I did not include in my output file:

This picture shows a different (smaller) dataset than the one that I originally posted. The domain is partitioned into two partitions, of which the first one is shown. I added some gaps between the cells to make it more clear what I am talking about. The dark blue cells are the inner cells, and the orange and light blue cells are boundary cells. The inner cells are regular 3D cells, while the boundary cells are 2D cells used to specify boundary conditions (not relevant here). If you wanted to compute isosurfaces for a dataset without ghost cells, the only way to get good results is to use the Ghost Cells filter in ParaView (important detail: turn on Generate Global Ids in the settings). This will generate the missing ghost cells for you, at the cost of additional computation during post-processing. Originally, this filter did not work for me, probably because I was using a ParaView binary from my package manager, but after switching to an official release binary, everything worked as expected. The additional cost comes from ParaView having to send and receive the ghost cell data to and from the individual partitions (MPI ranks). I don’t have any performance measurements on this, but I would suspect that this is actually not too bad, compared to the effort of doing this yourself…

Doing this yourself: In my case, the data comes from a cell-based finite volume code, in which I already have to perform communication between the MPI ranks. For the solution procedure, I need to compute the flux across each face of the mesh. A face is the triangle or quadrangle between two cells. This means that for cells on the boundary of a partition, I need to have access to the cell values on the neighboring partition. For this exchange to work, each partition keeps track of a set of halo cells, which, after the exchange, hold the cell value of the neighboring partition (the red cells):

After adding these cells to the output file and marking them as DUPLICATECELL (=1) in a special data array called “vtkGhostType”, the situation improved, and it looked like this would resolve my issue. However, the dataset I was using for my analysis was quasi-structured, meaning the cells that are adjacent to all the points of one partition are the same cells that are adjacent to the cells of that partition (except for the edges and corners). Therefore, it seemed like it was working, prompting me to mark this issue as solved, while this was only a special case and not a general solution.

A general solution: As I mentioned at the very top (long ago, I know…), the Contour filter needs to have access to all the cells that are adjacent to all the points within one partition. Only then will the average value (most likely weighted average…) be the same at every shared point. This is what such an output file looks like:

As you can see, there are considerably more halo cells in the output, and crucially, every cell which is made up of at least one node from that partition. This is the form that outputs must have in order for filters like Contour to work as expected. With this dataset, no further processing is needed in ParaView. In fact, if you are not explicitly looking for these ghost cells, you would never know that they are there.

The procedure for determining adjacent cells will depend on your code/setup. For me, I was already using ParMETIS to compute the dual (cell-to-cell) of my cell graph (cell-to-node). The ParMETIS_V3_PartKway function takes as an input argument ncommonnodes, which is set to 3 to obtain the dual that you need for a finite volume method (in 2D it would be set to 2). With a little bit of pre- and post-processing, you can set this argument to 1, to obtain a graph that tells you for each cell which other cells share at least one node with it. This is the information that I used to compute the send/receive information for the complete set of halo cells. Of course, you only need to exchange the information of the complete set, when you are writing outputs. For regular finite volume operations, it is enough to only exchange the cells obtained from ncommonnodes = 3. If you reorder the halo cells such that these cells come first, this works as if these extra cells were not even there.

Alternative solutions: Writing halo cells yourself is a bit of a hassle and really only necessary if you are dealing with large datasets. What you can do instead is write the file in parallel, but remap the local node indices to global ones. For this to work, you only need to keep the unique global index of every mesh point around. For me, this information comes directly from the mesh generator, so it should not be difficult to obtain. When doing this, the output file will look to ParaView like it was written from a single rank, which means you can completely omit any halo cells. I think there is some limit to the number of points/cells that ParaView can handle per single partition. I did not specifically go looking for it, but I’ve had it crash when my cell counts approached INT32_MAX, so this method will only work for meshes with less than about 2e9 points/cells.

Here are the datasets from the plots:

  1. No ghost cells:

    no_ghost_cells.hdf (145.0 KB)

  2. Face-connected ghost cells:

    face_connected.hdf (150.2 KB)

  3. Point-connected ghost cells:

    point_connected.hdf (164.5 KB)

  4. Single partition:

    single_partition.hdf (189.7 KB)

(If you are asking yourself why the single partition dataset is the largest, the answer is that I am using int64_t for integers instead of int32_t.)

2 Likes