1919# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121# SOFTWARE.
22+ import warnings
2223
2324import numpy as np
25+ import pyvista
2426import pyvista as pv
2527from typing import Union
2628from vtk import (
@@ -362,6 +364,7 @@ def dpf_mesh_to_vtk(
362364 mesh : dpf .MeshedRegion ,
363365 nodes : Union [dpf .Field , None ] = None ,
364366 as_linear : bool = True ,
367+ check_validity : bool = False ,
365368) -> pv .UnstructuredGrid :
366369 """Return a pyvista UnstructuredGrid given a pydpf MeshedRegion.
367370
@@ -376,18 +379,115 @@ def dpf_mesh_to_vtk(
376379 as_linear:
377380 Export quadratic surface elements as linear.
378381
379- export_faces :
380- Whether to export face elements along with volume elements for fluid meshes .
382+ check_validity :
383+ Whether to run the VTK cell validity check on the generated mesh and warn if not valid .
381384
382385 Returns
383386 -------
384387 grid:
385388 UnstructuredGrid corresponding to the DPF mesh.
386389 """
387390 try :
388- return dpf_mesh_to_vtk_op (mesh , nodes , as_linear )
391+ grid = dpf_mesh_to_vtk_op (mesh , nodes , as_linear )
389392 except (AttributeError , KeyError , errors .DPFServerException ):
390- return dpf_mesh_to_vtk_py (mesh , nodes , as_linear )
393+ grid = dpf_mesh_to_vtk_py (mesh , nodes , as_linear )
394+ if check_validity :
395+ valid , msg = vtk_mesh_is_valid (grid )
396+ if not valid :
397+ warnings .warn (f"\n VTK mesh validity check\n { msg } " )
398+ return grid
399+
400+
401+ def vtk_mesh_is_valid (grid : pv .UnstructuredGrid , verbose : bool = False ) -> (bool , str ):
402+ """Runs a vtk.CellValidator filter on the input grid.
403+
404+ Parameters
405+ ----------
406+ grid:
407+ A vtk mesh to validate.
408+ verbose:
409+ Whether to print the complete validation.
410+
411+ Returns
412+ -------
413+ valid:
414+ Whether the vtk mesh is valid according to the vtkCellValidator.
415+ message:
416+ Output message.
417+
418+ """
419+ from enum import Enum
420+
421+ from vtkmodules .vtkFiltersGeneral import vtkCellValidator
422+ from vtkmodules .util .numpy_support import vtk_to_numpy
423+
424+ # Prepare the Enum of possible validity states
425+ class State (Enum ):
426+ Valid = 0
427+ WrongNumberOfPoints = (1 ,)
428+ IntersectingEdges = (2 ,)
429+ IntersectingFaces = (4 ,)
430+ NoncontiguousEdges = (8 ,)
431+ Nonconvex = (16 ,)
432+ FacesAreOrientedIncorrectly = (32 ,)
433+
434+ # Run the cell validator
435+ cell_validator = vtkCellValidator ()
436+ cell_validator .SetInputData (grid )
437+ cell_validator .Update ()
438+ # Get the states for all cells as a numpy array
439+ cell_states = vtk_to_numpy (
440+ cell_validator .GetUnstructuredGridOutput ().GetCellData ().GetArray ("ValidityState" )
441+ )
442+ # Check for invalid states
443+ elem_with_wrong_number_of_nodes = np .where (cell_states == State .WrongNumberOfPoints .value )[0 ]
444+ elem_with_intersecting_edges = np .where (cell_states == State .IntersectingEdges .value )[0 ]
445+ elem_with_intersecting_faces = np .where (cell_states == State .IntersectingFaces .value )[0 ]
446+ elem_with_noncontiguous_edges = np .where (cell_states == State .NoncontiguousEdges .value )[0 ]
447+ elem_with_nonconvex_shape = np .where (cell_states == State .Nonconvex .value )[0 ]
448+ elem_with_badly_oriented_faces = np .where (
449+ cell_states == State .FacesAreOrientedIncorrectly .value
450+ )[0 ]
451+ # Build list of number of elements failing each test
452+ failing_elements_number = [
453+ len (elem_with_wrong_number_of_nodes ),
454+ len (elem_with_intersecting_edges ),
455+ len (elem_with_intersecting_faces ),
456+ len (elem_with_noncontiguous_edges ),
457+ len (elem_with_nonconvex_shape ),
458+ len (elem_with_badly_oriented_faces ),
459+ ]
460+ # Define whether mesh is valid
461+ mesh_is_valid = np .sum (failing_elements_number ) == 0
462+ # Build output message
463+ out_msg = ""
464+ if mesh_is_valid :
465+ out_msg += "Mesh is valid."
466+ else :
467+ out_msg += "Mesh is invalid because of (by index):\n "
468+ if failing_elements_number [0 ] > 0 :
469+ out_msg += (
470+ f" - { failing_elements_number [0 ]} elements with the wrong number of points:\n "
471+ )
472+ out_msg += f" { elem_with_wrong_number_of_nodes } \n "
473+ if failing_elements_number [1 ] > 0 :
474+ out_msg += f" - { failing_elements_number [1 ]} elements with intersecting edges:\n "
475+ out_msg += f" { elem_with_intersecting_edges } \n "
476+ if failing_elements_number [2 ] > 0 :
477+ out_msg += f" - { failing_elements_number [2 ]} elements with intersecting faces:\n "
478+ out_msg += f" { elem_with_intersecting_faces } \n "
479+ if failing_elements_number [3 ] > 0 :
480+ out_msg += f" - { failing_elements_number [3 ]} elements with non contiguous edges:\n "
481+ out_msg += f" { elem_with_noncontiguous_edges } \n "
482+ if failing_elements_number [4 ] > 0 :
483+ out_msg += f" - { failing_elements_number [4 ]} elements with non convex shape:\n "
484+ out_msg += f" { elem_with_nonconvex_shape } \n "
485+ if failing_elements_number [5 ] > 0 :
486+ out_msg += f" - { failing_elements_number [5 ]} elements with bad face orientations:\n "
487+ out_msg += f" { elem_with_badly_oriented_faces } \n "
488+ if verbose :
489+ print (out_msg )
490+ return mesh_is_valid , out_msg
391491
392492
393493def vtk_update_coordinates (vtk_grid , coordinates_array ):
0 commit comments