From 771b2992c35b097645740f83a814c92de47bb261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Roos?= Date: Thu, 5 Sep 2024 16:18:49 +0200 Subject: [PATCH] run pre-commit hooks --- examples/013_thermal_example.py | 61 +++++++------------ src/ansys/dpf/composites/_indexer.py | 10 ++- src/ansys/dpf/composites/constants.py | 2 +- .../dpf/composites/example_helper/__init__.py | 29 ++++++--- .../dpf/composites/layup_info/_layup_info.py | 14 ++--- src/ansys/dpf/composites/select_indices.py | 6 +- ...ment_info_output_all_element_types_test.py | 34 +++++++---- 7 files changed, 85 insertions(+), 71 deletions(-) diff --git a/examples/013_thermal_example.py b/examples/013_thermal_example.py index 47ba0dd8c..53bbaf29f 100644 --- a/examples/013_thermal_example.py +++ b/examples/013_thermal_example.py @@ -33,16 +33,16 @@ method to obtain the input files. PyDPF Composites can also be used to post-process thermal analyses. +In this case, the simulation is a two-step analysis. The results of the thermal analysis +are used as input for a structural analysis. The result file of the structural analysis is +post-processed. It also includes the temperatures of the thermal analysis. The example mimics a PCB which was modeled with Ansys Composites PrePost (ACP). -The solid model feature of ACP is used to generate the volume mesh. -Afterward, a thermal analysis is coupled with a static structural analysis. - -This examples shows how to extract the temperatures for a specific ply, -and material. +where the solid model feature of ACP is used to generate the volume mesh. +In detail, the example shows how to extract the temperatures for a specific ply, +and a specific material. """ - # %% # Set up analysis # ~~~~~~~~~~~~~~~ @@ -55,29 +55,13 @@ from ansys.dpf.composites.composite_model import CompositeModel from ansys.dpf.composites.constants import TEMPERATURE_COMPONENT from ansys.dpf.composites.example_helper import get_continuous_fiber_example_files -from ansys.dpf.composites.layup_info import AnalysisPlyInfoProvider, get_all_analysis_ply_names +from ansys.dpf.composites.layup_info import get_all_analysis_ply_names from ansys.dpf.composites.ply_wise_data import SpotReductionStrategy, get_ply_wise_data -from ansys.dpf.composites.select_indices import ( - get_selected_indices, - get_selected_indices_by_analysis_ply, - get_selected_indices_by_dpf_material_ids, -) -from ansys.dpf.composites.server_helpers import connect_to_or_start_server, version_equal_or_later -from ansys.dpf.composites.data_sources import get_composite_files_from_workbench_result_folder +from ansys.dpf.composites.select_indices import get_selected_indices_by_dpf_material_ids +from ansys.dpf.composites.server_helpers import connect_to_or_start_server server = connect_to_or_start_server() -#composite_files = get_continuous_fiber_example_files(server, "thermal_solid") - - -# Folder that opens after clicking "Open Solver Files Directory" -result_folder = r"D:\ANSYSDev\acp_test_model_data\model_data\postprocessing\temperature\solids\thermal_solids_files\dp0\SYS-4\MECH" -# result_folder = r"D:\ANSYSDev\acp_test_model_data\model_data\class40\class40_files\dp0\SYS-4\MECH" - -# Create the composite files object that contains -# the results file, the material properties file, and the -# composite definitions -composite_files = get_composite_files_from_workbench_result_folder(result_folder) - +composite_files = get_continuous_fiber_example_files(server, "thermal_solid") # %% # Initialize the model @@ -87,8 +71,8 @@ composite_model = CompositeModel(composite_files, server) # %% -# Get Temperatures -# ~~~~~~~~~~~~~~~~ +# Get Results - Temperatures +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ # The temperatures are stored under structural_temperature temp_op = composite_model.core_model.results.structural_temperature() temperatures_fc = temp_op.outputs.fields_container() @@ -97,14 +81,14 @@ # Ply-wise results # ~~~~~~~~~~~~~~~~ # Ply-wise results can be easily extracted using the function -# :func:`.get_ply_wise_data` and passing the ply name. +# :func:`.get_ply_wise_data` and by passing the ply name. all_ply_names = get_all_analysis_ply_names(composite_model.get_mesh()) print(all_ply_names) nodal_values = get_ply_wise_data( field=temperatures_fc, - ply_name='P1L1__ModelingPly.8', + ply_name="P1L1__ModelingPly.8", mesh=composite_model.get_mesh(), component=TEMPERATURE_COMPONENT, spot_reduction_strategy=SpotReductionStrategy.MAX, @@ -117,26 +101,27 @@ # Material-wise results # ~~~~~~~~~~~~~~~~~~~~~ # It is also possible to filter the results by material -# The maximum temperature per element is extracted -# for the UD Resin Epoxy/E-Glass material +# In this example the element-wise maximum temperature +# is extracted for the material `Honeycomb Aluminum Alloy`. +print(composite_model.material_names) +material_id = composite_model.material_names["Honeycomb Aluminum Alloy"] -ud_material_id = composite_model.material_names["UD Resin Epoxy/E-Glass"] +# get the last result field temperatures_field = temperatures_fc[-1] material_result_field = dpf.field.Field(location=dpf.locations.elemental, nature=dpf.natures.scalar) +# performance optimization: use a local field instead of a field which is pushed to the server with material_result_field.as_local_field() as local_result_field: element_ids = temperatures_field.scoping.ids for element_id in element_ids: element_info = composite_model.get_element_info(element_id) assert element_info is not None - if ud_material_id in element_info.dpf_material_ids: + if material_id in element_info.dpf_material_ids: temp_data = temperatures_field.get_entity_data_by_id(element_id) - selected_indices = get_selected_indices_by_dpf_material_ids( - element_info, [ud_material_id] - ) + selected_indices = get_selected_indices_by_dpf_material_ids(element_info, [material_id]) value = np.max(temp_data[selected_indices]) local_result_field.append([value], element_id) -composite_model.get_mesh().plot(material_result_field) \ No newline at end of file +composite_model.get_mesh().plot(material_result_field) diff --git a/src/ansys/dpf/composites/_indexer.py b/src/ansys/dpf/composites/_indexer.py index ca16c8d24..aedab268a 100644 --- a/src/ansys/dpf/composites/_indexer.py +++ b/src/ansys/dpf/composites/_indexer.py @@ -67,12 +67,16 @@ def by_id(self, entity_id: int) -> Optional[NDArray[np.int64]]: def _has_data_pointer(field: PropertyField | Field) -> bool: - if field._data_pointer is not None and field._data_pointer.any(): + if ( + field._data_pointer is not None and field._data_pointer.any() + ): # pylint: disable=protected-access return True return False -def get_property_field_indexer(field: PropertyField, no_bounds_check: bool) -> PropertyFieldIndexerSingleValue | PropertyFieldIndexerArrayValue: +def get_property_field_indexer( + field: PropertyField, no_bounds_check: bool +) -> PropertyFieldIndexerSingleValue | PropertyFieldIndexerArrayValue: """Get indexer for a property field. Parameters @@ -89,13 +93,13 @@ def get_property_field_indexer(field: PropertyField, no_bounds_check: bool) -> P return PropertyFieldIndexerNoDataPointer(field) - class FieldIndexSingleValueProtocol(Protocol): """Protocol for single value field indexer.""" def by_id(self, entity_id: int) -> Optional[np.double]: """Get index by id.""" + # General comment for all Indexer: # The .data call accesses the actual data. This sends the data over grpc which takes some time # It looks like it returns a DpfArray for non-local fields and an numpy array for local fields. diff --git a/src/ansys/dpf/composites/constants.py b/src/ansys/dpf/composites/constants.py index 1a48a3bce..4047bb867 100644 --- a/src/ansys/dpf/composites/constants.py +++ b/src/ansys/dpf/composites/constants.py @@ -30,7 +30,7 @@ "REF_SURFACE_NAME", "FAILURE_LABEL", "TIME_LABEL", - "TEMPERATURE_COMPONENT" + "TEMPERATURE_COMPONENT", ) FAILURE_LABEL = "failure_label" diff --git a/src/ansys/dpf/composites/example_helper/__init__.py b/src/ansys/dpf/composites/example_helper/__init__.py index 9dc4c152a..9173652c9 100644 --- a/src/ansys/dpf/composites/example_helper/__init__.py +++ b/src/ansys/dpf/composites/example_helper/__init__.py @@ -40,6 +40,10 @@ EXAMPLE_REPO = "https://github.com/ansys/example-data/raw/master/pydpf-composites/" +# Example URL to run the examples locally +# EXAMPLE_REPO = "file:////D:/Development/pyansys-example-data/pydpf-composites/" + + @dataclass class _ContinuousFiberCompositeFiles: definition: str @@ -62,7 +66,7 @@ class _ShortFiberCompositesExampleFilenames: @dataclass class _ContinuousFiberExampleLocation: - """Location of the a given continuous fiber example in the example_data repo. + """Location of a given continuous fiber example in the example_data repo. Parameters ---------- @@ -151,6 +155,16 @@ class _ShortFiberExampleLocation: }, ), ), + "thermal_solid": _ContinuousFiberExampleLocation( + directory="thermal_solid", + files=_ContinuousFiberCompositesExampleFilenames( + rst=["file.rst"], + engineering_data="MatML.xml", + composite={ + "shell": _ContinuousFiberCompositeFiles(definition="ACPSolidModel_SM.h5"), + }, + ), + ), } _short_fiber_examples: dict[str, _ShortFiberExampleLocation] = { @@ -168,7 +182,7 @@ def _get_file_url(directory: str, filename: str) -> str: def _download_and_upload_file( - directory: str, filename: str, tmpdir: str, server: dpf.server + directory: str, filename: str, tmpdir: str, server: dpf.server ) -> str: """Download example file from example_data repo and upload it the dpf server.""" file_url = _get_file_url(directory, filename) @@ -182,8 +196,8 @@ def _download_and_upload_file( def get_short_fiber_example_files( - server: dpf.server, - example_key: str, + server: dpf.server, + example_key: str, ) -> ShortFiberCompositesFiles: """Get short fiber example file by example key. @@ -197,7 +211,6 @@ def get_short_fiber_example_files( """ example_files = _short_fiber_examples[example_key] with tempfile.TemporaryDirectory() as tmpdir: - def get_server_path(filename: str) -> str: return _download_and_upload_file(example_files.directory, filename, tmpdir, server) @@ -210,9 +223,9 @@ def get_server_path(filename: str) -> str: def get_continuous_fiber_example_files( - server: dpf.server, - example_key: str, - skip_acp_layup_files: bool = False, + server: dpf.server, + example_key: str, + skip_acp_layup_files: bool = False, ) -> ContinuousFiberCompositesFiles: """Get continuous fiber example file by example key. diff --git a/src/ansys/dpf/composites/layup_info/_layup_info.py b/src/ansys/dpf/composites/layup_info/_layup_info.py index 5f49ddd64..8563dcc19 100644 --- a/src/ansys/dpf/composites/layup_info/_layup_info.py +++ b/src/ansys/dpf/composites/layup_info/_layup_info.py @@ -34,10 +34,7 @@ import numpy as np from numpy.typing import NDArray -from .._indexer import ( - get_field_indexer, - get_property_field_indexer -) +from .._indexer import get_field_indexer, get_property_field_indexer from ..server_helpers import version_equal_or_later, version_older_than from ._enums import LayupProperty @@ -398,7 +395,6 @@ def __init__( # focused on the most important properties. We can add different providers # for other properties (such as thickness and angles) - # Has to be always with bounds checks because it does not contain # data for all the elements @@ -412,7 +408,9 @@ def __init__( self.mesh = mesh self.corner_nodes_by_element_type = _get_corner_nodes_by_element_type_array() - self.apdl_material_indexer = get_property_field_indexer(self.mesh.elements.materials_field, no_bounds_checks) + self.apdl_material_indexer = get_property_field_indexer( + self.mesh.elements.materials_field, no_bounds_checks + ) self.solver_material_to_dpf_id = {} if solver_material_ids is not None: @@ -463,9 +461,7 @@ def get_element_info(self, element_id: int) -> Optional[ElementInfo]: dpf_material_ids = self.layer_materials.by_id(element_id) assert dpf_material_ids is not None if not isinstance(dpf_material_ids, np.ndarray): - dpf_material_ids = np.array( - [dpf_material_ids], dtype=np.int64 - ) + dpf_material_ids = np.array([dpf_material_ids], dtype=np.int64) assert layer_data[0] + 1 == len(layer_data), "Invalid size of layer data" n_layers = layer_data[0] diff --git a/src/ansys/dpf/composites/select_indices.py b/src/ansys/dpf/composites/select_indices.py index 88bfe0a70..28ea98148 100644 --- a/src/ansys/dpf/composites/select_indices.py +++ b/src/ansys/dpf/composites/select_indices.py @@ -143,7 +143,11 @@ def get_selected_indices( # Todo: Use numpy. Probably use ravel_multi_index method. current_index = 0 - num_nodes_per_spot = element_info.number_of_nodes_per_spot_plane if element_info.is_layered else element_info.n_corner_nodes + num_nodes_per_spot = ( + element_info.number_of_nodes_per_spot_plane + if element_info.is_layered + else element_info.n_corner_nodes + ) for layer_index in layer_indices: layer_start_index = layer_index * num_nodes_per_spot * element_info.n_spots diff --git a/tests/element_info_output_all_element_types_test.py b/tests/element_info_output_all_element_types_test.py index 76f6218a7..b840c3b2e 100644 --- a/tests/element_info_output_all_element_types_test.py +++ b/tests/element_info_output_all_element_types_test.py @@ -23,10 +23,10 @@ from collections.abc import Collection from dataclasses import dataclass import pathlib -import numpy as np import ansys.dpf.core as dpf from ansys.dpf.core import Field, MeshedRegion, PropertyField +import numpy as np import pytest from ansys.dpf.composites.constants import Spot @@ -223,9 +223,7 @@ def get_element_info_provider_for_rst(rst_file, server): mesh: MeshedRegion = mesh_provider.outputs.mesh() with pytest.raises(RuntimeError) as exc_info: - layup_info = get_element_info_provider( - mesh, stream_provider_or_data_source=rst_data_source - ) + layup_info = get_element_info_provider(mesh, stream_provider_or_data_source=rst_data_source) assert str(exc_info.value).startswith("Missing property field in mesh") material_property_field, layer_indices_property_field = get_layup_property_fields() mesh.set_property_field("element_layered_material_ids", material_property_field) @@ -235,7 +233,9 @@ def get_element_info_provider_for_rst(rst_file, server): def test_document_error_cases_indices(dpf_server): - layup_info = get_element_info_provider_for_rst("model_with_all_element_types_minimal_output.rst", dpf_server) + layup_info = get_element_info_provider_for_rst( + "model_with_all_element_types_minimal_output.rst", dpf_server + ) for element_id in get_element_ids().layered: with pytest.raises(RuntimeError) as exc_info: @@ -253,7 +253,9 @@ def test_document_error_cases_indices(dpf_server): "Computation of indices is not supported for non-layered elements." ) - layup_info = get_element_info_provider_for_rst("model_with_all_element_types_all_output.rst", dpf_server) + layup_info = get_element_info_provider_for_rst( + "model_with_all_element_types_all_output.rst", dpf_server + ) for element_id in get_element_ids().non_layered: with pytest.raises(RuntimeError) as exc_info: @@ -284,7 +286,9 @@ def test_document_error_cases_indices(dpf_server): selected_indices = get_selected_indices_by_dpf_material_ids(element_info, [5]) assert len(selected_indices) == 0 - layup_info = get_element_info_provider_for_rst("model_with_all_element_types_all_except_mid_output.rst", dpf_server) + layup_info = get_element_info_provider_for_rst( + "model_with_all_element_types_all_except_mid_output.rst", dpf_server + ) for element_id in get_element_ids().layered: with pytest.raises(RuntimeError) as exc_info: @@ -317,7 +321,9 @@ def test_select_indices_all_element_types(dpf_server): 51: np.array([0, 1, 2, 3, 4, 5]), # 6 node solid190 } - element_info_provider = get_element_info_provider_for_rst("model_with_all_element_types_all_output.rst", dpf_server) + element_info_provider = get_element_info_provider_for_rst( + "model_with_all_element_types_all_output.rst", dpf_server + ) element_ids = get_element_ids() for elem_id in element_ids.all: element_info = element_info_provider.get_element_info(elem_id) @@ -325,7 +331,9 @@ def test_select_indices_all_element_types(dpf_server): if element_info.is_layered: # All indices of the first layer indices = get_selected_indices(element_info, layers=[0]) - assert (indices == ref_indices_layer_0[elem_id]).all(), f"{element_info}, {indices} != {ref_indices_layer_0[elem_id]}" + assert ( + indices == ref_indices_layer_0[elem_id] + ).all(), f"{element_info}, {indices} != {ref_indices_layer_0[elem_id]}" # All indices of the second layer via it's material ID material_id = element_info.dpf_material_ids[1] # this is equivalent to the second layer @@ -333,7 +341,9 @@ def test_select_indices_all_element_types(dpf_server): # Offset indices for the second layer ref_2nd_layer = ref_indices_layer_0[elem_id] + max(ref_indices_layer_0[elem_id]) + 1 - assert (indices == ref_2nd_layer).all(), f"{element_info}, i{indices} != {ref_2nd_layer}" + assert ( + indices == ref_2nd_layer + ).all(), f"{element_info}, i{indices} != {ref_2nd_layer}" # Indices of the second layer and the top spot indices = get_selected_indices(element_info, layers=[1], spots=[Spot.TOP]) @@ -345,4 +355,6 @@ def test_select_indices_all_element_types(dpf_server): else: # Layered solids have only bottom and top. ref_2nd_layer_top = ref_2nd_layer[-num_indices:] - assert (indices == ref_2nd_layer_top).all(), f"{element_info}, {indices} != {ref_2nd_layer_top}" + assert ( + indices == ref_2nd_layer_top + ).all(), f"{element_info}, {indices} != {ref_2nd_layer_top}"