From 064ed1d81ec5a8fe14f135015be6d298489cb6ed Mon Sep 17 00:00:00 2001 From: luisaFelixSalles Date: Fri, 11 Oct 2024 11:34:59 +0200 Subject: [PATCH 1/5] new ls dyna example --- examples/14-lsdyna/01-lsdyna_beam.py | 107 +++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 examples/14-lsdyna/01-lsdyna_beam.py diff --git a/examples/14-lsdyna/01-lsdyna_beam.py b/examples/14-lsdyna/01-lsdyna_beam.py new file mode 100644 index 0000000000..0cd04f890b --- /dev/null +++ b/examples/14-lsdyna/01-lsdyna_beam.py @@ -0,0 +1,107 @@ +# Copyright (C) 2020 - 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. + +""" +.. _lsdyna_operators: + +Results extraction and analysis from LS-DYNA sources +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This example provides an overview of the LS-DYNA beam results manipulations. + +.. note:: + This example requires DPF 6.1 (ansys-dpf-server-2023-2-pre0) or above. + For more information, see :ref:`ref_compatibility`. + +""" + +import matplotlib.pyplot as plt +from ansys.dpf import core as dpf +from ansys.dpf.core import examples +from ansys.dpf.core import operators as ops + +############################################################################### +# d3plot file results extraction +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Create the model and print its contents. This LS-DYNA d3plot file contains +# several individual results, each at different times. The d3plot file does not +# contain information related to Units. In this case, as the simulation was run +# through Mechanical, a file.actunits file is produced. If this file is +# supplemented in the data_sources, the units will be correctly fetched for all +# results in the file as well as for the mesh. + +d3plot = examples.download_d3plot_beam() +ds = dpf.DataSources() +ds.set_result_file_path(d3plot[0], "d3plot") +ds.add_file_path(d3plot[3], "actunits") +my_model = dpf.Model(ds) +# print(model) + +############################################################################### +# The model has solid (3D) elements and beam (1D) elements. Some of the results +# only apply to one type of elements (such as the stress tensor for solids, or +# the axial force for beams, for example). + +# By splitting the mesh by element shape we see that the ball is made by the solid +# 3D elements and the plate by the beam 1D elements + +my_meshed_region = my_model.metadata.meshed_region + +my_meshes = ops.mesh.split_mesh(mesh=my_meshed_region, property="elemental").eval() +# print(my_meshes) +############################################################################### +# Ball + +# print(my_meshes[0]) +# my_meshes[0].plot() + +############################################################################### +# Plate + +# print(my_meshes[1]) +# my_meshes[1].plot() + + +############################################################################### +# We can split the mesh scoping so it's easier to chose from which body we are +# analysing the results + +my_meshes_scopings = ops.scoping.split_on_property_type(mesh=my_meshed_region).eval() +my_time_scoping = my_model.metadata.time_freq_support.time_frequencies +# For example the ball velocity +v = my_model.results.velocity(time_scoping=my_time_scoping).eval() + + +# Forces + +############################################################################### +# compare results in different time steps +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sforces = my_model.results.beam_s_shear_force(mesh_scoping=my_meshes_scopings[1]).eval() +Sforces2 = my_model.results.beam_s_shear_force(mesh_scoping=my_meshes_scopings[0]).eval() + +comparison_plot = dpf.plotter.DpfPlotter +comparison_plot.add_field(field=Sforces, meshed_region=my_meshes[1]) +comparison_plot.add_field(field=Sforces2, meshed_region=my_meshes[0]) + +comparison_plot.show_figure() From 8c3a06cdb446a24941cc7119170775894fa4d1c9 Mon Sep 17 00:00:00 2001 From: luisaFelixSalles Date: Fri, 11 Oct 2024 15:03:26 +0200 Subject: [PATCH 2/5] new ls dyna example --- examples/14-lsdyna/01-lsdyna_beam.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/examples/14-lsdyna/01-lsdyna_beam.py b/examples/14-lsdyna/01-lsdyna_beam.py index 0cd04f890b..041d01025d 100644 --- a/examples/14-lsdyna/01-lsdyna_beam.py +++ b/examples/14-lsdyna/01-lsdyna_beam.py @@ -66,7 +66,11 @@ my_meshed_region = my_model.metadata.meshed_region -my_meshes = ops.mesh.split_mesh(mesh=my_meshed_region, property="elemental").eval() +my_meshes = ops.mesh.split_mesh( + mesh=my_meshed_region, property=dpf.common.elemental_properties.element_shape +).eval() +my_ball_mesh = my_meshes.get_mesh(label_space_or_index={"body": 1, "elshape": 1}) +my_plate_mesh = my_meshes.get_mesh(label_space_or_index={"body": 2, "elshape": 2}) # print(my_meshes) ############################################################################### # Ball @@ -86,6 +90,9 @@ # analysing the results my_meshes_scopings = ops.scoping.split_on_property_type(mesh=my_meshed_region).eval() +my_ball_scoping = my_meshes_scopings.get_scoping(label_space_or_index={"elshape": 1}) +my_plate_scoping = my_meshes_scopings.get_scoping(label_space_or_index={"elshape": 2}) + my_time_scoping = my_model.metadata.time_freq_support.time_frequencies # For example the ball velocity v = my_model.results.velocity(time_scoping=my_time_scoping).eval() @@ -97,11 +104,15 @@ # compare results in different time steps # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Sforces = my_model.results.beam_s_shear_force(mesh_scoping=my_meshes_scopings[1]).eval() -Sforces2 = my_model.results.beam_s_shear_force(mesh_scoping=my_meshes_scopings[0]).eval() +Sballforces = my_model.results.beam_s_shear_force(mesh_scoping=my_ball_scoping).eval() +Splateforces = my_model.results.beam_s_shear_force(mesh_scoping=my_plate_scoping).eval() -comparison_plot = dpf.plotter.DpfPlotter -comparison_plot.add_field(field=Sforces, meshed_region=my_meshes[1]) -comparison_plot.add_field(field=Sforces2, meshed_region=my_meshes[0]) +comparison_plot = dpf.plotter.DpfPlotter() +comparison_plot.add_field( + field=Sballforces.get_field(label_space_or_index={"time": 12}), meshed_region=my_ball_mesh +) +comparison_plot.add_field( + field=Splateforces.get_field(label_space_or_index={"time": 12}), meshed_region=my_plate_mesh +) comparison_plot.show_figure() From d860a33b2adc5cc1321712e8ed9e8aa8a15150c1 Mon Sep 17 00:00:00 2001 From: luisaFelixSalles Date: Mon, 14 Oct 2024 16:42:22 +0200 Subject: [PATCH 3/5] new ls dyna example --- examples/14-lsdyna/01-lsdyna_beam.py | 94 ++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/examples/14-lsdyna/01-lsdyna_beam.py b/examples/14-lsdyna/01-lsdyna_beam.py index 041d01025d..801249db1d 100644 --- a/examples/14-lsdyna/01-lsdyna_beam.py +++ b/examples/14-lsdyna/01-lsdyna_beam.py @@ -54,7 +54,7 @@ ds.set_result_file_path(d3plot[0], "d3plot") ds.add_file_path(d3plot[3], "actunits") my_model = dpf.Model(ds) -# print(model) +# print(my_model) ############################################################################### # The model has solid (3D) elements and beam (1D) elements. Some of the results @@ -64,55 +64,97 @@ # By splitting the mesh by element shape we see that the ball is made by the solid # 3D elements and the plate by the beam 1D elements +# Define the analysis mesh my_meshed_region = my_model.metadata.meshed_region +# Get separate meshes for each body my_meshes = ops.mesh.split_mesh( mesh=my_meshed_region, property=dpf.common.elemental_properties.element_shape ).eval() -my_ball_mesh = my_meshes.get_mesh(label_space_or_index={"body": 1, "elshape": 1}) -my_plate_mesh = my_meshes.get_mesh(label_space_or_index={"body": 2, "elshape": 2}) + +# Define the meshes in separate variables +ball_mesh = my_meshes.get_mesh(label_space_or_index={"body": 1, "elshape": 1}) +plate_mesh = my_meshes.get_mesh(label_space_or_index={"body": 2, "elshape": 2}) + # print(my_meshes) ############################################################################### # Ball -# print(my_meshes[0]) -# my_meshes[0].plot() +# print(ball_mesh) +# my_ball_mesh.plot() ############################################################################### # Plate -# print(my_meshes[1]) -# my_meshes[1].plot() - +# print(my_plate_mesh) +# my_plate_mesh.plot() ############################################################################### -# We can split the mesh scoping so it's easier to chose from which body we are -# analysing the results my_meshes_scopings = ops.scoping.split_on_property_type(mesh=my_meshed_region).eval() -my_ball_scoping = my_meshes_scopings.get_scoping(label_space_or_index={"elshape": 1}) -my_plate_scoping = my_meshes_scopings.get_scoping(label_space_or_index={"elshape": 2}) - -my_time_scoping = my_model.metadata.time_freq_support.time_frequencies -# For example the ball velocity -v = my_model.results.velocity(time_scoping=my_time_scoping).eval() +# Define the mesh scoping in separate variables +# Here we have a elemental location +ball_scoping = my_meshes_scopings.get_scoping(label_space_or_index={"elshape": 1}) +plate_scoping = my_meshes_scopings.get_scoping(label_space_or_index={"elshape": 2}) -# Forces - +# We will need a nodal location, so we have to transpose the mesh scoping from elemental to nodal +ball_scoping_nodal = dpf.operators.scoping.transpose( + mesh_scoping=ball_scoping, meshed_region=my_meshed_region +).eval() +plate_scoping_nodal = dpf.operators.scoping.transpose( + mesh_scoping=plate_scoping, meshed_region=my_meshed_region +).eval() ############################################################################### -# compare results in different time steps -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sballforces = my_model.results.beam_s_shear_force(mesh_scoping=my_ball_scoping).eval() -Splateforces = my_model.results.beam_s_shear_force(mesh_scoping=my_plate_scoping).eval() - +# Comparing results in different time steps +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# 1) Define the time steps +time_steps_set = [2, 6, 12] +j = -400 +# 2) Copy the mesh of interest. Here it is the plate mesh that we copy along the X axis +for i in time_steps_set: + # Copy the mesh + globals()[f"plate_mesh_{i}"] = plate_mesh.deep_copy() + + # 3) Get the plot coordinates that will be changed + coords_to_update = globals()[f"plate_mesh_{i}"].nodes.coordinates_field + # 4) Define the coordinates where the new mesh will be placed + overall_field = dpf.fields_factory.create_3d_vector_field( + num_entities=1, location=dpf.locations.overall + ) + overall_field.append(data=[j, 0.0, 0.0], scopingid=1) + + # 5) Define the updated coordinates + new_coordinates = ops.math.add(fieldA=coords_to_update, fieldB=overall_field).eval() + coords_to_update.data = new_coordinates.data + + # 6) Extract the result, here we start by getting the displacement + globals()[f"my_displacement_{i}"] = my_model.results.displacement( + time_scoping=i, mesh_scoping=plate_scoping_nodal + ).eval()[0] + + # Increment the coordinate value for the loop + j = j - 400 +############################################################################### +# Use the :class: `Plotter ` class to add the plots +# in the same image comparison_plot = dpf.plotter.DpfPlotter() + comparison_plot.add_field( - field=Sballforces.get_field(label_space_or_index={"time": 12}), meshed_region=my_ball_mesh + field=my_displacement_2, meshed_region=plate_mesh_2, deform_by=my_displacement_2 ) comparison_plot.add_field( - field=Splateforces.get_field(label_space_or_index={"time": 12}), meshed_region=my_plate_mesh + field=my_displacement_6, meshed_region=plate_mesh_6, deform_by=my_displacement_6 ) +comparison_plot.add_field( + field=my_displacement_12, meshed_region=plate_mesh_12, deform_by=my_displacement_12 +) + comparison_plot.show_figure() + +############################################################################### +# For example the ball velocity +# v = my_model.results.velocity(time_scoping=my_time_scoping, mesh_scoping=ball_scoping).eval() + +############################################################################### From 8ea67e51334b903b6992600c23aa4b9c75d7ff6e Mon Sep 17 00:00:00 2001 From: luisaFelixSalles Date: Thu, 31 Oct 2024 17:36:15 +0100 Subject: [PATCH 4/5] complete beam example --- examples/14-lsdyna/01-lsdyna_beam.py | 278 ++++++++++++++++++++++----- 1 file changed, 226 insertions(+), 52 deletions(-) diff --git a/examples/14-lsdyna/01-lsdyna_beam.py b/examples/14-lsdyna/01-lsdyna_beam.py index 801249db1d..bfefb489f2 100644 --- a/examples/14-lsdyna/01-lsdyna_beam.py +++ b/examples/14-lsdyna/01-lsdyna_beam.py @@ -44,17 +44,18 @@ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Create the model and print its contents. This LS-DYNA d3plot file contains # several individual results, each at different times. The d3plot file does not -# contain information related to Units. In this case, as the simulation was run -# through Mechanical, a file.actunits file is produced. If this file is -# supplemented in the data_sources, the units will be correctly fetched for all -# results in the file as well as for the mesh. +# contain information related to Units. + +# In this case, as the simulation was run through Mechanical, a ''file.actunits'' +# file is produced. If this file is supplemented in the data_sources, the units +# will be correctly fetched for all results in the file as well as for the mesh. d3plot = examples.download_d3plot_beam() -ds = dpf.DataSources() -ds.set_result_file_path(d3plot[0], "d3plot") -ds.add_file_path(d3plot[3], "actunits") -my_model = dpf.Model(ds) -# print(my_model) +my_data_sources = dpf.DataSources() +my_data_sources.set_result_file_path(d3plot[0], key="d3plot") +my_data_sources.add_file_path(d3plot[3], key="actunits") +my_model = dpf.Model(my_data_sources) +print(my_model) ############################################################################### # The model has solid (3D) elements and beam (1D) elements. Some of the results @@ -72,89 +73,262 @@ mesh=my_meshed_region, property=dpf.common.elemental_properties.element_shape ).eval() -# Define the meshes in separate variables +# Define the meshes for each body in separate variables ball_mesh = my_meshes.get_mesh(label_space_or_index={"body": 1, "elshape": 1}) plate_mesh = my_meshes.get_mesh(label_space_or_index={"body": 2, "elshape": 2}) # print(my_meshes) + ############################################################################### # Ball -# print(ball_mesh) -# my_ball_mesh.plot() +print("Ball mesh", "\n", ball_mesh, "\n") +ball_mesh.plot(title="Ball mesh", text="Ball mesh") ############################################################################### # Plate -# print(my_plate_mesh) -# my_plate_mesh.plot() +print("Plate mesh", "\n", plate_mesh) +plate_mesh.plot(title="Plate mesh", text="Plate mesh") ############################################################################### +# Define the mesh scoping to use it with the operators +my_meshes_scoping = ops.scoping.split_on_property_type(mesh=my_meshed_region).eval() -my_meshes_scopings = ops.scoping.split_on_property_type(mesh=my_meshed_region).eval() +# Define the mesh scoping for each body/element shape in separate variables +ball_scoping = my_meshes_scoping.get_scoping(label_space_or_index={"elshape": 1}) +plate_scoping = my_meshes_scoping.get_scoping(label_space_or_index={"elshape": 2}) -# Define the mesh scoping in separate variables -# Here we have a elemental location -ball_scoping = my_meshes_scopings.get_scoping(label_space_or_index={"elshape": 1}) -plate_scoping = my_meshes_scopings.get_scoping(label_space_or_index={"elshape": 2}) - -# We will need a nodal location, so we have to transpose the mesh scoping from elemental to nodal -ball_scoping_nodal = dpf.operators.scoping.transpose( - mesh_scoping=ball_scoping, meshed_region=my_meshed_region -).eval() +# We will plot the results in a mesh deformed by the displacement. The displacement +# is in a nodal location, so we need to define a nodal scoping for the palte plate_scoping_nodal = dpf.operators.scoping.transpose( mesh_scoping=plate_scoping, meshed_region=my_meshed_region ).eval() + +############################################################################### + +# The next manipulations can be applied to the following beam operators +# that handle the correspondent results : + +# - beam_axial_force: Beam Axial Force +# - beam_s_shear_force: Beam S Shear Force +# - beam_t_shear_force: Beam T Shear Force +# - beam_s_bending_moment: Beam S Bending Moment +# - beam_t_bending_moment: Beam T Bending Moment +# - beam_torsional_moment: Beam Torsional Moment +# - beam_axial_stress: Beam Axial Stress +# - beam_rs_shear_stress: Beam Rs Shear Stress +# - beam_tr_shear_stress: Beam Tr Shear Stress +# - beam_axial_plastic_strain: Beam Axial Plastic Strain +# - beam_axial_total_strain: Beam Axial Total Strain + +# We do not demonstrate separately how to use each of them in this example +# once they have similar methods. We .... in the beam stress and forces results + +# So, if you want to operate on other operator, uou just need to change their +# scripting name in the code lines. + ############################################################################### # Comparing results in different time steps # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# 1) Define the time steps + +# 1) Define the time steps set time_steps_set = [2, 6, 12] + +# 2) Prepare the collections to store the results for each time step + +# To compare the results in the same image you have to copy the mesh for each plot +plate_meshes = dpf.MeshesContainer() +plate_meshes.add_label("time") + +# The displacements for each time steps to deform the mesh accordingly +plate_displacements = dpf.FieldsContainer() +plate_displacements.add_label(label="time") + +# The axial force results for each time steps. Here +plate_axial_force = dpf.FieldsContainer() +plate_axial_force.add_label(label="time") + +# 3) Use the :class: `Plotter ` class +# to add the plots in the same image +comparison_plot = dpf.plotter.DpfPlotter() + +side_bar_args = dict( + title="Beam axial force (N)", fmt="%.2e", title_font_size=15, label_font_size=15 +) + +# 4) As we want to compare the results in the same plot we will need this variable. +# It represents the distance between the meshes j = -400 -# 2) Copy the mesh of interest. Here it is the plate mesh that we copy along the X axis -for i in time_steps_set: + +# Here we use a loop where each iteration correspond to the manipulations for a given time step + +# 5) Copy the mesh of interest. Here it is the plate mesh that we copy along the X axis +for i in time_steps_set: # Loop through the time steps # Copy the mesh - globals()[f"plate_mesh_{i}"] = plate_mesh.deep_copy() + plate_meshes.add_mesh(label_space={"time": i}, mesh=plate_mesh.deep_copy()) - # 3) Get the plot coordinates that will be changed - coords_to_update = globals()[f"plate_mesh_{i}"].nodes.coordinates_field - # 4) Define the coordinates where the new mesh will be placed + # 6) Get the plot coordinates that will be changed (so we can compare the results side by side) + coords_to_update = plate_meshes.get_mesh( + label_space_or_index={"time": i} + ).nodes.coordinates_field + + # 7) Define the coordinates where the new mesh will be placed overall_field = dpf.fields_factory.create_3d_vector_field( num_entities=1, location=dpf.locations.overall ) overall_field.append(data=[j, 0.0, 0.0], scopingid=1) - # 5) Define the updated coordinates + # 8) Define the updated coordinates new_coordinates = ops.math.add(fieldA=coords_to_update, fieldB=overall_field).eval() coords_to_update.data = new_coordinates.data - # 6) Extract the result, here we start by getting the displacement - globals()[f"my_displacement_{i}"] = my_model.results.displacement( - time_scoping=i, mesh_scoping=plate_scoping_nodal - ).eval()[0] - - # Increment the coordinate value for the loop + # 9) Extract the result, here we start by getting the beam_rs_shear_stress + plate_axial_force.add_field( + label_space={"time": i}, + field=my_model.results.beam_axial_force( + time_scoping=i, mesh_scoping=plate_scoping_nodal + ).eval()[0], + ) + # 10) We will also get the displacement to deform the mesh + plate_displacements.add_field( + label_space={"time": i}, + field=my_model.results.displacement( + time_scoping=i, mesh_scoping=plate_scoping_nodal + ).eval()[0], + ) + # 11) Add the result and the mesh to the plot + comparison_plot.add_field( + field=plate_axial_force.get_field(label_space_or_index={"time": i}), + meshed_region=plate_meshes.get_mesh(label_space_or_index={"time": i}), + deform_by=plate_displacements.get_field(label_space_or_index={"time": i}), + scalar_bar_args=side_bar_args, + ) + comparison_plot.add_node_labels( + nodes=[289], + labels=[f"Time step = {i}"], + meshed_region=plate_meshes.get_mesh(label_space_or_index={"time": i}), + font_size=10, + ) + # 12) Increment the coordinate value for the loop j = j - 400 + +# Visualise the plot +comparison_plot.show_figure() + ############################################################################### -# Use the :class: `Plotter ` class to add the plots -# in the same image +# Plot a graph over time for the elements with max and min results values +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -comparison_plot = dpf.plotter.DpfPlotter() +# Here we make a workflow with a more verbose approach. This is useful because we use operators +# having several matching inputs or outputs. So the connexions are more clear, and it is +# easier to use and reuse the workflow. -comparison_plot.add_field( - field=my_displacement_2, meshed_region=plate_mesh_2, deform_by=my_displacement_2 -) -comparison_plot.add_field( - field=my_displacement_6, meshed_region=plate_mesh_6, deform_by=my_displacement_6 -) -comparison_plot.add_field( - field=my_displacement_12, meshed_region=plate_mesh_12, deform_by=my_displacement_12 +# Find the element with the max values over all the time steps and return its ID + +# Define the workflow object +max_workflow = dpf.Workflow() +max_workflow.progress_bar = False +# Define the norm operator +max_norm = ops.math.norm_fc() +# Define the max of each entity with the evaluated norm as an input +max_per_ent = ops.min_max.min_max_by_entity(fields_container=max_norm.outputs.fields_container) +# Define the max over all entities +global_max = ops.min_max.min_max(field=max_per_ent.outputs.field_max) +# Get the scoping +max_scop = ops.utility.extract_scoping(field_or_fields_container=global_max.outputs.field_max) +# Get the id +max_id = ops.scoping.scoping_get_attribute( + scoping=max_scop.outputs.mesh_scoping_as_scoping, property_name="ids" ) -comparison_plot.show_figure() +# Add the operators to the workflow +max_workflow.add_operators(operators=[max_norm, max_per_ent, global_max, max_scop, max_id]) +max_workflow.set_input_name("fields_container", max_norm.inputs.fields_container) +max_workflow.set_output_name("max_id", max_id.outputs.property_as_vector_int32_) +max_workflow.set_output_name("max_entity_scoping", max_scop.outputs.mesh_scoping_as_scoping) ############################################################################### -# For example the ball velocity -# v = my_model.results.velocity(time_scoping=my_time_scoping, mesh_scoping=ball_scoping).eval() +# Get all the time steps +time_all = my_model.metadata.time_freq_support.time_frequencies +# Extract all the stresses results on the plate +plate_beam_axial_stress = my_model.results.beam_axial_stress( + time_scoping=time_all, mesh_scoping=plate_scoping +).eval() +plate_beam_rs_shear_stress = my_model.results.beam_rs_shear_stress( + time_scoping=time_all, mesh_scoping=plate_scoping +).eval() +plate_beam_tr_shear_stress = my_model.results.beam_tr_shear_stress( + time_scoping=time_all, mesh_scoping=plate_scoping +).eval() + +# List of operators to simplify the code +beam_stresses = [plate_beam_axial_stress, plate_beam_rs_shear_stress, plate_beam_tr_shear_stress] +graph_labels = [ + "Beam axial stress", + "Beam rs shear stress", + "Beam tr shear stress", +] +# List of elements ids +max_stress_elements_ids = [] +# Scopings container +max_stress_elements_scopings = dpf.ScopingsContainer() +max_stress_elements_scopings.add_label("stress_result") + +# Loop through each stress result that gets the elements with maximum solicitation id, re-escope the fields +# container to keep only the data for this element, and finally plot a stress x time graph + +for j in range(0, len(beam_stresses)): # Loop through each stress result + # Use the pre-defined workflow to define the element with maximum solicitation + max_workflow.connect(pin_name="fields_container", inpt=beam_stresses[j]) + max_stress_elements_ids.append( + max_workflow.get_output(pin_name="max_id", output_type=dpf.types.vec_int) + ) + max_stress_elements_scopings.add_scoping( + label_space={"stress_result": j}, + scoping=max_workflow.get_output( + pin_name="max_entity_scoping", output_type=dpf.types.scoping + ), + ) + + # Re-scope the results to keep only the data for the identified element + beam_stresses[j] = ops.scoping.rescope_fc( + fields_container=beam_stresses[j], + mesh_scoping=max_stress_elements_scopings.get_scoping( + label_space_or_index={"stress_result": j} + ), + ).eval() + + # The d3plot file gives us fields containers labeled by time. So in each field we have the stress value in a + # given time for the chosen element. We need to rearrange the fields container into fields. + + beam_stresses[j] = ops.utility.merge_to_field_matrix(fields1=beam_stresses[j]).eval() + plt.plot( + time_all.data, + beam_stresses[j].data[0], + label=f"{graph_labels[j]}, element id:{max_stress_elements_ids[j][0]}", + ) + +plt.title("Beam stresses evolution") +plt.xlabel("Time (s)") +plt.ylabel("Beam stresses (MPa)") +plt.legend() +plt.show() ############################################################################### +# Results coordinates system +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# The results are given in the Cartesian coordinates system by default. + +# The beam results are given directly in the local directions. For example the beam stresses: + +# We have the axial stress, given in the beam axis, and the stresses defined in the +# cross-section directions, tr stress in the transverse direction (t) and rs stress +# perpendicular to the tr direction (s). + +# Those results are given as scalars. + +# Unfortunately there are no operators for LS-DYNA files that directly allows you to: +# - Rotate results from local coordinate system to global coordinate system; +# - Extract the rotation matrix between the local and global coordinate systems; From 142a15ce2b93e42a400ddf23773ebce1320b9a81 Mon Sep 17 00:00:00 2001 From: luisaFelixSalles Date: Fri, 8 Nov 2024 14:32:25 +0100 Subject: [PATCH 5/5] updates --- examples/14-lsdyna/01-lsdyna_beam.py | 125 ++++++++++++++++----------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/examples/14-lsdyna/01-lsdyna_beam.py b/examples/14-lsdyna/01-lsdyna_beam.py index bfefb489f2..e228a08017 100644 --- a/examples/14-lsdyna/01-lsdyna_beam.py +++ b/examples/14-lsdyna/01-lsdyna_beam.py @@ -23,8 +23,8 @@ """ .. _lsdyna_operators: -Results extraction and analysis from LS-DYNA sources -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Beam results manipulations +-------------------------- This example provides an overview of the LS-DYNA beam results manipulations. @@ -40,12 +40,12 @@ from ansys.dpf.core import operators as ops ############################################################################### -# d3plot file results extraction -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# d3plot file data extraction +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Create the model and print its contents. This LS-DYNA d3plot file contains # several individual results, each at different times. The d3plot file does not # contain information related to Units. - +# # In this case, as the simulation was run through Mechanical, a ''file.actunits'' # file is produced. If this file is supplemented in the data_sources, the units # will be correctly fetched for all results in the file as well as for the mesh. @@ -58,58 +58,67 @@ print(my_model) ############################################################################### +# Exploring the mesh +# ~~~~~~~~~~~~~~~~~~ +# # The model has solid (3D) elements and beam (1D) elements. Some of the results # only apply to one type of elements (such as the stress tensor for solids, or # the axial force for beams, for example). - +# # By splitting the mesh by element shape we see that the ball is made by the solid # 3D elements and the plate by the beam 1D elements - -# Define the analysis mesh +# +# - Define the analysis mesh my_meshed_region = my_model.metadata.meshed_region -# Get separate meshes for each body +# - Get separate meshes for each body my_meshes = ops.mesh.split_mesh( mesh=my_meshed_region, property=dpf.common.elemental_properties.element_shape ).eval() -# Define the meshes for each body in separate variables +# - Define the meshes for each body in separate variables ball_mesh = my_meshes.get_mesh(label_space_or_index={"body": 1, "elshape": 1}) plate_mesh = my_meshes.get_mesh(label_space_or_index={"body": 2, "elshape": 2}) -# print(my_meshes) +print(my_meshes) ############################################################################### -# Ball +# Plate mesh -print("Ball mesh", "\n", ball_mesh, "\n") -ball_mesh.plot(title="Ball mesh", text="Ball mesh") +print("Plate mesh", "\n", plate_mesh) +plate_mesh.plot(title="Plate mesh", text="Plate mesh") ############################################################################### -# Plate +# Ball mesh -print("Plate mesh", "\n", plate_mesh) -plate_mesh.plot(title="Plate mesh", text="Plate mesh") +print("Ball mesh", "\n", ball_mesh, "\n") +ball_mesh.plot(title="Ball mesh", text="Ball mesh") ############################################################################### -# Define the mesh scoping to use it with the operators +# Scoping +# ~~~~~~~ +# +# - Define the mesh scoping to use it with the operators my_meshes_scoping = ops.scoping.split_on_property_type(mesh=my_meshed_region).eval() -# Define the mesh scoping for each body/element shape in separate variables +############################################################################### +# - Define the mesh scoping for each body/element shape in separate variables ball_scoping = my_meshes_scoping.get_scoping(label_space_or_index={"elshape": 1}) plate_scoping = my_meshes_scoping.get_scoping(label_space_or_index={"elshape": 2}) -# We will plot the results in a mesh deformed by the displacement. The displacement -# is in a nodal location, so we need to define a nodal scoping for the palte +############################################################################### +# - We will plot the results in a mesh deformed by the displacement. +# The displacement is in a nodal location, so we need to define a nodal scoping for the plate plate_scoping_nodal = dpf.operators.scoping.transpose( mesh_scoping=plate_scoping, meshed_region=my_meshed_region ).eval() ############################################################################### - +# Beam results +# ~~~~~~~~~~~~ # The next manipulations can be applied to the following beam operators # that handle the correspondent results : - +# # - beam_axial_force: Beam Axial Force # - beam_s_shear_force: Beam S Shear Force # - beam_t_shear_force: Beam T Shear Force @@ -121,10 +130,10 @@ # - beam_tr_shear_stress: Beam Tr Shear Stress # - beam_axial_plastic_strain: Beam Axial Plastic Strain # - beam_axial_total_strain: Beam Axial Total Strain - +# # We do not demonstrate separately how to use each of them in this example -# once they have similar methods. We .... in the beam stress and forces results - +# once they have similar methods. +# # So, if you want to operate on other operator, uou just need to change their # scripting name in the code lines. @@ -137,22 +146,22 @@ # 2) Prepare the collections to store the results for each time step -# To compare the results in the same image you have to copy the mesh for each plot +# a. To compare the results in the same image you have to copy the mesh for each plot plate_meshes = dpf.MeshesContainer() plate_meshes.add_label("time") -# The displacements for each time steps to deform the mesh accordingly +# b. The displacements for each time steps to deform the mesh accordingly plate_displacements = dpf.FieldsContainer() plate_displacements.add_label(label="time") -# The axial force results for each time steps. Here +# c. The axial force results for each time steps. Here plate_axial_force = dpf.FieldsContainer() plate_axial_force.add_label(label="time") -# 3) Use the :class: `Plotter ` class -# to add the plots in the same image +# 3) Use the Plotter class to add the plots in the same image comparison_plot = dpf.plotter.DpfPlotter() +# Side bar arguments definition side_bar_args = dict( title="Beam axial force (N)", fmt="%.2e", title_font_size=15, label_font_size=15 ) @@ -161,9 +170,9 @@ # It represents the distance between the meshes j = -400 +# 5) Copy the mesh of interest. Here it is the plate mesh that we copy along the X axis # Here we use a loop where each iteration correspond to the manipulations for a given time step -# 5) Copy the mesh of interest. Here it is the plate mesh that we copy along the X axis for i in time_steps_set: # Loop through the time steps # Copy the mesh plate_meshes.add_mesh(label_space={"time": i}, mesh=plate_mesh.deep_copy()) @@ -213,18 +222,19 @@ # 12) Increment the coordinate value for the loop j = j - 400 + # Visualise the plot comparison_plot.show_figure() ############################################################################### # Plot a graph over time for the elements with max and min results values # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +# # Here we make a workflow with a more verbose approach. This is useful because we use operators # having several matching inputs or outputs. So the connexions are more clear, and it is # easier to use and reuse the workflow. - -# Find the element with the max values over all the time steps and return its ID +# +# The following workflow finds the element with the max values over all the time steps and return its ID # Define the workflow object max_workflow = dpf.Workflow() @@ -249,8 +259,13 @@ max_workflow.set_output_name("max_entity_scoping", max_scop.outputs.mesh_scoping_as_scoping) ############################################################################### +# Using the workflow to the stresses results on the plate: +# +# - Extract the results + # Get all the time steps time_all = my_model.metadata.time_freq_support.time_frequencies + # Extract all the stresses results on the plate plate_beam_axial_stress = my_model.results.beam_axial_stress( time_scoping=time_all, mesh_scoping=plate_scoping @@ -262,21 +277,30 @@ time_scoping=time_all, mesh_scoping=plate_scoping ).eval() -# List of operators to simplify the code +############################################################################### +# - As we will use the workflow for different results operators we group them and +# use a loop through the group. Here we prepare where the workflow outputs will be stored + +# List of operators to be used in the workflow beam_stresses = [plate_beam_axial_stress, plate_beam_rs_shear_stress, plate_beam_tr_shear_stress] graph_labels = [ "Beam axial stress", "Beam rs shear stress", "Beam tr shear stress", ] -# List of elements ids + +# List of elements ids that we will get from the workflow max_stress_elements_ids = [] + # Scopings container max_stress_elements_scopings = dpf.ScopingsContainer() max_stress_elements_scopings.add_label("stress_result") -# Loop through each stress result that gets the elements with maximum solicitation id, re-escope the fields -# container to keep only the data for this element, and finally plot a stress x time graph +############################################################################### +# - The following loop: +# a) Goes through each stress result and get the element id with maximum solicitation +# b) Re-escope the fields container to keep only the data for this element +# c) Plot a stress x time graph for j in range(0, len(beam_stresses)): # Loop through each stress result # Use the pre-defined workflow to define the element with maximum solicitation @@ -309,6 +333,7 @@ label=f"{graph_labels[j]}, element id:{max_stress_elements_ids[j][0]}", ) +# Graph formatting plt.title("Beam stresses evolution") plt.xlabel("Time (s)") plt.ylabel("Beam stresses (MPa)") @@ -318,17 +343,17 @@ ############################################################################### # Results coordinates system # ~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# The results are given in the Cartesian coordinates system by default. - -# The beam results are given directly in the local directions. For example the beam stresses: - -# We have the axial stress, given in the beam axis, and the stresses defined in the -# cross-section directions, tr stress in the transverse direction (t) and rs stress -# perpendicular to the tr direction (s). - -# Those results are given as scalars. - +# +# The general results are given in the Cartesian coordinates system by default. +# +# The beam results are given directly in the local directions as scalars. +# For example the beam stresses we have: +# +# - The axial stress, given in the beam axis +# - The stresses defined in the cross-section directions: tr stress in the transverse +# direction (t) and rs stress perpendicular to the tr direction (s). +# +# # Unfortunately there are no operators for LS-DYNA files that directly allows you to: # - Rotate results from local coordinate system to global coordinate system; # - Extract the rotation matrix between the local and global coordinate systems;