Skip to content

Commit

Permalink
Doc/cyclic symmetry (#517)
Browse files Browse the repository at this point in the history
* add example about post-processing a cyclic symmetry analysis

* document limitation about expanded cyclic symmetry models

---------

Co-authored-by: Nellie Shum <[email protected]>
  • Loading branch information
roosre and nshum4 authored Sep 18, 2024
1 parent e44f311 commit cfa6929
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 8 deletions.
1 change: 1 addition & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Here are some key features of PyDPF Composites:
Limitations
'''''''''''
- Only the Mechanical APDL solver is supported.
- The post-processing of expanded cyclic symmetry models is not implemented.
- The following operators and features are only supported if the model was
preprocessed with ACP and if the corresponding lay-up definition file is passed to the :class:`.CompositeModel` class.

Expand Down
159 changes: 159 additions & 0 deletions examples/014_cyclic_symmetry_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""
.. _cyclic_symmetry_example:
Cyclic symmetry
---------------
This example shows how to postprocess a cyclic symmetry analysis.
The initial (original) sector can be postprocessed with the same tools
as a standard analysis. The postprocessing workflow is demonstrated by
running a failure analysis, extracting ply-wise stresses, and implementing
a custom failure criterion.
The postprocessing of expanded sectors is not yet supported.
"""

# %%
# Set up analysis
# ~~~~~~~~~~~~~~~
# Setting up the analysis consists of loading the required modules, connecting to the
# DPF server, and retrieving the example files.
#
# Load Ansys libraries and helper functions.
import ansys.dpf.core as dpf

from ansys.dpf.composites.composite_model import CompositeModel
from ansys.dpf.composites.constants import FailureOutput, Sym3x3TensorComponent
from ansys.dpf.composites.example_helper import get_continuous_fiber_example_files
from ansys.dpf.composites.failure_criteria import CombinedFailureCriterion, MaxStressCriterion
from ansys.dpf.composites.layup_info import get_all_analysis_ply_names
from ansys.dpf.composites.layup_info.material_properties import MaterialProperty
from ansys.dpf.composites.ply_wise_data import SpotReductionStrategy, get_ply_wise_data
from ansys.dpf.composites.select_indices import get_selected_indices
from ansys.dpf.composites.server_helpers import connect_to_or_start_server

# %%
# Start a DPF server and copy the example files into the current working directory.
server = connect_to_or_start_server()
composite_files = get_continuous_fiber_example_files(server, "cyclic_symmetry")

# %%
# Create a composite model.
composite_model = CompositeModel(composite_files, server)

# %%
# Evaluate a combined failure criterion.
combined_failure_criterion = CombinedFailureCriterion(failure_criteria=[MaxStressCriterion()])
failure_result = composite_model.evaluate_failure_criteria(combined_failure_criterion)

# %%
# Plot the failure results.
irf_field = failure_result.get_field({"failure_label": FailureOutput.FAILURE_VALUE})
irf_field.plot()

# %%
# Plot ply-wise stresses
# ~~~~~~~~~~~~~~~~~~~~~~
# All functions in PyDPF Composites can be used to
# postprocess the initial (original) sector.

rst_stream = composite_model.core_model.metadata.streams_provider
stress_operator = dpf.operators.result.stress()
stress_operator.inputs.streams_container.connect(rst_stream)
stress_operator.inputs.bool_rotate_to_global(False)
stress_container = stress_operator.outputs.fields_container()

all_ply_names = get_all_analysis_ply_names(composite_model.get_mesh())
all_ply_names

component_s11 = Sym3x3TensorComponent.TENSOR11
stress_field = stress_container[0]
elemental_values = get_ply_wise_data(
field=stress_field,
ply_name="P3L1__ModelingPly.1",
mesh=composite_model.get_mesh(),
component=component_s11,
spot_reduction_strategy=SpotReductionStrategy.MAX,
requested_location=dpf.locations.elemental,
)
composite_model.get_mesh().plot(elemental_values)

# %%
# Custom failure criterion
# ~~~~~~~~~~~~~~~~~~~~~~~~
# The following code block shows how to implement a custom failure criterion.
# It computes the inverse reserve factor for each element with respect to
# fiber failure. The criterion distinguishes between tension and compression.

# Prepare dict with the material properties.
property_xt = MaterialProperty.Stress_Limits_Xt
property_xc = MaterialProperty.Stress_Limits_Xc
property_dict = composite_model.get_constant_property_dict([property_xt, property_xc])

result_field = dpf.field.Field(location=dpf.locations.elemental, nature=dpf.natures.scalar)
with result_field.as_local_field() as local_result_field:
# Process only the layered elements
for element_id in composite_model.get_all_layered_element_ids():
element_info = composite_model.get_element_info(element_id)
element_irf_max = 0.0
stress_data = stress_field.get_entity_data_by_id(element_id)
for layer_index, dpf_material_id in enumerate(element_info.dpf_material_ids):
xt = property_dict[dpf_material_id][property_xt]
xc = property_dict[dpf_material_id][property_xc]
selected_indices = get_selected_indices(element_info, layers=[layer_index])
# Maximum of fiber failure in tension and compression
layer_stress_values = stress_data[selected_indices][:, component_s11]
max_s11 = max(layer_stress_values)
min_s11 = min(layer_stress_values)
if xt > 0 and max_s11 > 0:
element_irf_max = max(max_s11 / xt, element_irf_max)
if xc < 0 and min_s11 < 0:
element_irf_max = max(min_s11 / xc, element_irf_max)

local_result_field.append([element_irf_max], element_id)

composite_model.get_mesh().plot(result_field)

# %%
# Plot deformations on the expanded model
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# You can expand the deformations of the cyclic symmetry model as shown below.
# The same expansion is possible for strains and stresses. For more information, see `Ansys DPF`_.
#
# .. _Ansys DPF: https://dpf.docs.pyansys.com/version/stable/

# Get the displacements and expand them
symmetry_option = 2 # fully expand the model
u_cyc = composite_model.core_model.results.displacement()
u_cyc.inputs.read_cyclic(symmetry_option)
# expand the displacements
deformations = u_cyc.outputs.fields_container()[0]

# Get and expand the mesh
mesh_provider = composite_model.core_model.metadata.mesh_provider
mesh_provider.inputs.read_cyclic(symmetry_option)
mesh = mesh_provider.outputs.mesh()
# Plot the expanded deformations
mesh.plot(deformations)
15 changes: 8 additions & 7 deletions src/ansys/dpf/composites/_composite_model_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def __init__(
data_sources=self.data_sources,
material_operators=self.material_operators,
unit_system=self._unit_system,
rst_stream_provider=self._get_rst_streams_provider(),
rst_stream_provider=self.get_rst_streams_provider(),
)

# self._layup_provider.outputs.layup_model_context_type.get_data() does not work because
Expand Down Expand Up @@ -154,7 +154,7 @@ def __init__(

self._element_info_provider = get_element_info_provider(
mesh=self.get_mesh(),
stream_provider_or_data_source=self._get_rst_streams_provider(),
stream_provider_or_data_source=self.get_rst_streams_provider(),
material_provider=self.material_operators.material_provider,
)
self._layup_properties_provider = LayupPropertiesProvider(
Expand Down Expand Up @@ -370,7 +370,7 @@ def evaluate_failure_criteria(
chunking_data_tree.add({"named_selections": ns_in})

chunking_generator = dpf.Operator("composite::scope_generator")
chunking_generator.inputs.stream_provider(self._get_rst_streams_provider())
chunking_generator.inputs.stream_provider(self.get_rst_streams_provider())
chunking_generator.inputs.data_tree(chunking_data_tree)
if self.data_sources.composite:
chunking_generator.inputs.data_sources(self.data_sources.composite)
Expand Down Expand Up @@ -412,7 +412,7 @@ def evaluate_failure_criteria(
self.material_operators.material_provider.outputs
)
evaluate_failure_criterion_per_scope_op.inputs.stream_provider(
self._get_rst_streams_provider()
self.get_rst_streams_provider()
)
evaluate_failure_criterion_per_scope_op.inputs.mesh(self.get_mesh())
if version_equal_or_later(self._server, "8.0"):
Expand Down Expand Up @@ -555,7 +555,7 @@ def get_sampling_point(
self._material_operators,
self.get_mesh(),
self._layup_provider,
self._get_rst_streams_provider(),
self.get_rst_streams_provider(),
self._data_sources.rst,
self._unit_system,
time_in,
Expand Down Expand Up @@ -688,7 +688,7 @@ def get_constant_property_dict(
return get_constant_property_dict(
material_properties=material_properties,
materials_provider=self.material_operators.material_provider,
data_source_or_streams_provider=self._get_rst_streams_provider(),
data_source_or_streams_provider=self.get_rst_streams_provider(),
mesh=self.get_mesh(),
)

Expand Down Expand Up @@ -766,7 +766,8 @@ def get_all_layered_element_ids_for_composite_definition_label(
)
return self.get_all_layered_element_ids()

def _get_rst_streams_provider(self) -> Operator:
def get_rst_streams_provider(self) -> Operator:
"""Get the streams provider of the loaded result file."""
return self._core_model.metadata.streams_provider

def _first_composite_definition_label_if_only_one(self) -> str:
Expand Down
4 changes: 4 additions & 0 deletions src/ansys/dpf/composites/_composite_model_impl_2023r2.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,3 +644,7 @@ def _first_composite_definition_label_if_only_one(self) -> str:
f"Multiple composite definition keys exist: {self.composite_definition_labels}. "
f"Specify a key explicitly."
)

def get_rst_streams_provider(self) -> Operator:
"""Get the streams provider of the loaded result file."""
return self._core_model.metadata.streams_provider
4 changes: 4 additions & 0 deletions src/ansys/dpf/composites/composite_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ def get_mesh(self, composite_definition_label: Optional[str] = None) -> MeshedRe
"""
return self._implementation.get_mesh(composite_definition_label)

def get_rst_streams_provider(self) -> Operator:
"""Get the streams provider of the loaded result file."""
return self._implementation.get_rst_streams_provider()

def get_layup_operator(self, composite_definition_label: Optional[str] = None) -> Operator:
"""Get the lay-up operator.
Expand Down
12 changes: 11 additions & 1 deletion src/ansys/dpf/composites/example_helper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@


# Example URL to run the examples locally
# EXAMPLE_REPO = "file:////D:/Development/pyansys-example-data/pydpf-composites/"
# EXAMPLE_REPO = "file:////D:/dev/pyansys-example-data/pydpf-composites/"


@dataclass
Expand Down Expand Up @@ -165,6 +165,16 @@ class _ShortFiberExampleLocation:
},
),
),
"cyclic_symmetry": _ContinuousFiberExampleLocation(
directory="cyclic_symmetry",
files=_ContinuousFiberCompositesExampleFilenames(
rst=["file.rst"],
engineering_data="MatML.xml",
composite={
"solid": _ContinuousFiberCompositeFiles(definition="ACPSolidModel_SM.h5"),
},
),
),
}

_short_fiber_examples: dict[str, _ShortFiberExampleLocation] = {
Expand Down

0 comments on commit cfa6929

Please sign in to comment.