Repository: pyvista/pyvista

Issue:
Allow passing through cell data in `to_tetrahedra` method in RectilinearGrid
### Describe the feature you would like to be added.

No cell data is passed through when converting to a tetrahedra.  The user can currently request to pass through the original cell id, but it requires one more step to regenerate the cell data on the tetrahedralized mesh.

### Links to VTK Documentation, Examples, or Class Definitions.

_No response_

### Pseudocode or Screenshots

Currently we have to do

```python
mesh # Rectilinear or UniformGrid, which has cell data "cell_data"
tetra_mesh = mesh.to_tetrahedra(pass_cell_ids=True)
tetra_mesh["cell_data"] = mesh["cell_data"][tetra_mesh.cell_data.active_scalars]
```

It would be better to do something like

```python
mesh # Rectilinear or UniformGrid, which has cell data "cell_data"
tetra_mesh = mesh.to_tetrahedra(pass_cell_data=True)  # the prior code would occur inside the method
```

Discussion:
codecov[bot]: ## [Codecov](https://codecov.io/gh/pyvista/pyvista/pull/4311?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) Report
> Merging [#4311](https://codecov.io/gh/pyvista/pyvista/pull/4311?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) (9d087cc) into [main](https://codecov.io/gh/pyvista/pyvista/commit/e71be93c902d516a384cb2edff4e37006caad26b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) (e71be93) will **decrease** coverage by `2.13%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #4311      +/-   ##
==========================================
- Coverage   95.77%   93.64%   -2.13%     
==========================================
  Files          97       97              
  Lines       20694    20767      +73     
==========================================
- Hits        19820    19448     -372     
- Misses        874     1319     +445     
```

akaszynski: > We should consider making this default `True`. One problem is that this will be a mild breaking change and will also implicitly change the default of `pass_cell_ids`.

I've changed the defaults. Most of our other filters have this behavior by default.

One side-effect from debugging this was apparently the original cell IDs were not given a name. This makes accessing them difficult, so I've changed the default name of this to `'vtkOriginalCellIds'`.
akaszynski: Ran into an edge case where the name of the active scalars is 'None', but when you check if that name is in the data attributes, it returns `False`, because accessing `key` has the side effect of naming any unnamed arrays.

Patch:
diff --git a/pyvista/core/datasetattributes.py b/pyvista/core/datasetattributes.py
index 50c4a10f..48bb3284 100644
--- a/pyvista/core/datasetattributes.py
+++ b/pyvista/core/datasetattributes.py
@@ -1072,7 +1072,12 @@ class DataSetAttributes(_vtk.VTKObjectWrapper):
 
         """
         if self.GetScalars() is not None:
-            return str(self.GetScalars().GetName())
+            name = self.GetScalars().GetName()
+            if name is None:
+                # Getting the keys has the side effect of naming "unnamed" arrays
+                self.keys()
+                name = self.GetScalars().GetName()
+            return str(name)
         return None
 
     @active_scalars_name.setter
diff --git a/pyvista/core/filters/rectilinear_grid.py b/pyvista/core/filters/rectilinear_grid.py
index 80a54e5e..cd5d7b02 100644
--- a/pyvista/core/filters/rectilinear_grid.py
+++ b/pyvista/core/filters/rectilinear_grid.py
@@ -17,7 +17,8 @@ class RectilinearGridFilters:
         self,
         tetra_per_cell: int = 5,
         mixed: Union[Sequence[int], bool] = False,
-        pass_cell_ids: bool = False,
+        pass_cell_ids: bool = True,
+        pass_cell_data: bool = True,
         progress_bar: bool = False,
     ):
         """Create a tetrahedral mesh structured grid.
@@ -39,10 +40,17 @@ class RectilinearGridFilters:
             string uses a cell array rather than the active array to determine
             the number of tetrahedra to generate per cell.
 
-        pass_cell_ids : bool, default: False
+        pass_cell_ids : bool, default: True
             Set to ``True`` to make the tetrahedra have scalar data indicating
             which cell they came from in the original
-            :class:`pyvista.RectilinearGrid`.
+            :class:`pyvista.RectilinearGrid`. The name of this array is
+            ``'vtkOriginalCellIds'`` within the ``cell_data``.
+
+        pass_cell_data : bool, default: True
+            Set to ``True`` to make the tetradera mesh have the cell data from
+            the original :class:`pyvista.RectilinearGrid`.  This uses
+            ``pass_cell_ids=True`` internally. If ``True``, ``pass_cell_ids``
+            will also be set to ``True``.
 
         progress_bar : bool, default: False
             Display a progress bar to indicate progress.
@@ -80,7 +88,7 @@ class RectilinearGridFilters:
 
         """
         alg = _vtk.vtkRectilinearGridToTetrahedra()
-        alg.SetRememberVoxelId(pass_cell_ids)
+        alg.SetRememberVoxelId(pass_cell_ids or pass_cell_data)
         if mixed is not False:
             if isinstance(mixed, str):
                 self.cell_data.active_scalars_name = mixed
@@ -107,4 +115,17 @@ class RectilinearGridFilters:
 
         alg.SetInputData(self)
         _update_alg(alg, progress_bar, 'Converting to tetrahedra')
-        return _get_output(alg)
+        out = _get_output(alg)
+        if pass_cell_data:
+            # algorithm stores original cell ids in active scalars
+            for name in self.cell_data:  # type: ignore
+                if name != out.cell_data.active_scalars_name:
+                    out[name] = self.cell_data[name][out.cell_data.active_scalars]  # type: ignore
+
+        if alg.GetRememberVoxelId():
+            # original cell_ids are not named and are the active scalars
+            out.cell_data.set_array(
+                out.cell_data.pop(out.cell_data.active_scalars_name), 'vtkOriginalCellIds'
+            )
+
+        return out
diff --git a/pyvista/core/grid.py b/pyvista/core/grid.py
index 6ce23688..5248dd3f 100644
--- a/pyvista/core/grid.py
+++ b/pyvista/core/grid.py
@@ -135,23 +135,30 @@ class RectilinearGrid(_vtk.vtkRectilinearGrid, Grid, RectilinearGridFilters):
                     self.shallow_copy(args[0])
             elif isinstance(args[0], (str, pathlib.Path)):
                 self._from_file(args[0], **kwargs)
-            elif isinstance(args[0], np.ndarray):
-                self._from_arrays(args[0], None, None, check_duplicates)
+            elif isinstance(args[0], (np.ndarray, Sequence)):
+                self._from_arrays(np.asanyarray(args[0]), None, None, check_duplicates)
             else:
                 raise TypeError(f'Type ({type(args[0])}) not understood by `RectilinearGrid`')
 
         elif len(args) == 3 or len(args) == 2:
-            arg0_is_arr = isinstance(args[0], np.ndarray)
-            arg1_is_arr = isinstance(args[1], np.ndarray)
+            arg0_is_arr = isinstance(args[0], (np.ndarray, Sequence))
+            arg1_is_arr = isinstance(args[1], (np.ndarray, Sequence))
             if len(args) == 3:
-                arg2_is_arr = isinstance(args[2], np.ndarray)
+                arg2_is_arr = isinstance(args[2], (np.ndarray, Sequence))
             else:
                 arg2_is_arr = False
 
             if all([arg0_is_arr, arg1_is_arr, arg2_is_arr]):
-                self._from_arrays(args[0], args[1], args[2], check_duplicates)
+                self._from_arrays(
+                    np.asanyarray(args[0]),
+                    np.asanyarray(args[1]),
+                    np.asanyarray(args[2]),
+                    check_duplicates,
+                )
             elif all([arg0_is_arr, arg1_is_arr]):
-                self._from_arrays(args[0], args[1], None, check_duplicates)
+                self._from_arrays(
+                    np.asanyarray(args[0]), np.asanyarray(args[1]), None, check_duplicates
+                )
             else:
                 raise TypeError("Arguments not understood by `RectilinearGrid`.")
 


Test patch:
diff --git a/tests/filters/test_rectilinear_grid.py b/tests/filters/test_rectilinear_grid.py
index 2019cc0a..ab9eb186 100644
--- a/tests/filters/test_rectilinear_grid.py
+++ b/tests/filters/test_rectilinear_grid.py
@@ -45,3 +45,25 @@ def test_to_tetrahedral_mixed(tiny_rectilinear):
 def test_to_tetrahedral_edge_case():
     with pytest.raises(RuntimeError, match='is 1'):
         pv.UniformGrid(dimensions=(1, 2, 2)).to_tetrahedra(tetra_per_cell=12)
+
+
+def test_to_tetrahedral_pass_cell_ids(tiny_rectilinear):
+    tet_grid = tiny_rectilinear.to_tetrahedra(pass_cell_ids=False, pass_cell_data=False)
+    assert not tet_grid.cell_data
+    tet_grid = tiny_rectilinear.to_tetrahedra(pass_cell_ids=True, pass_cell_data=False)
+    assert 'vtkOriginalCellIds' in tet_grid.cell_data
+    assert np.issubdtype(tet_grid.cell_data['vtkOriginalCellIds'].dtype, np.integer)
+
+
+def test_to_tetrahedral_pass_cell_data(tiny_rectilinear):
+    tiny_rectilinear["cell_data"] = np.ones(tiny_rectilinear.n_cells)
+
+    tet_grid = tiny_rectilinear.to_tetrahedra(pass_cell_ids=False, pass_cell_data=False)
+    assert not tet_grid.cell_data
+
+    tet_grid = tiny_rectilinear.to_tetrahedra(pass_cell_ids=False, pass_cell_data=True)
+    assert tet_grid.cell_data
+    assert "cell_data" in tet_grid.cell_data
+
+    # automatically added
+    assert 'vtkOriginalCellIds' in tet_grid.cell_data
diff --git a/tests/test_grid.py b/tests/test_grid.py
index 1f3645b9..5197f636 100644
--- a/tests/test_grid.py
+++ b/tests/test_grid.py
@@ -735,6 +735,21 @@ def test_create_rectilinear_grid_from_specs():
     assert grid.n_cells == 9 * 3 * 19
     assert grid.n_points == 10 * 4 * 20
     assert grid.bounds == (-10.0, 8.0, -10.0, 5.0, -10.0, 9.0)
+
+    # with Sequence
+    xrng = [0, 1]
+    yrng = [0, 1, 2]
+    zrng = [0, 1, 2, 3]
+    grid = pyvista.RectilinearGrid(xrng)
+    assert grid.n_cells == 1
+    assert grid.n_points == 2
+    grid = pyvista.RectilinearGrid(xrng, yrng)
+    assert grid.n_cells == 2
+    assert grid.n_points == 6
+    grid = pyvista.RectilinearGrid(xrng, yrng, zrng)
+    assert grid.n_cells == 6
+    assert grid.n_points == 24
+
     # 2D example
     cell_spacings = np.array([1.0, 1.0, 2.0, 2.0, 5.0, 10.0])
     x_coordinates = np.cumsum(cell_spacings)