diff --git a/src/ansys/dpf/post/selection.py b/src/ansys/dpf/post/selection.py index 9d19bd781..c5da3669c 100644 --- a/src/ansys/dpf/post/selection.py +++ b/src/ansys/dpf/post/selection.py @@ -33,6 +33,8 @@ class _WfNames: data_sources = "data_sources" scoping = "scoping" + time_scoping = "time_scoping" + mesh_scoping = "mesh_scoping" final_scoping = "final_scoping" scoping_a = "scoping_a" scoping_b = "scoping_b" @@ -93,7 +95,7 @@ def select_time_freq_sets(self, time_freq_sets: List[int]) -> None: ) op = operators.utility.forward(sets, server=self._server) self._selection.add_operator(op) - self._selection.set_output_name(_WfNames.scoping, op.outputs.any) + self._selection.set_output_name(_WfNames.time_scoping, op.outputs.any) def select_load_steps(self, load_steps: List[int]) -> None: """Select a list of load steps (one-based indexing). @@ -106,7 +108,7 @@ def select_load_steps(self, load_steps: List[int]) -> None: sets = time_freq_scoping_factory.scoping_by_load_steps(load_steps=load_steps) op = operators.utility.forward(sets, server=self._server) self._selection.add_operator(op) - self._selection.set_output_name(_WfNames.scoping, op.outputs.any) + self._selection.set_output_name(_WfNames.time_scoping, op.outputs.any) def select_with_scoping(self, scoping: Scoping): """Directly sets the scoping as the time/freq selection. @@ -124,7 +126,7 @@ def select_with_scoping(self, scoping: Scoping): op = operators.utility.forward(scoping, server=self._server) self._selection.add_operator(op) - self._selection.set_output_name(_WfNames.scoping, op.outputs.any) + self._selection.set_output_name(_WfNames.time_scoping, op.outputs.any) def select_time_freq_values( self, time_freq_values: Union[List[float], ndarray, Field] @@ -147,7 +149,7 @@ def select_time_freq_values( values = time_freq_field op = operators.utility.forward(values, server=self._server) self._selection.add_operator(op) - self._selection.set_output_name(_WfNames.scoping, op.outputs.any) + self._selection.set_output_name(_WfNames.time_scoping, op.outputs.any) def _evaluate_on(self, simulation: Simulation) -> Union[Scoping, None]: """Returns what is evaluated from the selections made on a given Simulation. @@ -178,7 +180,7 @@ def _evaluate_on(self, simulation: Simulation) -> Union[Scoping, None]: _WfNames.data_sources, simulation._model.metadata.data_sources ) - return self._selection.get_output(_WfNames.scoping, types.scoping) + return self._selection.get_output(_WfNames.time_scoping, types.scoping) def apply_to(self, simulation: Simulation) -> List[int]: """Performs the currently defined selection on the given Simulation. @@ -253,7 +255,9 @@ def select_named_selection( # self._selection.set_input_name( # _WfNames.streams, op.inputs.streams_container # ) - self._selection.set_output_name(_WfNames.scoping, op.outputs.mesh_scoping) + self._selection.set_output_name( + _WfNames.mesh_scoping, op.outputs.mesh_scoping + ) else: op = operators.utility.merge_scopings(server=self._server) forward_ds = operators.utility.forward(any=None, server=self._server) @@ -273,7 +277,9 @@ def select_named_selection( mesh_scoping_op.connect(3, forward_sc.outputs.any) mesh_scoping_op.connect(4, forward_ds.outputs.any) op.connect(pin, mesh_scoping_op.outputs.mesh_scoping) - self._selection.set_output_name(_WfNames.scoping, op.outputs.merged_scoping) + self._selection.set_output_name( + _WfNames.mesh_scoping, op.outputs.merged_scoping + ) def select_external_layer( self, @@ -365,11 +371,11 @@ def select_external_layer( self._selection.set_output_name(_WfNames.external_layer, op.outputs.mesh) if location == locations.nodal: self._selection.set_output_name( - _WfNames.scoping, op.outputs.nodes_mesh_scoping + _WfNames.mesh_scoping, op.outputs.nodes_mesh_scoping ) elif location == locations.elemental or location == locations.elemental_nodal: self._selection.set_output_name( - _WfNames.scoping, op.outputs.elements_mesh_scoping + _WfNames.mesh_scoping, op.outputs.elements_mesh_scoping ) def select_skin( @@ -463,7 +469,7 @@ def select_skin( self._selection.set_output_name(_WfNames.skin, op.outputs.mesh) if location == locations.nodal and result_native_location == locations.nodal: self._selection.set_output_name( - _WfNames.scoping, op.outputs.nodes_mesh_scoping + _WfNames.mesh_scoping, op.outputs.nodes_mesh_scoping ) elif not _is_model_cyclic(is_model_cyclic) and ( @@ -478,7 +484,7 @@ def select_skin( _WfNames.initial_mesh, transpose_op.inputs.meshed_region ) self._selection.set_output_name( - _WfNames.scoping, transpose_op.outputs.mesh_scoping_as_scoping + _WfNames.mesh_scoping, transpose_op.outputs.mesh_scoping_as_scoping ) def select_with_scoping(self, scoping: Scoping): @@ -497,7 +503,7 @@ def select_with_scoping(self, scoping: Scoping): op = operators.utility.forward(scoping, server=self._server) self._selection.add_operator(op) - self._selection.set_output_name(_WfNames.scoping, op.outputs.any) + self._selection.set_output_name(_WfNames.mesh_scoping, op.outputs.any) def select_nodes(self, nodes: Union[List[int], Scoping]) -> None: """Select nodes using their IDs or a nodal mesh scoping. @@ -546,7 +552,7 @@ def select_nodes_of_elements( ) self._selection.add_operator(op) self._selection.set_output_name( - _WfNames.scoping, op.outputs.mesh_scoping_as_scoping + _WfNames.mesh_scoping, op.outputs.mesh_scoping_as_scoping ) def select_nodes_of_faces( @@ -576,7 +582,7 @@ def select_nodes_of_faces( ) self._selection.add_operator(op) self._selection.set_output_name( - _WfNames.scoping, op.outputs.mesh_scoping_as_scoping + _WfNames.mesh_scoping, op.outputs.mesh_scoping_as_scoping ) def select_faces_of_elements( @@ -608,7 +614,7 @@ def select_faces_of_elements( ) self._selection.add_operator(op) self._selection.set_output_name( - _WfNames.scoping, op.outputs.mesh_scoping_as_scoping + _WfNames.mesh_scoping, op.outputs.mesh_scoping_as_scoping ) def select_faces(self, faces: Union[List[int], Scoping]) -> None: @@ -665,10 +671,12 @@ def intersect( new_wf.add_operator(intersect_op) new_wf.set_input_name(_WfNames.scoping_a, intersect_op.inputs.scopingA) new_wf.set_input_name(_WfNames.scoping_b, intersect_op.inputs.scopingB) - new_wf.set_output_name(_WfNames.scoping, intersect_op.outputs.intersection) - new_wf.connect_with(self._selection, {_WfNames.scoping: _WfNames.scoping_a}) + new_wf.set_output_name(_WfNames.mesh_scoping, intersect_op.outputs.intersection) + new_wf.connect_with( + self._selection, {_WfNames.mesh_scoping: _WfNames.scoping_a} + ) new_wf.connect_with( - spatial_selection._selection, {_WfNames.scoping: _WfNames.scoping_b} + spatial_selection._selection, {_WfNames.mesh_scoping: _WfNames.scoping_b} ) self._selection = new_wf @@ -704,7 +712,7 @@ def _evaluate_on(self, simulation: Simulation) -> Union[Scoping, None]: _WfNames.data_sources, simulation._model.metadata.data_sources ) - return self._selection.get_output(_WfNames.scoping, types.scoping) + return self._selection.get_output(_WfNames.mesh_scoping, types.scoping) def apply_to(self, simulation: Simulation) -> List[int]: """Performs the currently defined selection on the given Simulation. diff --git a/src/ansys/dpf/post/static_mechanical_simulation.py b/src/ansys/dpf/post/static_mechanical_simulation.py index 3f9d5c8b2..e690a2f78 100644 --- a/src/ansys/dpf/post/static_mechanical_simulation.py +++ b/src/ansys/dpf/post/static_mechanical_simulation.py @@ -26,6 +26,7 @@ def _get_result_workflow( selection: Union[Selection, None] = None, expand_cyclic: Union[bool, List[Union[int, List[int]]]] = True, phase_angle_cyclic: Union[float, None] = None, + split_by: Union[str, None] = None, ) -> (core.Workflow, Union[str, list[str], None], str): """Generate (without evaluating) the Workflow to extract results.""" comp, to_extract, _ = self._create_components(base_name, category, components) @@ -47,12 +48,12 @@ def _get_result_workflow( # Its output is selected as future workflow output for now out = result_op.outputs.fields_container # Its inputs are selected as workflow inputs for merging with selection workflows - wf.set_input_name("time_scoping", result_op.inputs.time_scoping) - wf.set_input_name("mesh_scoping", result_op.inputs.mesh_scoping) + wf.set_input_name(_WfNames.time_scoping, result_op.inputs.time_scoping) + wf.set_input_name(_WfNames.mesh_scoping, result_op.inputs.mesh_scoping) wf.connect_with( selection.time_freq_selection._selection, - output_input_names=("scoping", "time_scoping"), + output_input_names=(_WfNames.time_scoping, _WfNames.time_scoping), ) if selection.requires_mesh: mesh_wf = core.Workflow(server=self._model._server) @@ -65,10 +66,50 @@ def _get_result_workflow( output_input_names={_WfNames.initial_mesh: _WfNames.initial_mesh}, ) - wf.connect_with( - selection.spatial_selection._selection, - output_input_names={"scoping": "mesh_scoping"}, - ) + if split_by: + split_wf = core.Workflow(server=self._model._server) + outputs_to_inputs = {} + if split_by in [ + core.common.elemental_properties.material, + core.common.elemental_properties.element_shape, + ]: + split_op = core.operators.scoping.split_on_property_type( + label1=split_by, + requested_location=location, + server=self._model._server, + ) + if ( + _WfNames.mesh_scoping + in selection.spatial_selection._selection.output_names + ): + split_wf.set_input_name( + _WfNames.mesh_scoping, split_op.inputs.mesh_scoping + ) + outputs_to_inputs[_WfNames.mesh_scoping] = _WfNames.mesh_scoping + if _WfNames.mesh in selection.spatial_selection._selection.output_names: + split_wf.set_input_name(_WfNames.mesh, split_op.inputs.mesh) + outputs_to_inputs[_WfNames.mesh] = _WfNames.mesh + else: + split_op.inputs.mesh.connect(self.mesh._meshed_region) + split_wf.set_output_name( + _WfNames.mesh_scoping, split_op.outputs.mesh_scoping + ) + split_wf.add_operator(split_op) + + split_wf.connect_with( + selection.spatial_selection._selection, + output_input_names=outputs_to_inputs, + ) + + wf.connect_with( + split_wf, + output_input_names={_WfNames.mesh_scoping: _WfNames.mesh_scoping}, + ) + else: + wf.connect_with( + selection.spatial_selection._selection, + output_input_names={_WfNames.mesh_scoping: _WfNames.mesh_scoping}, + ) # Treat cyclic cases wf = self._treat_cyclic(expand_cyclic, phase_angle_cyclic, wf) @@ -187,6 +228,7 @@ def _get_result( phase_angle_cyclic: Union[float, None] = None, external_layer: Union[bool, List[int]] = False, skin: Union[bool, List[int]] = False, + split_by: Union[str, None] = None, ) -> DataFrame: """Extract results from the simulation. @@ -254,6 +296,8 @@ def _get_result( is computed over list of elements (not supported for cyclic symmetry). Getting the skin on more than one result (several time freq sets, split data...) is only supported starting with Ansys 2023R2. + split_by: + Property to split the result on ("mat", "elshape", "part"). Returns ------- @@ -299,6 +343,7 @@ def _get_result( selection=selection, expand_cyclic=expand_cyclic, phase_angle_cyclic=phase_angle_cyclic, + split_by=split_by, ) # Evaluate the workflow @@ -336,6 +381,7 @@ def displacement( phase_angle_cyclic: Union[float, None] = None, external_layer: Union[bool, List[int]] = False, skin: Union[bool, List[int]] = False, + split_by: Union[str, None] = None, ) -> DataFrame: """Extract displacement results from the simulation. @@ -392,6 +438,8 @@ def displacement( is computed over list of elements (not supported for cyclic symmetry). Getting the skin on more than one result (several time freq sets, split data...) is only supported starting with Ansys 2023R2. + split_by: + Property to split the result on ("mat", "elshape", "part"). Returns ------- @@ -416,6 +464,7 @@ def displacement( phase_angle_cyclic=phase_angle_cyclic, external_layer=external_layer, skin=skin, + split_by=split_by, ) def stress(