Repository: pyvista/pyvista

Issue:
Error saving meshes with `POLYGON` cells using `pv.save_meshio`
### Describe the bug, what's wrong, and what you expected.

I found a bug!



### Steps to reproduce the bug.

I get a `KeyError` trying to save the cow mesh. The code:
```python
from pyvista import examples
cow = examples.download_cow().cast_to_unstructured_grid()
pv.save_meshio('cow.vtu', cow)
```
yields:
``` bash
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../pyvista/core/utilities/fileio.py:607: in save_meshio
    meshio.write_points_cells(
../venv/lib/python3.12/site-packages/meshio/_helpers.py:130: in write_points_cells
    mesh = Mesh(
../venv/lib/python3.12/site-packages/meshio/_mesh.py:146: in __init__
    cell_block = CellBlock(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'CellBlock' object has no attribute 'tags'") raised in repr()] CellBlock object at 0x14d37c110>
cell_type = 'polygon5', data = array([[779, 751, 750, 789, 791]]), tags = None

    def __init__(
        self,
        cell_type: str,
        data: list | np.ndarray,
        tags: list[str] | None = None,
    ):
        self.type = cell_type
        self.data = data
    
        if cell_type.startswith("polyhedron"):
            self.dim = 3
        else:
            self.data = np.asarray(self.data)
>           self.dim = topological_dimension[cell_type]
E           KeyError: 'polygon5'

../venv/lib/python3.12/site-packages/meshio/_mesh.py:99: KeyError
```

This error occurs with `download_shark` as well. But not with `download_action_figure`.

### System Information

```shell
--------------------------------------------------------------------------------
  Date: Tue Mar 12 00:10:58 2024
                OS : Darwin
            CPU(s) : 8
           Machine : arm64
      Architecture : 64bit
               RAM : 16.0 GiB
       Environment : IPython
       File system : apfs
        GPU Vendor : Apple
      GPU Renderer : Apple M2
       GPU Version : 4.1 Metal - 83.1
  MathText Support : True
  Python 3.12.2 (v3.12.2:6abddd9f6a, Feb  6 2024, 17:02:06) [Clang 13.0.0
  (clang-1300.0.29.30)]
           pyvista : 0.44.dev0
               vtk : 9.3.0
             numpy : 1.26.4
        matplotlib : 3.8.2
            scooby : 0.9.2
             pooch : 1.8.0
            pillow : 10.2.0
           imageio : 2.33.1
           IPython : 8.21.0
          colorcet : 3.0.1
           cmocean : 3.1.3
        ipywidgets : 8.1.2
              tqdm : 4.66.2
            meshio : 5.3.5
    pytest_pyvista : 0.1.8
             trame : 3.5.2
      trame_client : 2.16.1
      trame_server : 2.17.2
         trame_vtk : 2.8.5
     trame_vuetify : 2.4.2
      nest_asyncio : 1.6.0
--------------------------------------------------------------------------------
```


### Screenshots

_No response_

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pyvista/pyvista/pull/5767?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 90.02%. Comparing base [(`5f17b1d`)](https://app.codecov.io/gh/pyvista/pyvista/commit/5f17b1d00f5ad07f1e84c4488fce503ff54018c4?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) to head [(`0dcd009`)](https://app.codecov.io/gh/pyvista/pyvista/pull/5767?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #5767      +/-   ##
==========================================
- Coverage   96.55%   90.02%   -6.54%     
==========================================
  Files         137      137              
  Lines       23457    23459       +2     
==========================================
- Hits        22650    21119    -1531     
- Misses        807     2340    +1533     
```

</details>
tkoyama010: @keurfonluu Thanks!

> LGTM. Not sure when this change happened though (I guess with `meshio` v5).

For the new account who read this PR. We don't need to consider v5 or lower because `pyvista` supports meshio>=5.2.
https://github.com/pyvista/pyvista/blob/5f17b1d00f5ad07f1e84c4488fce503ff54018c4/pyproject.toml#L47

Patch:
diff --git a/pyvista/core/utilities/fileio.py b/pyvista/core/utilities/fileio.py
index 1cb2f121..4c43c460 100644
--- a/pyvista/core/utilities/fileio.py
+++ b/pyvista/core/utilities/fileio.py
@@ -437,7 +437,11 @@ def from_meshio(mesh):
         else:
             vtk_type = meshio_to_vtk_type[c.type]
             numnodes = vtk_type_to_numnodes[vtk_type]
-            fill_values = np.full((len(c.data), 1), numnodes, dtype=c.data.dtype)
+            if numnodes == -1:
+                # Count nodes in each cell
+                fill_values = np.array([[len(data)] for data in c.data], dtype=c.data.dtype)
+            else:
+                fill_values = np.full((len(c.data), 1), numnodes, dtype=c.data.dtype)
             cells.append(np.hstack((fill_values, c.data)).ravel())
 
         cell_type += [vtk_type] * len(c.data)
@@ -581,7 +585,7 @@ def save_meshio(filename, mesh, file_format=None, **kwargs):
                 else cell[[0, 1, 3, 2]] if cell_type == 8 else cell[[0, 1, 3, 2, 4, 5, 7, 6]]
             )
             cell_type = cell_type if cell_type not in pixel_voxel else cell_type + 1
-            cell_type = vtk_to_meshio_type[cell_type] if cell_type != 7 else f"polygon{numnodes}"
+            cell_type = vtk_to_meshio_type[cell_type]
 
         if len(cells) > 0 and cells[-1][0] == cell_type:
             cells[-1][1].append(cell)


Test patch:
diff --git a/tests/test_meshio.py b/tests/test_meshio.py
index 38171fdc..904eaae5 100644
--- a/tests/test_meshio.py
+++ b/tests/test_meshio.py
@@ -7,6 +7,7 @@ import pytest
 import pyvista as pv
 from pyvista import examples
 
+cow = examples.download_cow().cast_to_unstructured_grid()
 beam = pv.UnstructuredGrid(examples.hexbeamfile)
 airplane = examples.load_airplane().cast_to_unstructured_grid()
 uniform = examples.load_uniform().cast_to_unstructured_grid()
@@ -94,7 +95,7 @@ polyhedron = meshio.Mesh(
 )
 
 
-@pytest.mark.parametrize("mesh_in", [beam, airplane, uniform, mesh2d, polyhedron])
+@pytest.mark.parametrize("mesh_in", [beam, airplane, uniform, mesh2d, polyhedron, cow])
 def test_meshio(mesh_in, tmpdir):
     if isinstance(mesh_in, meshio.Mesh):
         mesh_in = pv.from_meshio(mesh_in)