11import numpy as np
22import pyvista as pv
3- import ansys .dpf .core as dpf
4- from ansys .dpf .core import errors
3+ from typing import Union
54from vtk import (
65 VTK_HEXAHEDRON ,
76 VTK_LINE ,
2221 VTK_WEDGE ,
2322 vtkVersion ,
2423)
24+
25+ import ansys .dpf .core as dpf
26+ from ansys .dpf .core import errors
27+ from ansys .dpf .core .check_version import server_meet_version_and_raise
2528from ansys .dpf .core .elements import element_types
2629
2730VTK9 = vtkVersion ().GetVTKMajorVersion () >= 9
@@ -124,7 +127,7 @@ def __init__(
124127 ModuleNotFoundError .__init__ (self , msg )
125128
126129
127- def dpf_mesh_to_vtk_op (mesh , nodes , as_linear ):
130+ def dpf_mesh_to_vtk_op (mesh , nodes = None , as_linear = True ):
128131 """Return a pyvista unstructured grid given DPF node and element
129132 definitions from operators (server > 6.2)
130133
@@ -134,7 +137,7 @@ def dpf_mesh_to_vtk_op(mesh, nodes, as_linear):
134137 Meshed Region to export to pyVista format
135138
136139 nodes : dpf.Field
137- Field containing the nodes of the mesh.
140+ Field containing the node coordinates of the mesh.
138141
139142 as_linear : bool
140143 Export quadratic surface elements as linear.
@@ -333,25 +336,28 @@ def compute_offset():
333336 return pv .UnstructuredGrid (offset , cells , vtk_cell_type , node_coordinates )
334337
335338
336- def dpf_mesh_to_vtk (mesh , nodes = None , as_linear = True ):
337- """Return a pyvista unstructured grid given DPF node and element
338- definitions.
339+ def dpf_mesh_to_vtk (
340+ mesh : dpf .MeshedRegion ,
341+ nodes : Union [dpf .Field , None ] = None ,
342+ as_linear : bool = True
343+ ) -> pv .UnstructuredGrid :
344+ """Return a pyvista UnstructuredGrid given a pydpf MeshedRegion.
339345
340346 Parameters
341347 ----------
342348 mesh : dpf.MeshedRegion
343- Meshed Region to export to pyVista format
349+ Meshed Region to export to pyVista format.
344350
345351 nodes : dpf.Field, optional
346- Field containing the nodes of the mesh.
352+ Field containing the node coordinates of the mesh (useful to get a deformed geometry) .
347353
348354 as_linear : bool, optional
349355 Export quadratic surface elements as linear.
350356
351357 Returns
352358 -------
353- grid : pyvista.UnstructuredGrid
354- Unstructured grid of the DPF mesh.
359+ grid:
360+ UnstructuredGrid corresponding to the DPF mesh.
355361 """
356362 try :
357363 return dpf_mesh_to_vtk_op (mesh , nodes , as_linear )
@@ -363,3 +369,277 @@ def vtk_update_coordinates(vtk_grid, coordinates_array):
363369 from copy import copy
364370
365371 vtk_grid .points = copy (coordinates_array )
372+
373+
374+ def dpf_meshes_to_vtk (
375+ meshes_container : dpf .MeshesContainer ,
376+ nodes : Union [dpf .FieldsContainer , None ] = None ,
377+ as_linear : bool = True
378+ ) -> pv .UnstructuredGrid :
379+ """Return a pyvista UnstructuredGrid given a pydpf MeshedRegion.
380+
381+ Parameters
382+ ----------
383+ meshes_container:
384+ MeshesContainer to export to pyVista format.
385+
386+ nodes:
387+ FieldsContainer containing the node coordinates for each mesh
388+ (useful to get a deformed geometry). The labels must match a field to a mesh.
389+
390+ as_linear : bool, optional
391+ Export quadratic surface elements as linear.
392+
393+ Returns
394+ -------
395+ grid:
396+ UnstructuredGrid corresponding to the DPF meshes.
397+ """
398+ grids = []
399+ for i , mesh in enumerate (meshes_container ):
400+ nodes_i = None
401+ if nodes :
402+ nodes_i = nodes [i ]
403+ grids .append (dpf_mesh_to_vtk (mesh , nodes_i , as_linear ))
404+ return pv .MultiBlock (grids ).combine ()
405+
406+
407+ def dpf_field_to_vtk (
408+ field : dpf .Field ,
409+ meshed_region : Union [dpf .MeshedRegion , None ] = None ,
410+ nodes : Union [dpf .Field , None ] = None ,
411+ as_linear : bool = True
412+ ) -> pv .UnstructuredGrid :
413+ """Return a pyvista UnstructuredGrid given a DPF Field.
414+
415+ Parameters
416+ ----------
417+ field:
418+ Field to export to pyVista format.
419+
420+ meshed_region:
421+ Mesh to associate to the field.
422+ Useful for fluid results where the field is not automatically associated to its mesh.
423+
424+ nodes:
425+ Field containing the node coordinates of the mesh (useful to get a deformed geometry).
426+
427+ as_linear:
428+ Export quadratic surface elements as linear.
429+
430+ Returns
431+ -------
432+ grid:
433+ UnstructuredGrid corresponding to the DPF Field.
434+ """
435+ # Check Field location
436+ supported_locations = [
437+ dpf .locations .nodal , dpf .locations .elemental , dpf .locations .faces , dpf .locations .overall
438+ ]
439+ if field .location not in supported_locations :
440+ raise ValueError (
441+ f"Supported field locations for translation to VTK are: { supported_locations } ."
442+ )
443+
444+ # Associate the provided mesh with the field
445+ if meshed_region :
446+ field .meshed_region = meshed_region
447+ else :
448+ try :
449+ meshed_region = field .meshed_region
450+ except errors .DPFServerException as e :
451+ if "the field doesn't have this support type" in str (e ):
452+ raise ValueError ("The field does not have a meshed_region." )
453+ else :
454+ raise e
455+ except RuntimeError as e :
456+ if "The field's support is not a mesh" in str (e ):
457+ raise ValueError ("The field does not have a meshed_region." )
458+ else :
459+ raise e
460+
461+ # Initialize the bare UnstructuredGrid
462+ if meshed_region .nodes .n_nodes == 0 :
463+ raise ValueError ("The field does not have a meshed_region." )
464+ grid = dpf_mesh_to_vtk (mesh = meshed_region , nodes = nodes , as_linear = as_linear )
465+
466+ # Map Field.data to the VTK mesh
467+ overall_data = _map_field_to_mesh (field = field , meshed_region = meshed_region )
468+
469+ # Update the UnstructuredGrid
470+ if field .location == dpf .locations .nodal :
471+ grid .point_data [field .name ] = overall_data
472+ else :
473+ grid .cell_data [field .name ] = overall_data
474+ return grid
475+
476+
477+ def dpf_fieldscontainer_to_vtk (
478+ fields_container : dpf .FieldsContainer ,
479+ meshes_container : Union [dpf .MeshesContainer , None ] = None ,
480+ nodes : Union [dpf .Field , None ] = None ,
481+ as_linear : bool = True
482+ ) -> pv .UnstructuredGrid :
483+ """Return a pyvista UnstructuredGrid given a DPF FieldsContainer.
484+
485+ If the fields have different mesh supports, a global merged mesh support is created.
486+
487+ Parameters
488+ ----------
489+ fields_container:
490+ FieldsContainer to export to pyVista format.
491+
492+ meshes_container:
493+ MeshesContainer with meshes to associate to the fields in the FieldsContainer.
494+ Useful for fluid results where the fields are not automatically associated to their mesh.
495+
496+ nodes:
497+ Field containing the node coordinates of the mesh (useful to get a deformed geometry).
498+
499+ as_linear:
500+ Export quadratic surface elements as linear.
501+
502+ Returns
503+ -------
504+ grid:
505+ UnstructuredGrid corresponding to the DPF Field.
506+ """
507+ # Check Field location
508+ supported_locations = [
509+ dpf .locations .nodal , dpf .locations .elemental , dpf .locations .faces , dpf .locations .overall
510+ ]
511+ if fields_container [0 ].location not in supported_locations :
512+ raise ValueError (
513+ f"Supported field locations for translation to VTK are: { supported_locations } ."
514+ )
515+
516+ # Associate the meshes in meshes_container to the corresponding fields if provided
517+ if meshes_container :
518+ for i , mesh in enumerate (meshes_container ):
519+ label_space = meshes_container .get_label_space (i )
520+ fields_container .get_field (
521+ label_space_or_index = label_space
522+ ).meshed_region = meshes_container .get_mesh (label_space_or_index = label_space )
523+
524+ # Initialize the bare UnstructuredGrid
525+ # Loop on the fields to check if merging supports is necessary
526+ meshes = []
527+ for field in fields_container :
528+ if field .meshed_region not in meshes :
529+ meshes .append (field .meshed_region )
530+ if len (meshes )> 1 :
531+ # Merge the meshed_regions
532+ merge_op = dpf .operators .utility .merge_meshes (server = fields_container ._server )
533+ for i , mesh in enumerate (meshes ):
534+ merge_op .connect (i , mesh )
535+ meshed_region = merge_op .eval ()
536+ else :
537+ meshed_region = meshes [0 ]
538+ if meshed_region .nodes .n_nodes == 0 :
539+ raise ValueError ("The meshed_region of the fields contains no nodes." )
540+ grid = dpf_mesh_to_vtk (mesh = meshed_region , nodes = nodes , as_linear = as_linear )
541+
542+ for i , field in enumerate (fields_container ):
543+ # Map Field.data to the VTK mesh
544+ overall_data = _map_field_to_mesh (field = field , meshed_region = meshed_region )
545+ label_space = fields_container .get_label_space (i )
546+ label_space = dict ([(k , label_space [k ]) for k in sorted (label_space .keys ())])
547+ field .name = field .name + f" { label_space } "
548+ # Update the UnstructuredGrid
549+ if field .location == dpf .locations .nodal :
550+ grid .point_data [field .name ] = overall_data
551+ else :
552+ grid .cell_data [field .name ] = overall_data
553+
554+ return grid
555+
556+
557+ def _map_field_to_mesh (
558+ field : Union [dpf .Field , dpf .PropertyField ],
559+ meshed_region : dpf .MeshedRegion
560+ ) -> np .ndarray :
561+ """Return an NumPy array of 'Field.data' mapped to the mesh on the field's location."""
562+ location = field .location
563+ if location == dpf .locations .nodal :
564+ mesh_location = meshed_region .nodes
565+ elif location == dpf .locations .elemental :
566+ mesh_location = meshed_region .elements
567+ elif location == dpf .locations .faces :
568+ mesh_location = meshed_region .faces
569+ if len (mesh_location ) == 0 :
570+ raise ValueError ("No faces found to plot on" )
571+ elif location == dpf .locations .overall :
572+ mesh_location = meshed_region .elements
573+ else :
574+ raise ValueError ("Only elemental, nodal or faces location are supported for plotting." )
575+ component_count = field .component_count
576+ if component_count > 1 :
577+ overall_data = np .full ((len (mesh_location ), component_count ), np .nan )
578+ else :
579+ overall_data = np .full (len (mesh_location ), np .nan )
580+ if location != dpf .locations .overall :
581+ ind , mask = mesh_location .map_scoping (field .scoping )
582+ overall_data [ind ] = field .data [mask ]
583+ else :
584+ overall_data [:] = field .data [0 ]
585+ return overall_data
586+
587+
588+ def dpf_property_field_to_vtk (
589+ property_field : dpf .PropertyField ,
590+ meshed_region : dpf .MeshedRegion ,
591+ nodes : Union [dpf .Field , None ] = None ,
592+ as_linear : bool = True
593+ ) -> pv .UnstructuredGrid :
594+ """Return a pyvista UnstructuredGrid given a DPF PropertyField.
595+
596+ ..note:
597+ Available starting with DPF 2024.2.pre1.
598+
599+ Parameters
600+ ----------
601+ property_field:
602+ PropertyField to export to pyVista format.
603+
604+ meshed_region:
605+ Mesh to associate to the property field.
606+
607+ nodes:
608+ Field containing the node coordinates of the mesh (useful to get a deformed geometry).
609+
610+ as_linear:
611+ Export quadratic surface elements as linear.
612+
613+ Returns
614+ -------
615+ grid:
616+ UnstructuredGrid corresponding to the DPF PropertyField.
617+ """
618+ server_meet_version_and_raise (
619+ required_version = "8.1" ,
620+ server = meshed_region ._server ,
621+ msg = "Use of dpf_property_field_to_vtk requires DPF 2024.2.pre1 or above."
622+ )
623+ # Check Field location
624+ supported_locations = [
625+ dpf .locations .nodal , dpf .locations .elemental , dpf .locations .faces , dpf .locations .overall
626+ ]
627+ if property_field .location not in supported_locations :
628+ raise ValueError (
629+ f"Supported field locations for translation to VTK are: { supported_locations } ."
630+ )
631+
632+ # Initialize the bare UnstructuredGrid
633+ if meshed_region .nodes .n_nodes == 0 :
634+ raise ValueError ("The property field does not have a meshed_region." )
635+ grid = dpf_mesh_to_vtk (mesh = meshed_region , nodes = nodes , as_linear = as_linear )
636+
637+ # Map Field.data to the VTK mesh
638+ overall_data = _map_field_to_mesh (field = property_field , meshed_region = meshed_region )
639+
640+ # Update the UnstructuredGrid
641+ if property_field .location == dpf .locations .nodal :
642+ grid .point_data [property_field .name ] = overall_data
643+ else :
644+ grid .cell_data [property_field .name ] = overall_data
645+ return grid
0 commit comments