From 9875d436cfc1fe90b517a971fef523b4e7a84c33 Mon Sep 17 00:00:00 2001 From: Camille Bellot <80476446+cbellot000@users.noreply.github.com> Date: Fri, 3 Mar 2023 08:40:17 +0100 Subject: [PATCH] Create examples for pydpf-post and make small bug fixes (#300) * Add Mesh.node_ids * Fix if evaluations in MechanicalSimulation._build_selection * Return first principal component by default if None is asked (combination of all three is not possible yet) * Add animation on deformed mesh * Add Mesh._core_object and DataObject._core_object * Add typehinting to Simulation.time_freq_support * Add Simulation.set_ids * Add Simulation.release_streams() * Add DataFrame._core_object and Mesh._core_object * Add Mesh.__str__ * Rename DataObject to DataFrame * Add DataFrame.__str__ and remove DataFrame.to_pandas and to_numpy * Allow import DataFrame from post * Add display_width logic to the Dataframe string representation * Finish renaming to DataFrame * Add testing to DataFrame * Update Mesh.__str__ * Update DataFrame signature * Finish testing Mesh methods * Fix Mesh.available_named_selections docstring * Remove useless tests for Simulation.boundary_conditions and Simulation.loads as these properties are not implemented yet. * Fix Codacy * Add Index and MultiIndex * Add ResultsIndex * Update Index and MultiIndex APIs and update DataFrame API * Update DataFrame._update_str * Add DataFrame.select * Update DataFrame.plot() to accept selection arguments. * Add DataFrame.iselect * Update Index classes and DataFrame creation. Refactor components treatment and DataFrame creation in result APIs * Add component selection to DataFrame.select * Add component selection to DataFrame.iselect * Working DataFrame.__str__ * Working DataFrame.__str__ * first fixes * First fix for harmonic * examples and tests * stringify * Bump ansys-sphinx-theme from 0.8.2 to 0.9.5 (#299) Bumps [ansys-sphinx-theme](https://github.com/ansys/ansys-sphinx-theme) from 0.8.2 to 0.9.5. - [Release notes](https://github.com/ansys/ansys-sphinx-theme/releases) - [Commits](https://github.com/ansys/ansys-sphinx-theme/compare/v0.8.2...v0.9.5) --- updated-dependencies: - dependency-name: ansys-sphinx-theme dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump imageio from 2.25.0 to 2.26.0 (#298) Bumps [imageio](https://github.com/imageio/imageio) from 2.25.0 to 2.26.0. - [Release notes](https://github.com/imageio/imageio/releases) - [Changelog](https://github.com/imageio/imageio/blob/master/CHANGELOG.md) - [Commits](https://github.com/imageio/imageio/compare/v2.25.0...v2.26.0) --- updated-dependencies: - dependency-name: imageio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump pyvista from 0.36.1 to 0.38.3 (#294) Bumps [pyvista](https://github.com/pyvista/pyvista) from 0.36.1 to 0.38.3. - [Release notes](https://github.com/pyvista/pyvista/releases) - [Commits](https://github.com/pyvista/pyvista/compare/v0.36.1...v0.38.3) --- updated-dependencies: - dependency-name: pyvista dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump sphinx-autodoc-typehints from 1.21.7 to 1.22 (#276) Bumps [sphinx-autodoc-typehints](https://github.com/tox-dev/sphinx-autodoc-typehints) from 1.21.7 to 1.22. - [Release notes](https://github.com/tox-dev/sphinx-autodoc-typehints/releases) - [Changelog](https://github.com/tox-dev/sphinx-autodoc-typehints/blob/main/CHANGELOG.md) - [Commits](https://github.com/tox-dev/sphinx-autodoc-typehints/compare/1.21.7...1.22) --- updated-dependencies: - dependency-name: sphinx-autodoc-typehints dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fixes * new example * bugs * bugs * revert * Update src/ansys/dpf/post/examples/__init__.py * DataFrame.plot handles shell_layers * Fix bug for 01-static-simulation.py * Remove debug plot * Add truncation to print * Fix truncation detection logic * Fix scale_factor default value for DataFrame.animate * Remove rogue Stringify * animation * examples * new examples * Improve examples * Invariants examples --------- Signed-off-by: dependabot[bot] Co-authored-by: paul.profizi Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../01-static-simulation.py | 96 +++++++++++++ .../02-modal-simulation.py | 64 +++++++++ .../03-transient-simulation.py | 76 ++++++++++ .../04-harmonic-complex-results.py | 70 +++++++++ .../00-Different-analysis-types/README.txt | 5 + examples/00-Overview/README.txt | 5 - .../01-Different-analysis-types/README.txt | 5 - .../01-named-selections-modal-simulation.py | 68 +++++++++ .../02-modal-extract-sub-results.py | 60 ++++++++ .../03-explore-result-data-harmonic.py | 95 +++++++++++++ .../02-Detailed-Examples/04-invariants.py | 73 ++++++++++ .../00-overview_example.py | 0 .../01-get_data_from_static_simulation.py} | 0 .../01-introduction.py | 0 .../02-static-analysis.py} | 0 .../03-modal-analysis.py} | 0 .../04-harmonic-analysis.py} | 0 .../05-transient-analysis.py} | 0 .../06-complex_results.py} | 0 .../07-result_keywords.py} | 0 .../08-result_on_path_of_coordinates.py} | 0 .../02-basics.py => 04-Legacy/09-basics.py} | 0 pyproject.toml | 2 +- requirements/requirements_docs.txt | 6 +- requirements/requirements_test.txt | 2 +- src/ansys/dpf/post/__init__.py | 4 + src/ansys/dpf/post/common.py | 1 + src/ansys/dpf/post/dataframe.py | 134 +++++++++++++----- src/ansys/dpf/post/examples/__init__.py | 15 +- src/ansys/dpf/post/index.py | 22 ++- src/ansys/dpf/post/post_utility.py | 2 +- src/ansys/dpf/post/selection.py | 4 + src/ansys/dpf/post/simulation.py | 4 +- tests/conftest.py | 6 + tests/test_dataframe.py | 71 ++++++++-- tests/test_simulation.py | 10 +- 36 files changed, 818 insertions(+), 82 deletions(-) create mode 100644 examples/00-Different-analysis-types/01-static-simulation.py create mode 100644 examples/00-Different-analysis-types/02-modal-simulation.py create mode 100644 examples/00-Different-analysis-types/03-transient-simulation.py create mode 100644 examples/00-Different-analysis-types/04-harmonic-complex-results.py create mode 100644 examples/00-Different-analysis-types/README.txt delete mode 100644 examples/00-Overview/README.txt delete mode 100644 examples/01-Different-analysis-types/README.txt create mode 100644 examples/02-Detailed-Examples/01-named-selections-modal-simulation.py create mode 100644 examples/02-Detailed-Examples/02-modal-extract-sub-results.py create mode 100644 examples/02-Detailed-Examples/03-explore-result-data-harmonic.py create mode 100644 examples/02-Detailed-Examples/04-invariants.py rename examples/{00-Overview => 04-Legacy}/00-overview_example.py (100%) rename examples/{00-Overview/02-get_data_from_static_simulation.py => 04-Legacy/01-get_data_from_static_simulation.py} (100%) rename examples/{02-Detailed-Examples => 04-Legacy}/01-introduction.py (100%) rename examples/{01-Different-analysis-types/01-static-analysis.py => 04-Legacy/02-static-analysis.py} (100%) rename examples/{01-Different-analysis-types/02-modal-analysis.py => 04-Legacy/03-modal-analysis.py} (100%) rename examples/{01-Different-analysis-types/03-harmonic-analysis.py => 04-Legacy/04-harmonic-analysis.py} (100%) rename examples/{01-Different-analysis-types/04-transient-analysis.py => 04-Legacy/05-transient-analysis.py} (100%) rename examples/{02-Detailed-Examples/04-complex_results.py => 04-Legacy/06-complex_results.py} (100%) rename examples/{02-Detailed-Examples/03-result_keywords.py => 04-Legacy/07-result_keywords.py} (100%) rename examples/{02-Detailed-Examples/05-result_on_path_of_coordinates.py => 04-Legacy/08-result_on_path_of_coordinates.py} (100%) rename examples/{02-Detailed-Examples/02-basics.py => 04-Legacy/09-basics.py} (100%) diff --git a/examples/00-Different-analysis-types/01-static-simulation.py b/examples/00-Different-analysis-types/01-static-simulation.py new file mode 100644 index 000000000..35e196cd6 --- /dev/null +++ b/examples/00-Different-analysis-types/01-static-simulation.py @@ -0,0 +1,96 @@ +""" +.. _ref_static_example: + +Static Simulation +================= +In this script static simulation is processed to extract results like stress, displacement. +Selecting sub parts of the results by scoping on specific nodes or elements is also displayed here. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. # This example uses a supplied file that you can +# get by importing the DPF ``examples`` package. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.find_static_rst() +simulation = post.load_simulation(example_path) + +# for no autocompletion, this line is equivalent to: +simulation = post.StaticMechanicalSimulation(example_path) + +# print the simulation to get an overview of what's available +print(simulation) + +displacement = simulation.displacement() +print(displacement) + + +############################################################################### +# Select sub parts of displacement +# --------------------------------- + +# To get X displacements +x_displacement = displacement.select(comp="X") +print(x_displacement) + + +# equivalent to +x_displacement = simulation.displacement(components=["X"]) +print(x_displacement) + +# plot +x_displacement.plot() + +# extract displacement on specific nodes +nodes_displacement = displacement.select(node=[1, 10, 100]) +nodes_displacement.plot() + +# equivalent to: +nodes_displacement = simulation.displacement(node_ids=[1, 10, 100]) +print(nodes_displacement) + +############################################################################### +# Compute total displacement (norm) +# --------------------------------- +# Compute the norm of displacement on a selection of nodes + +nodes_displacement = simulation.displacement(node_ids=simulation.mesh.node_ids[10:], norm=True) +print(nodes_displacement) +nodes_displacement.plot() + + +############################################################################### +# Extract tensor stress, apply averaging, compute equivalent +# ---------------------------------------------------------- +# Extract raw elemental nodal stresses from the rst file +elem_nodal_stress = simulation.stress() +print(elem_nodal_stress) + +# Compute nodal stresses from the result file +nodal_stress = simulation.stress_nodal() +print(nodal_stress) + +# Compute elemental stresses from the result file +elemental_stress = simulation.stress_elemental() +print(elemental_stress) + +# extract elemental stresses on specific elements +elemental_stress = elemental_stress.select(element=[5, 6, 7]) +elemental_stress.plot() + +# Compute nodal eqv stresses from the result file +eqv_stress = simulation.stress_eqv_von_mises_nodal() +print(eqv_stress) +eqv_stress.plot() diff --git a/examples/00-Different-analysis-types/02-modal-simulation.py b/examples/00-Different-analysis-types/02-modal-simulation.py new file mode 100644 index 000000000..428d26cf4 --- /dev/null +++ b/examples/00-Different-analysis-types/02-modal-simulation.py @@ -0,0 +1,64 @@ +""" +.. _ref_modal_example: + +Modal Simulation +================ +Simple post processing operations like viewing the different mode shapes is displayed +in this example. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. # This example uses a supplied file that you can +# get by importing the DPF ``examples`` package. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.download_modal_frame() +simulation = post.load_simulation(example_path) + +# for no autocompletion, this line is equivalent to: +simulation = post.ModalMechanicalSimulation(example_path) + +############################################################################### +# View the frequency domain +# ------------------------- +# Printing the time freq support can help pick the right modes + +print(simulation.time_freq_support) + +# set_ids returns the unique identifiers for the modes +print(simulation.set_ids) + +############################################################################### +# Extract all mode shapes and view them one by one +# ------------------------------------------------ + +displacement_norm = simulation.displacement(all_sets=True, norm=True) +print(displacement_norm) + +for set_id in simulation.set_ids: + displacement_norm.plot(set_id=set_id) + + +############################################################################### +# Extract a selection of mode shapes and view them one by one +# ----------------------------------------------------------- + +modes = [1, 2, 3] + +displacement_norm = simulation.displacement(modes=modes, norm=True) +print(displacement_norm) + +for set_id in modes: + displacement_norm.plot(set_id=set_id) diff --git a/examples/00-Different-analysis-types/03-transient-simulation.py b/examples/00-Different-analysis-types/03-transient-simulation.py new file mode 100644 index 000000000..cb74c20b5 --- /dev/null +++ b/examples/00-Different-analysis-types/03-transient-simulation.py @@ -0,0 +1,76 @@ +""" +.. _ref_transient_example: + +Transient Simulation with Animation +=================================== +In this script transient simulation is processed to extract results like +stress, strain, displacement. +Extracting data for chosen time steps and animating is also displayed. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. # This example uses a supplied file that you can +# get by importing the DPF ``examples`` package. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.find_msup_transient() +simulation = post.load_simulation(example_path) + +# for no autocompletion, this line is equivalent to: +simulation = post.TransientMechanicalSimulation(example_path) + +# print the simulation to get an overview of what's available +print(simulation) + + +############################################################################### +# Extract displacement at all times or on a selection +# --------------------------------------------------- + +displacement = simulation.displacement(all_sets=True) +print(displacement) +displacement.animate(deform=True) + + +# equivalent to +x_displacement = simulation.displacement(all_sets=True, components=["X"]) +print(x_displacement) +displacement.animate(deform=True) + +# get the available time set ids in the simulation +print(simulation.set_ids) + +# extract displacement on given time steps or select the times steps from teh already evaluated +# displacements +displacement = simulation.displacement(set_ids=simulation.set_ids[5:]) +displacement = displacement.select(set_id=simulation.set_ids[5:]) +print(displacement) + +############################################################################### +# Extract strain at all times or on a selection +# --------------------------------------------------- +strain = simulation.elastic_strain_nodal(all_sets=True) +print(strain) + +strain = simulation.elastic_strain_nodal(set_ids=simulation.set_ids[10:]) +print(strain) + + +############################################################################### +# Animate strain eqv over all times +# --------------------------------- + +strain_eqv = simulation.elastic_strain_eqv_von_mises_nodal(all_sets=True) +strain_eqv.animate() diff --git a/examples/00-Different-analysis-types/04-harmonic-complex-results.py b/examples/00-Different-analysis-types/04-harmonic-complex-results.py new file mode 100644 index 000000000..c18f7cba9 --- /dev/null +++ b/examples/00-Different-analysis-types/04-harmonic-complex-results.py @@ -0,0 +1,70 @@ +""" +.. _ref_harmonic_example: + +Harmonic Simulation +=================== +In this script harmonic simulation is processed and complex results are used. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. # This example uses a supplied file that you can +# get by importing the DPF ``examples`` package. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.download_harmonic_clamped_pipe() +simulation = post.load_simulation(example_path) + +# for no autocompletion, this line is equivalent to: +simulation = post.HarmonicMechanicalSimulation(example_path) + +# print the simulation to get an overview of what's available + +print(simulation) + + +############################################################################### +# Extract displacement over a list of frequencies sets +# ---------------------------------------------------- +# Printing the time freq support can help pick the right frequencies + +print(simulation.time_freq_support) + +displacement = simulation.displacement(set_ids=[1, 2]) +print(displacement) + +subdisp = displacement.select(complex=0, set_id=1) +subdisp.plot() + +subdisp = displacement.select(complex=1, set_id=1) +subdisp.plot() + +subdisp = displacement.select(complex=0, set_id=2) +subdisp.plot() + +############################################################################### +# Extract stress eqv over a list of frequencies sets +# -------------------------------------------------- + +stress_eqv = simulation.stress_eqv_von_mises_nodal(set_ids=[1, 2]) +print(stress_eqv) + +sub_eqv = stress_eqv.select(complex=0, set_id=1) +sub_eqv.plot() + +sub_eqv = stress_eqv.select(complex=1, set_id=1) +sub_eqv.plot() + +sub_eqv = stress_eqv.select(complex=0, set_id=2) +sub_eqv.plot() diff --git a/examples/00-Different-analysis-types/README.txt b/examples/00-Different-analysis-types/README.txt new file mode 100644 index 000000000..81e431fcc --- /dev/null +++ b/examples/00-Different-analysis-types/README.txt @@ -0,0 +1,5 @@ +.. _ref_different_analysis: + +DPF-Post analysis types +~~~~~~~~~~~~~~~~~~~~~~~ +These examples show how to post process different simulation types using pydpf-post. diff --git a/examples/00-Overview/README.txt b/examples/00-Overview/README.txt deleted file mode 100644 index f5859fdf3..000000000 --- a/examples/00-Overview/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -.. _ref_overview: - -DPF-Post overview ------------------ -This example provides an overview of how you use DPF-Post. diff --git a/examples/01-Different-analysis-types/README.txt b/examples/01-Different-analysis-types/README.txt deleted file mode 100644 index ed51661a6..000000000 --- a/examples/01-Different-analysis-types/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -.. _ref_different_analysis: - -DPF-Post analysis types -~~~~~~~~~~~~~~~~~~~~~~~ -These examples show how you use DFP-Post analysis types. diff --git a/examples/02-Detailed-Examples/01-named-selections-modal-simulation.py b/examples/02-Detailed-Examples/01-named-selections-modal-simulation.py new file mode 100644 index 000000000..57fd2ed41 --- /dev/null +++ b/examples/02-Detailed-Examples/01-named-selections-modal-simulation.py @@ -0,0 +1,68 @@ +""" +.. _ref_ns_modal_example: + +Extract results on named selections - Modal Simulation +======================================================= +In this script static simulation is processed to extract results like stress, displacement. +Selecting sub parts of the results by scoping on specific nodes, elements is also displayed here. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. # This example uses a supplied file that you can +# get by importing the DPF ``examples`` package. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.download_modal_frame() +simulation = post.load_simulation(example_path) + +# for no autocompletion, this line is equivalent to: +simulation = post.ModalMechanicalSimulation(example_path) + +# print the simulation to get an overview of what's available +print(simulation) + +############################################################################### +# Get the available named selections +# ---------------------------------- + +print(simulation.named_selections) + +############################################################################### +# Extract displacements on named selections +# ----------------------------------------- + +bar1_tot_displacement = simulation.displacement(named_selections=['BAR_1'], norm=True) +print(bar1_tot_displacement) +bar1_tot_displacement.plot() + +bar2_tot_displacement = simulation.displacement(named_selections=['BAR_2'], norm=True) +print(bar2_tot_displacement) +bar2_tot_displacement.plot() + +# both +tot_displacement = simulation.displacement(named_selections=['BAR_1', 'BAR_2'], norm=True) +print(tot_displacement) +tot_displacement.plot() + +############################################################################### +# Extract stress and averaged stress on named selections +# ------------------------------------------------------ +eqv_stress = simulation.stress_eqv_von_mises_nodal(named_selections=['_FIXEDSU']) +print(eqv_stress) + +# without selection +elemental_stress = simulation.stress_elemental(named_selections=['BAR_1']) +print(elemental_stress) +elemental_stress.plot() diff --git a/examples/02-Detailed-Examples/02-modal-extract-sub-results.py b/examples/02-Detailed-Examples/02-modal-extract-sub-results.py new file mode 100644 index 000000000..610236026 --- /dev/null +++ b/examples/02-Detailed-Examples/02-modal-extract-sub-results.py @@ -0,0 +1,60 @@ +""" +.. _ref_modal_sub_results_example: + +Extract components of results - Modal Simulation +================================================ +In this script modal simulation is processed to extract sub components of results like elastic +stain, displacement. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. # This example uses a supplied file that you can +# get by importing the DPF ``examples`` package. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.download_all_kinds_of_complexity_modal() +simulation = post.load_simulation(example_path) + +# for no autocompletion, this line is equivalent to: +simulation = post.ModalMechanicalSimulation(example_path) + +displacement = simulation.displacement(modes=[1, 2]) +str(displacement) +# print the simulation to get an overview of what's available +print(simulation) + + +############################################################################### +# Extract X displacement over a list modes +# ---------------------------------------- +# Printing the time freq support can help pick the right modes + +print(simulation.time_freq_support) + +# To get X displacements on the first 2 modes +x_displacement = simulation.displacement(modes=[1, 2], components=["X"]) +# equivalent to +x_displacement = simulation.displacement(set_ids=[1, 2], components=["X"]) +print(x_displacement) + +x_displacement.plot(set_id=1) + +############################################################################### +# Extract XX and XY elastic strain over a list modes +# -------------------------------------------------- +# To get X displacements on the first 2 modes +XX_XY_elastic_strain = simulation.elastic_strain_nodal(modes=[3], components=["XX", "XY"]) +print(XX_XY_elastic_strain) + diff --git a/examples/02-Detailed-Examples/03-explore-result-data-harmonic.py b/examples/02-Detailed-Examples/03-explore-result-data-harmonic.py new file mode 100644 index 000000000..6329f934b --- /dev/null +++ b/examples/02-Detailed-Examples/03-explore-result-data-harmonic.py @@ -0,0 +1,95 @@ +""" +.. _ref_data_data_frame_example: + +Explore the data of a result with the DataFrame - Harmonic Simulation +===================================================================== +In this script a harmonic simulation is used as an example to show how to +interact with the post DataFrame: object returned by each result. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. # This example uses a supplied file that you can +# get by importing the DPF ``examples`` package. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.download_harmonic_clamped_pipe() +simulation = post.load_simulation(example_path) + +# for no autocompletion, this line is equivalent to: +simulation = post.HarmonicMechanicalSimulation(example_path) + +############################################################################### +# Extract displacement over all sets as an example +# ------------------------------------------------ + +displacement = simulation.displacement(all_sets=True) +print(displacement) +type(displacement) + +############################################################################### +# Loop over all columns and rows to understand the DataFrame +# and get the values for each index. + +# columns +for column in displacement.columns: + print(f"Column with label \"{column.name}\" and available values {column.values}.") + +# rows +for row in displacement.index: + print(f"Row with label \"{row.name}\" and available values {row.values}.") + + +############################################################################### +# Make selections in this DataFrame +# --------------------------------- +# All the labels and values displayed above can be used to select sub parts of the +# DataFrame. + +all_real_values = displacement.select(complex=0) +print(all_real_values) + +all_imaginary_values = displacement.select(complex=1) +print(all_imaginary_values) + +sets_values = displacement.select(set_id=[1, 2]) +print(sets_values) + +node_values = displacement.select(node=[3548]) +print(node_values) + +############################################################################### +# Make selections by index in this DataFrame +# ------------------------------------------ +# To select values by index for each label, the iselect method can be used. +# The index to ID order follows what is returned by the values on index method used above. + +sets_values = displacement.iselect(set_id=0) +print(sets_values) + +node_values = displacement.iselect(node=[0]) +print(node_values) + +############################################################################### +# Make multi selections in this DataFrame +# --------------------------------------- + +real_values_for_one_set_onde_node = displacement.select(node=[3548], set_id=1, complex=0) +print(real_values_for_one_set_onde_node) + +############################################################################### +# Make selections to plot the DataFrame +# ------------------------------------- + +displacement.plot(set_id=1, complex=0) diff --git a/examples/02-Detailed-Examples/04-invariants.py b/examples/02-Detailed-Examples/04-invariants.py new file mode 100644 index 000000000..622a24e9b --- /dev/null +++ b/examples/02-Detailed-Examples/04-invariants.py @@ -0,0 +1,73 @@ +""" +.. _ref_invariants_example: + +Extract stress/strain invariants - Static Simulation +===================================================================== +In this script a static simulation is used as an example to show how to +extract tensor's invariants. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. # This example uses a supplied file that you can +# get by importing the DPF ``examples`` package. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.download_crankshaft() +simulation = post.load_simulation(example_path) + +# for no autocompletion, this line is equivalent to: +simulation = post.StaticMechanicalSimulation(example_path) + +# print the simulation to get an overview of what's available +print(simulation) + +############################################################################### +# Extract elemental nodal stress and strain +# ----------------------------------------- + +stress = simulation.stress(all_sets=True) +print(stress) + +strain = simulation.elastic_strain(all_sets=True) +print(strain) + +############################################################################### +# Compute principal invariant averaged and unaveraged on stress and strain +# ------------------------------------------------------------------------ + +princ_stress_1 = simulation.stress_principal(components=[1]) +print(princ_stress_1) + +nodal_princ_stress_2 = simulation.stress_principal_nodal(components=[2]) +print(nodal_princ_stress_2) +nodal_princ_stress_2.plot() + +nodal_princ_strain_2 = simulation.elastic_strain_principal_nodal(components=[2]) +print(nodal_princ_strain_2) +nodal_princ_strain_2.plot() + + +############################################################################### +# Compute Von Mises eqv averaged and unaveraged on stress and strain +# ------------------------------------------------------------------------ + +stress_eqv = simulation.stress_eqv_von_mises(set_ids=[1]) +print(stress_eqv) + +nodal_stress_eqv = simulation.stress_eqv_von_mises_nodal(set_ids=[1]) +nodal_stress_eqv.plot() + +nodal_strain_eqv = simulation.elastic_strain_eqv_von_mises_nodal(set_ids=[1]) +nodal_strain_eqv.plot() diff --git a/examples/00-Overview/00-overview_example.py b/examples/04-Legacy/00-overview_example.py similarity index 100% rename from examples/00-Overview/00-overview_example.py rename to examples/04-Legacy/00-overview_example.py diff --git a/examples/00-Overview/02-get_data_from_static_simulation.py b/examples/04-Legacy/01-get_data_from_static_simulation.py similarity index 100% rename from examples/00-Overview/02-get_data_from_static_simulation.py rename to examples/04-Legacy/01-get_data_from_static_simulation.py diff --git a/examples/02-Detailed-Examples/01-introduction.py b/examples/04-Legacy/01-introduction.py similarity index 100% rename from examples/02-Detailed-Examples/01-introduction.py rename to examples/04-Legacy/01-introduction.py diff --git a/examples/01-Different-analysis-types/01-static-analysis.py b/examples/04-Legacy/02-static-analysis.py similarity index 100% rename from examples/01-Different-analysis-types/01-static-analysis.py rename to examples/04-Legacy/02-static-analysis.py diff --git a/examples/01-Different-analysis-types/02-modal-analysis.py b/examples/04-Legacy/03-modal-analysis.py similarity index 100% rename from examples/01-Different-analysis-types/02-modal-analysis.py rename to examples/04-Legacy/03-modal-analysis.py diff --git a/examples/01-Different-analysis-types/03-harmonic-analysis.py b/examples/04-Legacy/04-harmonic-analysis.py similarity index 100% rename from examples/01-Different-analysis-types/03-harmonic-analysis.py rename to examples/04-Legacy/04-harmonic-analysis.py diff --git a/examples/01-Different-analysis-types/04-transient-analysis.py b/examples/04-Legacy/05-transient-analysis.py similarity index 100% rename from examples/01-Different-analysis-types/04-transient-analysis.py rename to examples/04-Legacy/05-transient-analysis.py diff --git a/examples/02-Detailed-Examples/04-complex_results.py b/examples/04-Legacy/06-complex_results.py similarity index 100% rename from examples/02-Detailed-Examples/04-complex_results.py rename to examples/04-Legacy/06-complex_results.py diff --git a/examples/02-Detailed-Examples/03-result_keywords.py b/examples/04-Legacy/07-result_keywords.py similarity index 100% rename from examples/02-Detailed-Examples/03-result_keywords.py rename to examples/04-Legacy/07-result_keywords.py diff --git a/examples/02-Detailed-Examples/05-result_on_path_of_coordinates.py b/examples/04-Legacy/08-result_on_path_of_coordinates.py similarity index 100% rename from examples/02-Detailed-Examples/05-result_on_path_of_coordinates.py rename to examples/04-Legacy/08-result_on_path_of_coordinates.py diff --git a/examples/02-Detailed-Examples/02-basics.py b/examples/04-Legacy/09-basics.py similarity index 100% rename from examples/02-Detailed-Examples/02-basics.py rename to examples/04-Legacy/09-basics.py diff --git a/pyproject.toml b/pyproject.toml index 9c6e7d202..af3598b3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ maintainers = [ {name = "PyAnsys developers", email = "pyansys.support@ansys.com"}, ] dependencies = [ - "ansys.dpf.core", + "ansys.dpf.core @ git+https://git@github.com/pyansys/pydpf-core.git", "scooby", ] classifiers = [ diff --git a/requirements/requirements_docs.txt b/requirements/requirements_docs.txt index 260487118..659aa9f0e 100644 --- a/requirements/requirements_docs.txt +++ b/requirements/requirements_docs.txt @@ -1,5 +1,5 @@ pypandoc==1.10 -imageio==2.25.0 +imageio==2.26.0 numpydoc==1.4.0 imageio-ffmpeg==0.4.7 Sphinx==5.3.0 @@ -10,5 +10,5 @@ pytest-sphinx==0.5.0 sphinx-notfound-page==0.8.3 sphinx-copybutton==0.5.0 sphinx-gallery==0.11.1 -ansys_sphinx_theme==0.8.2 -sphinx-autodoc-typehints==1.21.7 +ansys_sphinx_theme==0.9.5 +sphinx-autodoc-typehints==1.22 diff --git a/requirements/requirements_test.txt b/requirements/requirements_test.txt index 68c6362e2..3e266e049 100644 --- a/requirements/requirements_test.txt +++ b/requirements/requirements_test.txt @@ -3,4 +3,4 @@ pandas==1.3.5 pytest-cov==4.0.0 pytest-rerunfailures==11.0 pytest==7.2.1 -pyvista==0.36.1 \ No newline at end of file +pyvista==0.38.3 \ No newline at end of file diff --git a/src/ansys/dpf/post/__init__.py b/src/ansys/dpf/post/__init__.py index 4a4ad3284..a1c3ed2a3 100644 --- a/src/ansys/dpf/post/__init__.py +++ b/src/ansys/dpf/post/__init__.py @@ -31,6 +31,10 @@ load_solution, print_available_keywords, ) +from ansys.dpf.post.harmonic_mechanical_simulation import HarmonicMechanicalSimulation # noqa: F401 +from ansys.dpf.post.transient_mechanical_simulation import TransientMechanicalSimulation # noqa: F401 +from ansys.dpf.post.static_mechanical_simulation import StaticMechanicalSimulation # noqa: F401 +from ansys.dpf.post.modal_mechanical_simulation import ModalMechanicalSimulation # noqa: F401 # this must be after some ansys.dpf.post import __version__ = importlib_metadata.version("ansys-dpf-post") diff --git a/src/ansys/dpf/post/common.py b/src/ansys/dpf/post/common.py index f5f0df326..5178ea27a 100644 --- a/src/ansys/dpf/post/common.py +++ b/src/ansys/dpf/post/common.py @@ -112,6 +112,7 @@ class _AnalysisType: modal = "modal" harmonic = "harmonic" transient = "transient" + msup = "MSUP" class _PhysicsType: diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index 75dd665ef..3bb87470c 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -7,19 +7,18 @@ import warnings import ansys.dpf.core as dpf +from ansys.dpf.core.common import shell_layers from ansys.dpf.core.dpf_array import DPFArray from ansys.dpf.post import locations from ansys.dpf.post.index import ( CompIndex, - FrequencyIndex, Index, LabelIndex, MeshIndex, MultiIndex, ResultsIndex, SetIndex, - TimeIndex, ) display_width = 80 @@ -116,7 +115,7 @@ def mesh_index(self) -> Union[MeshIndex, None]: @property def labels(self) -> List[str]: - """Returns a list of the names of available ResultsIndex indexes.""" + """Returns a list of the names of available LabelIndex indexes.""" names = [index.name for index in self.index if isinstance(index, LabelIndex)] names.extend( [index.name for index in self.columns if isinstance(index, LabelIndex)] @@ -182,11 +181,18 @@ def select(self, **kwargs) -> DataFrame: fc.labels = self._fc.labels for i, field in enumerate(self._fc): label_space = self._fc.get_label_space(i) - for key in label_space.keys(): - if not isinstance(kwargs[key], list): - kwargs[key] = [kwargs[key]] - if label_space[key] in kwargs[key]: - fc.add_field(label_space=label_space, field=field) + for fc_key in label_space.keys(): + if fc_key in kwargs.keys() or ( + fc_key == "time" and "set_id" in kwargs.keys() + ): + key = fc_key + if fc_key == "time" and "set_id" in kwargs.keys(): + key = "set_id" + if not isinstance(kwargs[key], list): + kwargs[key] = [kwargs[key]] + if label_space[fc_key] not in kwargs[key]: + continue + fc.add_field(label_space=label_space, field=field) input_fc = fc # # Treat selection on components @@ -291,7 +297,7 @@ def iselect(self, **kwargs) -> DataFrame: value or a list of values. For example, if 'time' is an available class:`Index ` of the class:`DataFrame ` `df`, then you can select the first - `time` value by using `df.select(time=0)`. + `time` value by using `df.select(set_id=0)`. One can get the list of available axes using :func:`DataFrame.axes `. @@ -350,7 +356,7 @@ def _update_str(self, width: int, max_colwidth: int): """ lines = [] empty = " " * max_colwidth - truncated = "...".rjust(max_colwidth) + truncated_str = "...".rjust(max_colwidth) max_n_col = width // max_colwidth max_n_rows = display_max_lines # Create lines with row labels and values @@ -369,27 +375,31 @@ def _update_str(self, width: int, max_colwidth: int): ) lines.append(row_headers) entity_ids = [] - # Create combinations for rows - num_mesh_entities_to_ask = max_n_rows + truncated = False + num_mesh_entities_to_ask = self._fc[0].size + if num_mesh_entities_to_ask > max_n_rows: + num_mesh_entities_to_ask = max_n_rows + truncated = True lists = [] + # Create combinations for rows entity_ids = None for index in self.index: if isinstance(index, MeshIndex): if index._values is not None: values = index.values - entity_ids = values else: values = self._first_n_ids_first_field(num_mesh_entities_to_ask) + entity_ids = values elif isinstance(index, CompIndex): values = index.values else: values = index.values lists.append(values) - row_combinations = [p for p in itertools.product(*lists)] + row_combinations = [p for p in itertools.product(*lists)][:max_n_rows] # Add row headers for the first combinations (max_n_rows) previous_combination = [None] * len(lists) - for combination in row_combinations[:max_n_rows]: + for combination in row_combinations: line = "".join( [ str(combination[i]).rjust(max_colwidth) @@ -402,12 +412,10 @@ def _update_str(self, width: int, max_colwidth: int): lines.append(line) # Create combinations for columns - num_mesh_entities_to_ask = max_n_rows entity_ids = None lists = [] - time_position = 1 - complex_position = None - comp_values = None + label_positions_in_combinations = {} + comp_values = [1] for position, index in enumerate(self.columns): if isinstance(index, MeshIndex): if index._values is not None: @@ -415,18 +423,19 @@ def _update_str(self, width: int, max_colwidth: int): entity_ids = values else: values = self._first_n_ids_first_field(num_mesh_entities_to_ask) + entity_ids = values elif isinstance(index, CompIndex): values = index.values comp_values = values - elif isinstance(index, (FrequencyIndex, TimeIndex)): - values = index.values - time_position = position else: values = index.values - if index.name == "complex": - complex_position = position + label_positions_in_combinations[index.name] = position lists.append(values) combinations = [p for p in itertools.product(*lists)] + truncated_columns = False + if len(combinations) > n_max_value_col: + truncated_columns = True + combinations = combinations[:n_max_value_col] def flatten(arr): new_arr = [] @@ -453,8 +462,6 @@ def treat_elemental_nodal(treat_lines, pos, n_comp, n_ent, n_lines): treat_lines = treat_lines[:pos] + new_elem_headers + treat_lines[pos:] return treat_lines[:n_lines] - # Query data by selecting a sub-dataframe - # Add text for the first n_max_value_col columns previous_combination = [None] * len(lists) for i_c, combination in enumerate(combinations[:n_max_value_col]): @@ -467,9 +474,12 @@ def treat_elemental_nodal(treat_lines, pos, n_comp, n_ent, n_lines): to_append.append(empty) # Get data in the FieldsContainer for those positions # Create label_space from combination - label_space = {"time": combination[time_position]} - if complex_position is not None: - label_space["complex"] = combination[complex_position] + label_space = {} + for label_name in self.labels: + value = combination[label_positions_in_combinations[label_name]] + if label_name == "set_id": + label_name = "time" + label_space[label_name] = value fields = self._fc.get_fields(label_space=label_space) values = [] if entity_ids is None: @@ -501,10 +511,16 @@ def treat_elemental_nodal(treat_lines, pos, n_comp, n_ent, n_lines): num_entities, current_number_lines, ) - position += num_entities * num_components array_values.append(values_list) + if ( + position + num_entities * num_components + >= current_number_lines + len(column_headers) + 1 + ): + if k < num_mesh_entities_to_ask: + truncated = True if position >= current_number_lines: break + position += num_entities * num_components if array_values: array_values = flatten(array_values) values.extend(array_values) @@ -536,10 +552,16 @@ def treat_elemental_nodal(treat_lines, pos, n_comp, n_ent, n_lines): num_entities, current_number_lines, ) - position += num_entities * num_components - array_values.append(array_values) + array_values.append(values_list) + if ( + position + num_entities * num_components + >= current_number_lines + len(column_headers) + 1 + ): + if k < num_mesh_entities_to_ask: + truncated = True if position >= current_number_lines: break + position += num_entities * num_components if array_values: array_values = flatten(array_values) values.extend(array_values) @@ -562,6 +584,12 @@ def treat_elemental_nodal(treat_lines, pos, n_comp, n_ent, n_lines): for i in range(len(lines)): lines[i] = lines[i] + to_append[i] + if truncated_columns: + for i in range(len(lines)): + lines[i] = lines[i] + truncated_str + + if truncated: + lines.append(truncated_str) txt = "\n" + "".join([line + "\n" for line in lines]) self._str = txt @@ -573,11 +601,13 @@ def __repr__(self): """Representation of the DataFrame.""" return f"DataFrame" - def plot(self, **kwargs): + def plot(self, shell_layer=shell_layers.top, **kwargs): """Plot the result. Parameters ---------- + shell_layer: + Shell layer to show if multi-layered shell data present. Defaults to top. **kwargs: This function accepts as argument any of the Index names available associated with a single value. @@ -592,6 +622,8 @@ def plot(self, **kwargs): The interactive plotter object used for plotting. """ + from ansys.dpf.core.plotter import DpfPlotter as Plotter + if kwargs != {}: # Check for invalid arguments axes = self.axes @@ -618,14 +650,42 @@ def plot(self, **kwargs): else: label_space = fc.get_label_space(0) label_space = label_space - field = fc.get_field(label_space_or_index=label_space) - return field.plot(text=str(label_space), **kwargs) + # if "elshape" in self._fc.labels: + # merge_solids_shell_op = dpf.operators.logic.solid_shell_fields(fc) + # fc = merge_solids_shell_op.eval() + + for field in fc: + # Treat multi-layer field + shell_layer_check = field.shell_layers + if shell_layer_check in [ + shell_layers.topbottom, + shell_layers.topbottommid, + ]: + changeOp = dpf.Operator("change_shellLayers") + changeOp.inputs.fields_container.connect(fc) + sl = shell_layers.top + if shell_layer is not None: + if not isinstance(shell_layer, shell_layers): + raise TypeError( + "shell_layer attribute must be a core.shell_layers instance." + ) + sl = shell_layer + changeOp.inputs.e_shell_layer.connect(sl.value) # top layers taken + fc = changeOp.get_output(0, dpf.types.fields_container) + break + + fields = fc.get_fields(label_space=label_space) + plotter = Plotter(**kwargs) + for field in fields: + plotter.add_field(field=field, **kwargs) + # field.plot(text="debug") + return plotter.show_figure(text=str(label_space), **kwargs) def animate( self, save_as: Union[PathLike, None] = None, deform: bool = False, - scale_factor: Union[List[float], float, None] = None, + scale_factor: Union[List[float], float] = 1.0, **kwargs, ): """Animate the result. @@ -660,4 +720,4 @@ def animate( ) return self._fc.animate( save_as=save_as, deform_by=deform_by, scale_factor=scale_factor, **kwargs - ) + ) \ No newline at end of file diff --git a/src/ansys/dpf/post/examples/__init__.py b/src/ansys/dpf/post/examples/__init__.py index 530963530..486957807 100644 --- a/src/ansys/dpf/post/examples/__init__.py +++ b/src/ansys/dpf/post/examples/__init__.py @@ -2,20 +2,7 @@ import os # alias files used by the core -from ansys.dpf.core.examples import ( - complex_rst, - download_all_kinds_of_complexity, - download_all_kinds_of_complexity_modal, - download_transient_result, - electric_therm, - find_multishells_rst, - msup_transient, - multishells_rst, - simple_bar, - static_rst, - steady_therm, - transient_therm, -) +from ansys.dpf.core.examples import * # noqa: F401 # must copy files over when using docker # running_docker = os.environ.get('DPF_DOCKER', False) diff --git a/src/ansys/dpf/post/index.py b/src/ansys/dpf/post/index.py index 68d939a32..f9cf8f05c 100644 --- a/src/ansys/dpf/post/index.py +++ b/src/ansys/dpf/post/index.py @@ -164,6 +164,10 @@ def __init__( """Initiate this class.""" super().__init__(name=name, values=values, scoping=scoping) + def __repr__(self): + """Representation of the Index.""" + return f"LabelIndex" + class TimeIndex(Index): """Index class specific to time.""" @@ -176,6 +180,10 @@ def __init__( """Initiate this class.""" super().__init__(name="time", values=values, scoping=scoping) + def __repr__(self): + """Representation of the Index.""" + return f"TimeIndex" + class ModeIndex(Index): """Index class specific to modes.""" @@ -188,6 +196,10 @@ def __init__( """Initiate this class.""" super().__init__(name="mode", values=values, scoping=scoping) + def __repr__(self): + """Representation of the Index.""" + return f"ModeIndex" + class FrequencyIndex(Index): """Index class specific to frequency.""" @@ -200,8 +212,12 @@ def __init__( """Initiate this class.""" super().__init__(name="frequency", values=values, scoping=scoping) + def __repr__(self): + """Representation of the Index.""" + return f"FrequencyIndex" + -class SetIndex(Index): +class SetIndex(LabelIndex): """Index class specific to set_id.""" def __init__( @@ -212,6 +228,10 @@ def __init__( """Initiate this class.""" super().__init__(name="set_id", values=values, scoping=scoping) + def __repr__(self): + """Representation of the Index.""" + return f"SetIndex" + class CompIndex(Index): """Index class specific to components.""" diff --git a/src/ansys/dpf/post/post_utility.py b/src/ansys/dpf/post/post_utility.py index 9e9e7cacc..2ce59c870 100644 --- a/src/ansys/dpf/post/post_utility.py +++ b/src/ansys/dpf/post/post_utility.py @@ -198,7 +198,7 @@ def load_simulation( simulation_type = AvailableSimulationTypes.static_mechanical elif analysis_type == _AnalysisType.modal: simulation_type = AvailableSimulationTypes.modal_mechanical - elif analysis_type == _AnalysisType.harmonic: + elif analysis_type == _AnalysisType.harmonic or analysis_type == _AnalysisType.msup: simulation_type = AvailableSimulationTypes.harmonic_mechanical elif analysis_type == _AnalysisType.transient: simulation_type = AvailableSimulationTypes.transient_mechanical diff --git a/src/ansys/dpf/post/selection.py b/src/ansys/dpf/post/selection.py index 2763139b3..2cf05a493 100644 --- a/src/ansys/dpf/post/selection.py +++ b/src/ansys/dpf/post/selection.py @@ -289,6 +289,10 @@ def select_nodes(self, nodes: Union[List[int], Scoping]) -> None: if isinstance(nodes, Scoping): scoping = nodes else: + from ansys.dpf.core.dpf_array import DPFArray + + if isinstance(nodes, DPFArray): + nodes = nodes.tolist() scoping = Scoping(location=locations.nodal, ids=nodes, server=self._server) self.select_with_scoping(scoping) diff --git a/src/ansys/dpf/post/simulation.py b/src/ansys/dpf/post/simulation.py index 63a65810d..1ca29eef3 100644 --- a/src/ansys/dpf/post/simulation.py +++ b/src/ansys/dpf/post/simulation.py @@ -507,7 +507,7 @@ class MechanicalSimulation(Simulation, ABC): This class provides common methods and properties for all mechanical type simulations. """ - def __init__(self, result_file: PathLike): + def __init__(self, result_file: Union[PathLike,str]): """Instantiate a mechanical type simulation.""" model = dpf.Model(result_file) data_sources = model.metadata.data_sources @@ -544,7 +544,7 @@ def _build_selection( selection = Selection(server=self._model._server) # Create the SpatialSelection if named_selections: - selection.select_named_selection(named_selection=named_selections) + selection.select_named_selection(named_selection=named_selections, location=location) elif element_ids is not None: if location == locations.nodal: selection.select_nodes_of_elements(elements=element_ids, mesh=self.mesh) diff --git a/tests/conftest.py b/tests/conftest.py index fd6c8eeeb..78093d83e 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -140,6 +140,12 @@ def rth_electric(): return examples.electric_therm +@pytest.fixture() +def modal_frame(): + """Resolve the path of the "rth/rth_electric.rth" result file.""" + return examples.download_modal_frame() + + @pytest.fixture(scope="session", autouse=True) def cleanup(request): """Cleanup a testing directory once we are finished.""" diff --git a/tests/test_dataframe.py b/tests/test_dataframe.py index 54c075383..9fd0df12d 100644 --- a/tests/test_dataframe.py +++ b/tests/test_dataframe.py @@ -20,6 +20,12 @@ def df(static_rst): return simulation.displacement() +@fixture +def elastic_strain_df(static_rst): + simulation = StaticMechanicalSimulation(static_rst) + return simulation.elastic_strain_nodal() + + def test_dataframe_core_object(df): assert isinstance(df._core_object, core.FieldsContainer) assert len(df._core_object) == 1 @@ -115,7 +121,7 @@ def test_dataframe_repr(df): ref = ( "DataFrame>, " "Index>]>, columns=MultiIndex<[ResultIndex<['U']>, " - "Index>]>>" + "SetIndex]>>" ) assert repr(df) == ref @@ -125,20 +131,33 @@ def test_dataframe_str(transient_rst): df = simulation.displacement(all_sets=True) print(df) ref = """ - results U - set_id 1 2 3 4 5 6 - node comp - 525 X 0.00e+00 4.85e-05 2.30e-04 6.51e-04 1.48e-03 2.93e-03 - Y 0.00e+00 2.87e-04 1.14e-03 2.54e-03 4.41e-03 6.59e-03 - Z 0.00e+00 -1.26e-10 -4.34e-10 -8.29e-10 -1.15e-09 -1.39e-09 - 534 X 0.00e+00 6.55e-06 1.05e-04 5.30e-04 1.67e-03 4.02e-03 - Y 0.00e+00 6.27e-04 2.51e-03 5.62e-03 9.86e-03 1.50e-02 - Z 0.00e+00 -3.20e-10 -1.10e-09 -2.13e-09 -2.94e-09 -3.57e-09 + results U ... + set_id 1 2 3 4 5 6 ... + node comp ... + 525 X 0.00e+00 4.85e-05 2.30e-04 6.51e-04 1.48e-03 2.93e-03 ... + Y 0.00e+00 2.87e-04 1.14e-03 2.54e-03 4.41e-03 6.59e-03 ... + Z 0.00e+00 -1.26e-10 -4.34e-10 -8.29e-10 -1.15e-09 -1.39e-09 ... + 534 X 0.00e+00 6.55e-06 1.05e-04 5.30e-04 1.67e-03 4.02e-03 ... + Y 0.00e+00 6.27e-04 2.51e-03 5.62e-03 9.86e-03 1.50e-02 ... + Z 0.00e+00 -3.20e-10 -1.10e-09 -2.13e-09 -2.94e-09 -3.57e-09 ... + ... """ # noqa: W291 assert str(df) == ref + df2 = df.select(node=525) + print(df2) + ref = """ + results U ... + set_id 1 2 3 4 5 6 ... + node comp ... + 525 X 0.00e+00 4.85e-05 2.30e-04 6.51e-04 1.48e-03 2.93e-03 ... + Y 0.00e+00 2.87e-04 1.14e-03 2.54e-03 4.41e-03 6.59e-03 ... + Z 0.00e+00 -1.26e-10 -4.34e-10 -8.29e-10 -1.15e-09 -1.39e-09 ... +""" # noqa: W291 + assert str(df2) == ref + df = simulation.stress() print(df) - print(df._fc[0].get_entity_data_by_id(391)) + print(df._fc[0].get_entity_data_by_id(391)) ref = """ results S set_id 35 @@ -149,6 +168,7 @@ def test_dataframe_str(transient_rst): XY (1) -4.89e+06 YZ (1) 1.43e+07 XZ (1) 1.65e+07 + ... """ # noqa: W291 assert str(df) == ref df2 = df.select(comp="YY") @@ -163,5 +183,34 @@ def test_dataframe_str(transient_rst): 391 YY (4) -2.72e+07 391 YY (5) 2.83e+07 391 YY (6) 5.26e+06 + ... """ # noqa: W291 assert str(df2) == ref + + +def test_dataframe_str_comp(df): + # 3D str + stri = str(df) + expected_strs = ["X", "Y", "Z", "results", "U", "node"] + for expected_str in expected_strs: + assert expected_str in stri + # 1D str + stri = str(df.select(comp="X")) + expected_strs = ["X", "results", "U", "node"] + for expected_str in expected_strs: + assert expected_str in stri + assert "Y" not in stri + + +def test_dataframe_str_tensor(elastic_strain_df): + # 3D str + stri = str(elastic_strain_df) + expected_strs = ["XX", "XY", "XZ", "results", "EPEL", "node"] + for expected_str in expected_strs: + assert expected_str in stri + # 1D str + stri = str(elastic_strain_df.select(comp=["XX", "XY"])) + expected_strs = ["XX", "XY", "results", "EPEL", "node"] + for expected_str in expected_strs: + assert expected_str in stri + assert "ZZ" not in stri diff --git a/tests/test_simulation.py b/tests/test_simulation.py index 7f386e1ae..c31a4593c 100644 --- a/tests/test_simulation.py +++ b/tests/test_simulation.py @@ -595,7 +595,7 @@ def test_displacement(self, transient_simulation): assert result._fc.get_time_scoping().ids == [2] field = result._fc[0] assert field.component_count == 1 - assert field.data.shape == (53,) + assert field.data.shape == (393,) def test_velocity(self, transient_simulation): result = transient_simulation.velocity( @@ -1922,3 +1922,11 @@ def test_elastic_strain_eqv_von_mises_elemental(self, harmonic_simulation): field_ref = average_op.outputs.fields_container()[0] assert field.component_count == 1 assert np.allclose(field.data, field_ref.data) + + +def test_elemental_ns_on_nodal_result(modal_frame): + simulation = post.load_simulation(modal_frame) + assert "BAR_1" in simulation.named_selections + disp = simulation.displacement(named_selections=["BAR_1"]) + assert disp.index[0].name == "node" + assert len(disp.index[0].values) == 1370