Skip to content

Preserve coordinate dtype when casting RectilinearGrid to UnstructuredGrid - #8723

Merged
akaszynski merged 6 commits into
pyvista:mainfrom
binggao1230:fix-7931-rectilinear-cast-dtype
Jul 22, 2026
Merged

akaszynski merged 6 commits into
pyvista:mainfrom
binggao1230:fix-7931-rectilinear-cast-dtype

Conversation

@binggao1230

Copy link
Copy Markdown
Contributor

Description

cast_to_unstructured_grid silently downcasts a RectilinearGrid's float64 coordinates to float32, as reported in #7931.

Root cause

cast_to_unstructured_grid runs the input through vtkAppendFilter. That filter chooses its output point precision by inspecting the input's points — but only vtkPointSet subclasses store explicit points. A RectilinearGrid stores its points implicitly (as per-axis coordinate arrays), so it is not a vtkPointSet. The filter therefore cannot read the coordinate precision and falls back to its default of single precision, downcasting float64 coordinates and breaking workflows that depend on double-precision coordinates (e.g. geographic data).

Fix

When the input is a RectilinearGrid, detect whether any of its stored coordinate arrays is double precision and, if so, set the append filter's output precision to double explicitly. Other dataset types are unaffected: vtkPointSet inputs (PolyData, StructuredGrid, …) already report their precision to the filter, so their behavior is unchanged. (VTK_DOUBLE is added to the _vtk import allowlist to support the check.)

Reproduction

import numpy as np
import pyvista as pv

grid = pv.RectilinearGrid()
grid.x = np.array([0.1, 0.2, 0.3], dtype=np.float64)
grid.y = np.array([0.0, 1.0], dtype=np.float64)
grid.z = np.array([0.0], dtype=np.float64)

print(grid.cast_to_unstructured_grid().points.dtype)
# before: float32   (coordinates downcast, 0.1 no longer exact)
# after:  float64

Verification

input before after
RectilinearGrid float64 float32 float64
RectilinearGrid float32 float32 float32
RectilinearGrid mixed f64/f32 float32 float64
PolyData (Sphere) float32 float32 float32
PolyData (Sphere) float64 float64 float64
ImageData float32 float32

Added test_cast_rectilinear_grid_to_unstructured_grid_keeps_dtype (parametrized over float64/float32), which also checks point/cell counts and that the coordinate values round-trip exactly. Existing cast tests pass; ruff and mypy are clean on the changed files.

Fixes #7931

…dGrid

vtkAppendFilter selects its output point precision by inspecting the
input's points, but a RectilinearGrid stores its points implicitly (not as
a vtkPointSet), so the filter cannot detect the coordinate precision and
falls back to single precision. This silently downcast float64 coordinates
to float32 in cast_to_unstructured_grid, breaking workflows that rely on
double-precision coordinates (e.g. geographic data).

Detect a double-precision RectilinearGrid from its stored coordinate arrays
and set the append filter's output precision explicitly to double in that
case. Other dataset types are unaffected (vtkPointSet inputs already report
their precision to the filter).

Fixes pyvista#7931
@github-actions github-actions Bot added the bug Uh-oh! Something isn't working as expected. label May 29, 2026

@banesullivan banesullivan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably also an issue for ImageData.

I'm wondering if we should just set the default to double precision. This might be a performance regression, though.

Ideally, I'd like to see a sweep of all data set types in the test to validate that we can do single and double precision unstructured grid cast appropriately.

Address review feedback:
- Extend the precision fix to ImageData. Like RectilinearGrid it has no
  stored point array, so vtkAppendFilter could not detect its precision and
  downcast to single. Its coordinates derive from the origin and spacing,
  which VTK always stores in double precision, so the cast now produces
  double-precision points. vtkPointSet inputs are still left to the filter's
  default, which already preserves their precision (no blanket double-
  precision default, avoiding a memory regression for single-precision data).
- Replace the RectilinearGrid-only test with a sweep over PolyData,
  StructuredGrid, UnstructuredGrid, PointSet and RectilinearGrid in both
  single and double precision, plus a dedicated ImageData case.
@binggao1230

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Addressed in the follow-up commit:

ImageData — good catch, it had the same problem. Like RectilinearGrid it has no stored point array, so vtkAppendFilter couldn't detect its precision and downcast to single. Its coordinates are derived from the origin and spacing, which VTK always keeps in double precision, so the cast now produces double-precision points (the previous single-precision output was a silent, lossy conversion).

Defaulting everything to double — I held off on this for exactly the performance/memory reason you mentioned: it would double the point-array footprint for genuinely single-precision PolyData/StructuredGrid/etc. Instead the fix is type-specific: only the implicit-geometry datasets that can't otherwise be detected (RectilinearGrid, ImageData) get an explicit precision, while vtkPointSet inputs are left to vtkAppendFilter's default (DEFAULT_PRECISION), which already preserves their input precision. So single-precision input stays single everywhere it can.

Test sweep — replaced the RectilinearGrid-only test with test_cast_to_unstructured_grid_preserves_point_precision, parametrized over PolyData, StructuredGrid, UnstructuredGrid, PointSet and RectilinearGrid × {float32, float64}, asserting the output dtype, point count and bounds. ImageData has its own case (test_cast_image_data_to_unstructured_grid_is_double) since it has no single-precision form.

Verified the full matrix locally; the existing test_meshio.py (which casts ImageData and uniform grids) still passes with the now-double ImageData output.

banesullivan
banesullivan previously approved these changes Jun 11, 2026
@banesullivan
banesullivan enabled auto-merge June 11, 2026 20:18
Comment thread pyvista/core/dataset.py Resolved
@banesullivan
banesullivan disabled auto-merge June 11, 2026 21:51
@binggao1230

Copy link
Copy Markdown
Contributor Author

Happy to add that canary test here if useful — i.e. assert the current buggy VTK behavior directly (vtkAppendFilter output dtype with SetOutputPointsPrecision default, no workaround) with a comment linking https://gitlab.kitware.com/vtk/vtk/-/work_items/19965 and this PR, so the special handling has a clear cleanup trigger once the minimum supported VTK includes the fix. Say the word and I'll push it (holding off for now since the branch is being updated).

@binggao1230

Copy link
Copy Markdown
Contributor Author

Ah, our messages crossed — I see you already pushed exactly that canary plus the VTK-bug links. Looks great, nothing to add from me.

@akaszynski
akaszynski added this pull request to the merge queue Jul 21, 2026
Merged via the queue into pyvista:main with commit 5c9fc5a Jul 22, 2026
46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Uh-oh! Something isn't working as expected.

Development

Successfully merging this pull request may close these issues.

cast_to_unstructured_grid does not maintain dtype for RectilinearGrids

4 participants