From 1fb4700b6c4e7b0eec23fb7add61c4d3d7832b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Roos?= Date: Fri, 24 Nov 2023 11:52:32 +0100 Subject: [PATCH] update evaluate_failure_criteria to support the different types of lay-up models (ACP, RST, MIXED) Some cosmetic changes. --- doc/source/index.rst | 25 +++++++++++--- examples/008_assembly_example.py | 4 +-- ... => 099_dpf_composite_failure_workflow.py} | 0 .../dpf/composites/_composite_model_impl.py | 34 ++++++++++++++++--- .../dpf/composites/layup_info/__init__.py | 2 ++ .../dpf/composites/layup_info/_layup_info.py | 9 +++++ 6 files changed, 63 insertions(+), 11 deletions(-) rename examples/{011_dpf_composite_failure_workflow.py => 099_dpf_composite_failure_workflow.py} (100%) diff --git a/doc/source/index.rst b/doc/source/index.rst index 15620cdb2..c2c810a16 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -46,11 +46,17 @@ to implement custom failure criteria and computation. Provides developer installation and usage information. +The minimum set of inputs to run a post-processing for composites is the result file(s) of an +MAPDL solution and a material file (matml) which was generated by Engineering Data. +Lay-up files from ACP are optional and only required for some advanced operations. Refer to +:ref:`Limitations`. + Key features '''''''''''' Here are some key features of PyDPF Composites: +* Post-processing of layered shell and solid elements, whether they were preprocessed by ACP or not. * Failure criteria evaluation as shown in :ref:`Composite failure analysis `. * A :class:`.SamplingPoint` class for extracting and visualizing a result over the entire thickness of a laminate as shown in :ref:`Sampling point `. @@ -65,11 +71,22 @@ Here are some key features of PyDPF Composites: Limitations ''''''''''' -- Layered elements (section data) that have not been preprocessed with ACP are not supported. - For information on converting legacy models, see `Import of Legacy Mechanical APDL Composite Models`_ - in the Ansys Help. - Only the Mechanical APDL solver is supported. - +- 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`: + - The evaluation of the failure criteria for sandwich (:class:`FaceSheetWrinklingCriterion`, :class:`ShearCrimpingCriterion`) + for solid elements. Layered shell elements are always supported. + - The computation of interlaminar normal stresses (s3) for layered shell elements. + Otherwise, s3 is zero. This also affects 3D failure criteria which use s3, such as Puck 3D. + This limitation does not affect (layered) solid elements. + - The support of variable materials and material fields. Without ACP, only the + temperature is considered for the evaluation of variable material properties. + - Global plies, ply-wise scoping, and ply-wise failure criteria. Layer-wise post-processing is + always supported thanks to the scripting + - Plotting results on the reference surface of a laminate. + + Note: MAPDL models which have not been preprocessed with ACP can be converted. For more + information, see `Import of Legacy Mechanical APDL Composite Models`_ in the Ansys Help. .. _Ansys Workbench: https://download.ansys.com/Current%20Release .. _Import of Legacy Mechanical APDL Composite Models: https://ansyshelp.ansys.com/account/secured?returnurl=/Views/Secured/corp/v231/en/acp_ug/acp_import_legacy_APDL_comp.html diff --git a/examples/008_assembly_example.py b/examples/008_assembly_example.py index 7723ee3b0..1beeb02a9 100644 --- a/examples/008_assembly_example.py +++ b/examples/008_assembly_example.py @@ -57,8 +57,8 @@ irf_field.plot() # %% -# Plot IRF -# ~~~~~~~~ +# Plot IRF on The Reference Surface +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Plot the maximum IRF on the reference surface if version_equal_or_later(server, "8.0"): irf_field = output_all_elements.get_field( diff --git a/examples/011_dpf_composite_failure_workflow.py b/examples/099_dpf_composite_failure_workflow.py similarity index 100% rename from examples/011_dpf_composite_failure_workflow.py rename to examples/099_dpf_composite_failure_workflow.py diff --git a/src/ansys/dpf/composites/_composite_model_impl.py b/src/ansys/dpf/composites/_composite_model_impl.py index 3b687e76a..67991c6d1 100644 --- a/src/ansys/dpf/composites/_composite_model_impl.py +++ b/src/ansys/dpf/composites/_composite_model_impl.py @@ -22,6 +22,7 @@ ElementInfo, LayerProperty, LayupPropertiesProvider, + LayupModelModelContextType, add_layup_info_to_mesh, get_element_info_provider, ) @@ -177,7 +178,15 @@ def __init__( rst_stream_provider=self._get_rst_streams_provider() ) + # self._layup_provider.outputs.layup_model_context_type.get_data() does not work because + # int32 is not supported in the Python API. See bug 946754. + # 218 is the pin of LayupModelContext if version_equal_or_later(self._server, "8.0"): + self.layup_model_type = LayupModelModelContextType(self._layup_provider.get_output(218, int)) + else: + self.layup_model_type = LayupModelModelContextType.ACP if len(composite_files.composite) > 0 else LayupModelModelContextType.NOT_AVAILABLE + + if self._supports_reference_surface_operators(): self._reference_surface_and_mapping_field = _get_reference_surface_and_mapping_field( data_sources=self.data_sources.composite, unit_system=self._unit_system ) @@ -334,8 +343,6 @@ def evaluate_failure_criteria( # is irrelevant for cases without a ply scope, we set it to False here. write_data_for_full_element_scope = False - has_layup_provider = len(self._composite_files.composite) > 0 - # configure primary scoping scope_config = dpf.DataTree() if time_in: @@ -400,7 +407,12 @@ def evaluate_failure_criteria( self._get_rst_streams_provider() ) evaluate_failure_criterion_per_scope_op.inputs.mesh(self.get_mesh()) - evaluate_failure_criterion_per_scope_op.inputs.has_layup_provider(has_layup_provider) + if version_equal_or_later(self._server, "8.0"): + evaluate_failure_criterion_per_scope_op.inputs.layup_model_context_type(self.layup_model_type.value) + else: + evaluate_failure_criterion_per_scope_op.inputs.has_layup_provider( + self.layup_model_type != LayupModelModelContextType.NOT_AVAILABLE + ) evaluate_failure_criterion_per_scope_op.inputs.section_data_container( self._layup_provider.outputs.section_data_container ) @@ -425,7 +437,8 @@ def evaluate_failure_criteria( self.material_operators.material_support_provider.outputs ) - if has_layup_provider and write_data_for_full_element_scope: + if (self.layup_model_type != LayupModelModelContextType.NOT_AVAILABLE and + write_data_for_full_element_scope): add_default_data_op = dpf.Operator("composite::add_default_data") add_default_data_op.inputs.requested_element_scoping(chunking_generator.outputs) add_default_data_op.inputs.time_id( @@ -462,7 +475,7 @@ def evaluate_failure_criteria( "No output is generated! Please check the scope (element and ply ids)." ) - if version_equal_or_later(self._server, "8.0"): + if self._supports_reference_surface_operators(): self._map_to_reference_surface_operator.inputs.min_container( min_merger.outputs.merged_fields_container() ) @@ -743,3 +756,14 @@ 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." ) + + # Whether the reference surface operators are available or supported by the server + def _supports_reference_surface_operators(self): + if not version_equal_or_later(self._server, "8.0"): + return False + + if (self.layup_model_type == LayupModelModelContextType.ACP or + self.layup_model_type == LayupModelModelContextType.MIXED): + return True + + return False diff --git a/src/ansys/dpf/composites/layup_info/__init__.py b/src/ansys/dpf/composites/layup_info/__init__.py index 355e913c0..a1227902c 100644 --- a/src/ansys/dpf/composites/layup_info/__init__.py +++ b/src/ansys/dpf/composites/layup_info/__init__.py @@ -8,6 +8,7 @@ ElementInfo, ElementInfoProvider, LayupPropertiesProvider, + LayupModelModelContextType, get_all_analysis_ply_names, get_analysis_ply_index_to_name_map, get_dpf_material_id_by_analyis_ply_map, @@ -23,6 +24,7 @@ "LayerProperty", "LayupProperty", "LayupPropertiesProvider", + "LayupModelModelContextType", "get_all_analysis_ply_names", "get_analysis_ply_index_to_name_map", "get_dpf_material_id_by_analyis_ply_map", diff --git a/src/ansys/dpf/composites/layup_info/_layup_info.py b/src/ansys/dpf/composites/layup_info/_layup_info.py index fb4a2c944..0d30f4988 100644 --- a/src/ansys/dpf/composites/layup_info/_layup_info.py +++ b/src/ansys/dpf/composites/layup_info/_layup_info.py @@ -4,6 +4,7 @@ from dataclasses import dataclass from typing import Any, Optional, Union, cast from warnings import warn +from enum import Enum import ansys.dpf.core as dpf from ansys.dpf.core import DataSources, MeshedRegion, Operator, PropertyField @@ -60,6 +61,14 @@ def _get_analysis_ply(mesh: MeshedRegion, name: str, skip_check: bool = False) - return mesh.property_field(property_field_name) +# Note: must be in sync with the LayupModelContextTypeEnum in the C++ code +class LayupModelModelContextType(Enum): + NOT_AVAILABLE = 0 # no layup data + ACP = 1 # lay-up data was read from ACP + RST = 2 # lay-up data was read from RST + MIXED = 3 # lay-up data was read from RST and ACP + + @dataclass(frozen=True) class ElementInfo: """Provides lay-up information for an element.