@@ -147,6 +147,7 @@ def add_mesh(self, meshed_region, deform_by=None, scale_factor=1.0, as_linear=Tr
147147 grid = meshed_region ._as_vtk (
148148 meshed_region .nodes .coordinates_field , as_linear = as_linear
149149 )
150+ meshed_region ._full_grid = grid
150151 meshed_region .as_linear = as_linear
151152 else :
152153 grid = meshed_region .grid
@@ -317,8 +318,14 @@ def add_field(
317318 show_min = False
318319 elif location == locations .overall :
319320 mesh_location = meshed_region .elements
321+ elif location == locations .elemental_nodal :
322+ mesh_location = meshed_region .elements
323+ # If ElementalNodal, first extend results to mid-nodes
324+ field = dpf .core .operators .averaging .extend_to_mid_nodes (field = field ).eval ()
320325 else :
321- raise ValueError ("Only elemental, nodal or faces location are supported for plotting." )
326+ raise ValueError (
327+ "Only elemental, elemental nodal, nodal, faces, or overall location are supported for plotting."
328+ )
322329
323330 # Treat multilayered shells
324331 if not isinstance (shell_layer , eshell_layers ):
@@ -333,13 +340,27 @@ def add_field(
333340 )
334341 field = change_shell_layer_op .get_output (0 , core .types .field )
335342
343+ location_data_len = meshed_region .location_data_len (location )
336344 component_count = field .component_count
337345 if component_count > 1 :
338- overall_data = np .full ((len ( mesh_location ) , component_count ), np .nan )
346+ overall_data = np .full ((location_data_len , component_count ), np .nan )
339347 else :
340- overall_data = np .full (len ( mesh_location ) , np .nan )
348+ overall_data = np .full (location_data_len , np .nan )
341349 if location != locations .overall :
342350 ind , mask = mesh_location .map_scoping (field .scoping )
351+
352+ # Rework ind and mask to take into account n_nodes per element if ElementalNodal
353+ if location == locations .elemental_nodal :
354+ n_nodes_list = meshed_region .get_elemental_nodal_size_list ().astype (np .int32 )
355+ first_index = np .insert (np .cumsum (n_nodes_list )[:- 1 ], 0 , 0 ).astype (np .int32 )
356+ mask_2 = np .asarray (
357+ [mask_i for i , mask_i in enumerate (mask ) for _ in range (n_nodes_list [ind [i ]])]
358+ )
359+ ind_2 = np .asarray (
360+ [first_index [ind_i ] + j for ind_i in ind for j in range (n_nodes_list [ind_i ])]
361+ )
362+ mask = mask_2
363+ ind = ind_2
343364 overall_data [ind ] = field .data [mask ]
344365 else :
345366 overall_data [:] = field .data [0 ]
@@ -348,12 +369,22 @@ def add_field(
348369 # Have to remove any active scalar field from the pre-existing grid object,
349370 # otherwise we get two scalar bars when calling several plot_contour on the same mesh
350371 # but not for the same field. The PyVista UnstructuredGrid keeps memory of it.
351- if not deform_by :
352- grid = meshed_region . grid
353- else :
372+ if location == locations . elemental_nodal :
373+ as_linear = False
374+ if deform_by :
354375 grid = meshed_region ._as_vtk (
355- meshed_region .deform_by (deform_by , scale_factor ), as_linear
376+ meshed_region .deform_by (deform_by , scale_factor ), as_linear = as_linear
356377 )
378+ else :
379+ if as_linear != meshed_region .as_linear :
380+ grid = meshed_region ._as_vtk (
381+ meshed_region .nodes .coordinates_field , as_linear = as_linear
382+ )
383+ meshed_region .as_linear = as_linear
384+ else :
385+ grid = meshed_region .grid
386+ if location == locations .elemental_nodal :
387+ grid = grid .shrink (1.0 )
357388 grid .set_active_scalars (None )
358389 self ._plotter .add_mesh (grid , scalars = overall_data , ** kwargs_in )
359390
@@ -976,15 +1007,14 @@ def plot_contour(
9761007 import warnings
9771008
9781009 warnings .simplefilter ("ignore" )
979-
9801010 if isinstance (field_or_fields_container , (dpf .core .Field , dpf .core .FieldsContainer )):
9811011 fields_container = None
9821012 if isinstance (field_or_fields_container , dpf .core .Field ):
9831013 fields_container = dpf .core .FieldsContainer (
9841014 server = field_or_fields_container ._server
9851015 )
986- fields_container .add_label (DefinitionLabels . time )
987- fields_container .add_field ({DefinitionLabels . time : 1 }, field_or_fields_container )
1016+ fields_container .add_label ("id" )
1017+ fields_container .add_field ({"id" : 1 }, field_or_fields_container )
9881018 elif isinstance (field_or_fields_container , dpf .core .FieldsContainer ):
9891019 fields_container = field_or_fields_container
9901020 else :
@@ -1022,43 +1052,96 @@ def plot_contour(
10221052 unit = field .unit
10231053 break
10241054
1055+ # If ElementalNodal, first extend results to mid-nodes
1056+ if location == locations .elemental_nodal :
1057+ fields_container = dpf .core .operators .averaging .extend_to_mid_nodes_fc (
1058+ fields_container = fields_container
1059+ ).eval ()
1060+
1061+ location_data_len = mesh .location_data_len (location )
10251062 if location == locations .nodal :
10261063 mesh_location = mesh .nodes
10271064 elif location == locations .elemental :
10281065 mesh_location = mesh .elements
10291066 elif location == locations .faces :
10301067 mesh_location = mesh .faces
1068+ elif location == locations .elemental_nodal :
1069+ mesh_location = mesh .elements
10311070 else :
1032- raise ValueError ("Only elemental, nodal or faces location are supported for plotting." )
1071+ raise ValueError (
1072+ "Only elemental, elemental nodal, nodal or faces location are supported for plotting."
1073+ )
10331074
10341075 # pre-loop: check if shell layers for each field, if yes, set the shell layers
1035- changeOp = core .Operator ("change_shellLayers" )
1036- for field in fields_container :
1037- shell_layer_check = field .shell_layers
1038- if shell_layer_check in [
1039- eshell_layers .topbottom ,
1040- eshell_layers .topbottommid ,
1041- ]:
1042- changeOp .inputs .fields_container .connect (fields_container )
1043- sl = eshell_layers .top
1044- if shell_layers is not None :
1045- if not isinstance (shell_layers , eshell_layers ):
1046- raise TypeError (
1047- "shell_layer attribute must be a core.shell_layers instance."
1048- )
1049- sl = shell_layers
1050- changeOp .inputs .e_shell_layer .connect (sl .value ) # top layers taken
1051- fields_container = changeOp .get_output (0 , core .types .fields_container )
1052- break
1076+ changeOp = core .operators .utility .change_shell_layers ()
1077+ if location == locations .elemental_nodal :
1078+ # change_shell_layers does not support elemental_nodal when given a fields_container
1079+ new_fields_container = dpf .core .FieldsContainer ()
1080+ for l in fields_container .labels :
1081+ new_fields_container .add_label (l )
1082+ for i , field in enumerate (fields_container ):
1083+ label_space_i = fields_container .get_label_space (i )
1084+ shell_layer_check = field .shell_layers
1085+ if shell_layer_check in [
1086+ eshell_layers .topbottom ,
1087+ eshell_layers .topbottommid ,
1088+ ]:
1089+ changeOp .inputs .fields_container .connect (field )
1090+ changeOp .inputs .merge .connect (True )
1091+ sl = eshell_layers .top
1092+ if shell_layers is not None :
1093+ if not isinstance (shell_layers , eshell_layers ):
1094+ raise TypeError (
1095+ "shell_layer attribute must be a core.shell_layers instance."
1096+ )
1097+ sl = shell_layers
1098+ changeOp .inputs .e_shell_layer .connect (sl .value ) # top layers taken
1099+ field = changeOp .get_output (0 , core .types .field )
1100+ new_fields_container .add_field (label_space = label_space_i , field = field )
1101+ fields_container = new_fields_container
1102+ else :
1103+ for field in fields_container :
1104+ shell_layer_check = field .shell_layers
1105+ if shell_layer_check in [
1106+ eshell_layers .topbottom ,
1107+ eshell_layers .topbottommid ,
1108+ ]:
1109+ changeOp .inputs .fields_container .connect (fields_container )
1110+ sl = eshell_layers .top
1111+ if shell_layers is not None :
1112+ if not isinstance (shell_layers , eshell_layers ):
1113+ raise TypeError (
1114+ "shell_layer attribute must be a core.shell_layers instance."
1115+ )
1116+ sl = shell_layers
1117+ changeOp .inputs .e_shell_layer .connect (sl .value ) # top layers taken
1118+ fields_container = changeOp .get_output (0 , core .types .fields_container )
1119+ break
10531120
10541121 # Merge field data into a single array
10551122 if component_count > 1 :
1056- overall_data = np .full ((len ( mesh_location ) , component_count ), np .nan )
1123+ overall_data = np .full ((location_data_len , component_count ), np .nan )
10571124 else :
1058- overall_data = np .full (len (mesh_location ), np .nan )
1125+ overall_data = np .full (location_data_len , np .nan )
1126+
1127+ # field._data_pointer gives the first index of each entity data
1128+ # (should be of size nb_elements)
10591129
10601130 for field in fields_container :
10611131 ind , mask = mesh_location .map_scoping (field .scoping )
1132+ if location == locations .elemental_nodal :
1133+ # Rework ind and mask to take into account n_nodes per element
1134+ # entity_index_map = field._data_pointer
1135+ n_nodes_list = mesh .get_elemental_nodal_size_list ().astype (np .int32 )
1136+ first_index = np .insert (np .cumsum (n_nodes_list )[:- 1 ], 0 , 0 ).astype (np .int32 )
1137+ mask_2 = np .asarray (
1138+ [mask_i for i , mask_i in enumerate (mask ) for _ in range (n_nodes_list [ind [i ]])]
1139+ )
1140+ ind_2 = np .asarray (
1141+ [first_index [ind_i ] + j for ind_i in ind for j in range (n_nodes_list [ind_i ])]
1142+ )
1143+ mask = mask_2
1144+ ind = ind_2
10621145 overall_data [ind ] = field .data [mask ]
10631146
10641147 # create the plotter and add the meshes
@@ -1087,15 +1170,20 @@ def plot_contour(
10871170 bound_method = self ._internal_plotter ._plotter .add_mesh , ** kwargs
10881171 )
10891172 as_linear = True
1173+ if location == locations .elemental_nodal :
1174+ as_linear = False
10901175 if deform_by :
10911176 grid = mesh ._as_vtk (mesh .deform_by (deform_by , scale_factor ), as_linear = as_linear )
10921177 self ._internal_plotter .add_scale_factor_legend (scale_factor , ** kwargs )
10931178 else :
10941179 if as_linear != mesh .as_linear :
10951180 grid = mesh ._as_vtk (mesh .nodes .coordinates_field , as_linear = as_linear )
1181+ mesh ._full_grid = grid
10961182 mesh .as_linear = as_linear
10971183 else :
10981184 grid = mesh .grid
1185+ if location == locations .elemental_nodal :
1186+ grid = grid .shrink (1.0 )
10991187 grid .clear_data ()
11001188 self ._internal_plotter ._plotter .add_mesh (grid , scalars = overall_data , ** kwargs_in )
11011189
0 commit comments