Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2734aaa
Fix wrong input name for solid_to_skin operator
PProfizi Jul 10, 2023
cd4746b
Fix retro
PProfizi Jul 10, 2023
548aa0c
Merge branch 'master' into fix/skin_result_extraction
PProfizi Oct 26, 2023
dab0442
Working modif, let's try to add mesh_by_scop back for performance
PProfizi Oct 27, 2023
fe7a25e
Cannot add mesh_by_scop back as it is actually the source of the problem
PProfizi Oct 27, 2023
552b032
Add testing
PProfizi Oct 27, 2023
b040a22
Start fixing refs of tests
PProfizi Oct 27, 2023
15308a2
Fix refs of tests for current server
PProfizi Oct 27, 2023
3bc8779
Fix refs of tests
PProfizi Oct 27, 2023
e3393a0
Fix refs of tests
PProfizi Oct 30, 2023
8f81e3d
Fix typo
PProfizi Nov 3, 2023
29926b8
WIP
PProfizi Nov 7, 2023
f6161a4
Use "initial_mesh_wf_out" name
PProfizi Nov 7, 2023
a43d2fb
Use "result_mesh" name
PProfizi Nov 7, 2023
c34562a
WIP
PProfizi Nov 7, 2023
d7f1189
Fix mesh connection
PProfizi Nov 8, 2023
fa8aa84
Update 02-mesh-skin.py example with titles to plots
PProfizi Nov 13, 2023
adc8a46
Remove progress bar from selection workflows
PProfizi Nov 13, 2023
e24b07a
Remove progress bar from initial mesh workflow
PProfizi Nov 13, 2023
72c1d14
Temporary hack to remove mesh cached in model's streams
PProfizi Nov 13, 2023
f15e96c
Add tests WIP
PProfizi Nov 21, 2023
2d37b76
Apply suggestions from code review
PProfizi Nov 23, 2023
039ab55
Merge remote-tracking branch 'origin/fix/skin_result_extraction' into…
PProfizi Dec 20, 2023
e5abaad
Remove dirty trick to reset model.metadata now a fix has been made
PProfizi Dec 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions examples/02-Performance-Improvements/02-mesh-skin.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
# Extract displacement data on the skin.

displacement_skin = simulation.displacement(skin=True)
displacement_skin.plot()
displacement_skin.plot(text="U with skin=True")

print(f"number of nodes with skin=True: {len(displacement_skin.index.mesh_index)}")
print(f"number of nodes with skin=False: {len(simulation.mesh.node_ids)}")
Expand All @@ -62,7 +62,7 @@
# Averaging, and invariants computation are done through a solid to skin connectivity mapping.

elemental_stress_skin = simulation.stress_principal_elemental(components=[1], skin=True)
elemental_stress_skin.plot()
elemental_stress_skin.plot(text="S1 with skin=True")

print(
f"number of elements with skin=True: {len(elemental_stress_skin.index.mesh_index)}"
Expand All @@ -71,15 +71,15 @@


elastic_strain_eqv_skin = simulation.elastic_strain_eqv_von_mises_nodal(skin=True)
elastic_strain_eqv_skin.plot()
elastic_strain_eqv_skin.plot(text="EE with skin=True")

###############################################################################
# Extract the external layer on a selection of elements
# -----------------------------------------------------
# Extract on the skin of a selection of elements
# ----------------------------------------------

all_elements = simulation.mesh.element_ids
elements = []
for i in range(0, int(all_elements.size / 2)):
elements.append(all_elements[i])
elemental_stress_skin = simulation.stress_principal_elemental(skin=elements)
elemental_stress_skin.plot()
elemental_stress_skin.plot(text="S1 with skin=elements")
4 changes: 2 additions & 2 deletions src/ansys/dpf/post/post_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,10 @@ def load_simulation(
simulation_type = AvailableSimulationTypes.unsteady_fluid
else:
raise ValueError(
f"Unknown analysis type '{analysis_type}' for {physics_type}."
f"Unknown analysis type '{analysis_type}' for '{physics_type}'."
)
else:
raise ValueError(f"Unknown physics type '{physics_type}.")
raise ValueError(f"Unknown physics type '{physics_type}'.")

if simulation_type in [
getattr(AvailableSimulationTypes, x) for x in vars(AvailableSimulationTypes)
Expand Down
77 changes: 69 additions & 8 deletions src/ansys/dpf/post/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class _WfNames:
cyclic_sectors_to_expand = "cyclic_sectors_to_expand"
cyclic_phase = "cyclic_phase"
result = "result"
result_mesh = "result_mesh"


def _is_model_cyclic(is_cyclic: str):
Expand All @@ -68,6 +69,7 @@ def __init__(self, server: Union[BaseServer, None] = None):
"""
self._server = get_or_create_server(server)
self._selection = Workflow(server=self._server)
self._selection.progress_bar = False

def select_time_freq_indices(self, time_freq_indices: List[int]) -> None:
"""Select time frequency sets by their indices (zero-based indexing).
Expand Down Expand Up @@ -219,10 +221,33 @@ def __init__(
"""
self._server = get_or_create_server(server)
self._selection = Workflow(server=self._server)

self._selection.progress_bar = False
self._current_initial_mesh_output = None
if scoping is not None:
self.select_with_scoping(scoping)

def reduce_mesh(self, element_ids: Union[Scoping, List[int]]):
"""Reduce the mesh with consideration to the given list of elements.

Parameters
----------
element_ids:
Scoping or list of IDs of elements to reduce the mesh to.
"""
if not isinstance(element_ids, Scoping):
element_ids = Scoping(
ids=element_ids, location=locations.elemental, server=self._server
)
op = operators.mesh.from_scoping(element_ids, server=self._server)
self._selection.add_operator(op)
# If the workflow already has an initial_mesh output, connect this after
if self._current_initial_mesh_output:
op.inputs.mesh.connect(self._current_initial_mesh_output)
else:
self._selection.set_input_name(_WfNames.initial_mesh, op.inputs.mesh)
self._selection.set_output_name(_WfNames.initial_mesh, op.outputs.mesh)
self._current_initial_mesh_output = op.outputs.mesh

def select_named_selection(
self,
named_selection: Union[str, List[str]],
Expand Down Expand Up @@ -393,7 +418,7 @@ def select_skin(
Native (as found in the file) location of the output result. Used to pick
the location of the scoping.
elements:
List of elements to use to compute the external layer,
List of elements to use to compute the skin,
default is all the elements of the model. Getting the skin on a selection of
elements for cyclic symmetry is not supported.
is_model_cyclic:
Expand Down Expand Up @@ -443,14 +468,30 @@ def select_skin(
elements = Scoping(
server=self._server, ids=elements, location=locations.elemental
)
mesh_by_scop_op = operators.mesh.from_scoping(
scoping=elements, server=self._server
forward_op = operators.utility.forward()
mesh_input = forward_op.inputs.any
nodal_scoping = operators.scoping.transpose(
mesh_scoping=elements, server=self._server
)
nodal_scoping.connect(1, forward_op)
op.connect(0, forward_op)
op.inputs.mesh_scoping.connect(
nodal_scoping.outputs.mesh_scoping_as_scoping
)
mesh_input = mesh_by_scop_op.inputs.mesh
op.inputs.mesh.connect(mesh_by_scop_op)

if mesh_input is not None:
self._selection.set_input_name(_WfNames.initial_mesh, mesh_input)
# If the workflow already has an initial_mesh output, connect this after
if self._current_initial_mesh_output:
mesh_input.connect(self._current_initial_mesh_output)
else:
self._selection.set_input_name(_WfNames.initial_mesh, mesh_input)
# self._selection.set_output_name(_WfNames.initial_mesh, op.outputs.mesh)
# self._current_initial_mesh_output = op.outputs.mesh

# pass
# else:
# # otherwise, set this as the initial_mesh input
# self._selection.set_input_name(_WfNames.initial_mesh, mesh_input)
if location == result_native_location:
self._selection.set_output_name(_WfNames.mesh, op.outputs.mesh)
self._selection.set_output_name(_WfNames.skin, op.outputs.mesh)
Expand Down Expand Up @@ -721,9 +762,14 @@ def requires_mesh(self) -> bool:
"""Whether the selection workflow requires a ``mesh`` as an input or not."""
return _WfNames.initial_mesh in self._selection.input_names

@property
def reduces_mesh(self) -> bool:
"""Whether the selection workflow gives a reduced ``mesh`` as an output or not."""
return _WfNames.initial_mesh in self._selection.output_names

@property
def outputs_mesh(self) -> bool:
"""Whether the selection workflow as an output named ``mesh``."""
"""Whether the selection workflow has an output named ``mesh``."""
return _WfNames.mesh in self._selection.output_names

def requires_manual_averaging(
Expand Down Expand Up @@ -798,6 +844,16 @@ def spatial_selection(self) -> SpatialSelection:
def spatial_selection(self, value: SpatialSelection):
self._spatial_selection = value

def reduce_mesh(self, element_ids: Union[List[int]]):
"""Reduce the mesh with consideration to the given list of elements.

Parameters
----------
element_ids:
List of IDs of elements to reduce the mesh to.
"""
self._spatial_selection.reduce_mesh(element_ids)

def select_time_freq_indices(self, time_freq_indices: List[int]) -> None:
"""Select time frequency sets by their indices (zero-based indexing).

Expand Down Expand Up @@ -1016,6 +1072,11 @@ def requires_mesh(self) -> bool:
"""Whether the selection workflow requires a ``initial_mesh`` as an input or not."""
return self._spatial_selection.requires_mesh

@property
def reduces_mesh(self) -> bool:
"""Whether the selection workflow gives a reduced ``mesh`` as an output or not."""
return self._spatial_selection.reduces_mesh

@property
def outputs_mesh(self) -> bool:
"""Whether the selection workflow as an output named ``mesh``."""
Expand Down
31 changes: 30 additions & 1 deletion src/ansys/dpf/post/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,12 +536,12 @@ def _build_result_workflow(
force_elemental_nodal: bool,
) -> (dpf.Workflow, dpf.Operator):
op = self._model.operator(name=name)
op.connect(7, self.mesh._meshed_region)
if force_elemental_nodal:
op.connect(9, "ElementalNodal")
elif location:
op.connect(9, location)
wf = Workflow(server=self._model._server)
wf.set_input_name(_WfNames.result_mesh, op, 7)
wf.set_input_name(_WfNames.read_cyclic, op, 14)
wf.set_input_name(_WfNames.cyclic_sectors_to_expand, op, 18)
wf.set_input_name(_WfNames.cyclic_phase, op, 19)
Expand Down Expand Up @@ -816,6 +816,7 @@ def _build_selection(
external_layer: bool = False,
skin: Union[bool, List[int]] = False,
expand_cyclic: Union[bool, List[Union[int, List[int]]]] = True,
reduce_mesh: Union[bool, List[int]] = False,
) -> Selection:
tot = (
(node_ids is not None)
Expand All @@ -841,6 +842,34 @@ def _build_selection(
selection = Selection(server=self._model._server)
# Create the SpatialSelection

# Reduce mesh if necessary
if reduce_mesh is not False:
extract_scoping = None
# Reduce on the list of reduced
if not isinstance(reduce_mesh, bool):
# reduce_mesh=list
select_mesh = Selection()
select_mesh.select_elements(reduce_mesh)
extract_scoping = select_mesh.spatial_selection.apply_to(self)
else:
# reduce_mesh=True
if not isinstance(skin, bool):
select_mesh = Selection()
select_mesh.select_elements(skin)
extract_scoping = select_mesh.spatial_selection.apply_to(self)
else:
# reduce_mesh=True, skin=True|False
if element_ids is not None:
select_mesh = Selection()
select_mesh.select_elements(element_ids)
extract_scoping = select_mesh.spatial_selection.apply_to(self)
elif named_selections:
select_mesh = Selection()
select_mesh.select_named_selection(named_selections)
extract_scoping = select_mesh.spatial_selection.apply_to(self)
if extract_scoping is not None:
selection.reduce_mesh(extract_scoping)

# First: the skin and the external layer to be able to have both a mesh scoping and
# the skin/external layer
if (skin is not None and skin is not False) or (
Expand Down
58 changes: 42 additions & 16 deletions src/ansys/dpf/post/static_mechanical_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def _get_result(
phase_angle_cyclic: Union[float, None] = None,
external_layer: Union[bool, List[int]] = False,
skin: Union[bool, List[int]] = False,
reduce_mesh: Union[bool, List[int]] = False,
) -> DataFrame:
"""Extract results from the simulation.

Expand Down Expand Up @@ -104,6 +105,11 @@ 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.
reduce_mesh:
Perform a reduction of the mesh based on, by order of priority:
- The list of element IDs given to this parameter
- Parameter skin if reduce_mesh=True
- Parameter element_ids if reduce_mesh=True and skin=None

Returns
-------
Expand Down Expand Up @@ -138,6 +144,7 @@ def _get_result(
location=location,
external_layer=external_layer,
skin=skin,
reduce_mesh=reduce_mesh,
)

comp, to_extract, columns = self._create_components(
Expand Down Expand Up @@ -173,18 +180,30 @@ def _get_result(
)
if selection.requires_mesh:
mesh_wf = core.Workflow(server=self._model._server)
mesh_wf.progress_bar = False
mesh_wf.set_output_name(
_WfNames.initial_mesh, self._model.metadata.mesh_provider
"initial_mesh_wf_out", self._model.metadata.mesh_provider
)
selection.spatial_selection._selection.connect_with(
mesh_wf,
output_input_names={_WfNames.initial_mesh: _WfNames.initial_mesh},
output_input_names={"initial_mesh_wf_out": _WfNames.initial_mesh},
)
else:
result_op.connect(7, self.mesh._meshed_region)

if selection.reduces_mesh:
wf.connect_with(
selection.spatial_selection._selection,
output_input_names={
"scoping": "mesh_scoping",
"initial_mesh": _WfNames.result_mesh,
},
)
else:
wf.connect_with(
selection.spatial_selection._selection,
output_input_names={"scoping": "mesh_scoping"},
)

wf.connect_with(
selection.spatial_selection._selection,
output_input_names={"scoping": "mesh_scoping"},
)

# Treat cyclic cases
wf = self._treat_cyclic(expand_cyclic, phase_angle_cyclic, wf)
Expand Down Expand Up @@ -1099,6 +1118,7 @@ def stress_eqv_von_mises_elemental(
phase_angle_cyclic: Union[float, None] = None,
external_layer: Union[bool, List[int]] = False,
skin: Union[bool, List[int]] = False,
reduce_mesh: Union[bool, List[int]] = False,
) -> DataFrame:
"""Extract elemental equivalent von Mises stress results from the simulation.

Expand Down Expand Up @@ -1137,17 +1157,22 @@ def stress_eqv_von_mises_elemental(
If the problem is multi-stage, can take a list of lists of sector numbers, ordered
by stage.
phase_angle_cyclic:
For cyclic problems, phase angle to apply (in degrees).
For cyclic problems, phase angle to apply (in degrees).
external_layer:
Select the external layer (last layer of solid elements under the skin)
of the mesh for plotting and data extraction. If a list is passed, the external
layer is computed over list of elements.
Select the external layer (last layer of solid elements under the skin)
of the mesh for plotting and data extraction. If a list is passed, the external
layer is computed over list of elements.
skin:
Select the skin (creates new 2D elements connecting the external nodes)
of the mesh for plotting and data extraction. If a list is passed, the skin
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.
Select the skin (creates new 2D elements connecting the external nodes)
of the mesh for plotting and data extraction. If a list is passed, the skin
is computed over list of elements (not supported for cyclic symmetry). Getting the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this description true only for reduce_mesh==True?

Copy link
Contributor Author

@PProfizi PProfizi Nov 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cbellot000 do you mean the fact that having skin=[1, 2, 3] will map results onto the skin elements of elements 1, 2, and 3, based on the skin for the initial mesh?

If reduce_mesh=False, you will get results on "skin elements corresponding to elements 1, 2, and 3 based on the skin generated from the initial mesh".

If reduce_mesh=True, as described in the docstring for it, you will get results on the "all skin elements of the skin generated from the mesh composed of elements 1, 2, and 3 only".

skin on more than one result (several time freq sets, split data...) is only
supported starting with Ansys 2023 R2.
reduce_mesh:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about the external layer?
I'm not sure about the name "reduce" any other ideas, @rlagha @FedericoNegri?

Copy link
Contributor Author

@PProfizi PProfizi Nov 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cbellot000 you're right, I'll check what it does in combination with external_layer. As for the name reduce_mesh, it is definitely just a proposal. I thought about it though, and to me this is one of the most explicit ways to describe what it does.

Perform a reduction of the mesh based on, by order of priority:
- The list of element IDs given to this parameter
- Parameter skin if reduce_mesh=True
- Parameter element_ids if reduce_mesh=True and skin=None

Returns
-------
Expand All @@ -1171,6 +1196,7 @@ def stress_eqv_von_mises_elemental(
phase_angle_cyclic=phase_angle_cyclic,
external_layer=external_layer,
skin=skin,
reduce_mesh=reduce_mesh,
)

def stress_eqv_von_mises_nodal(
Expand Down
Loading