diff --git a/ansys/dpf/core/examples/downloads.py b/ansys/dpf/core/examples/downloads.py index b72df6b8a7f..f73a874ef26 100644 --- a/ansys/dpf/core/examples/downloads.py +++ b/ansys/dpf/core/examples/downloads.py @@ -3,7 +3,6 @@ import os import urllib.request - EXAMPLE_REPO = "https://github.com/pyansys/example-data/raw/master/result_files/" @@ -22,7 +21,8 @@ def _retrieve_file(url, filename, directory): """Download a file from a url""" from ansys.dpf.core import LOCAL_DOWNLOADED_EXAMPLES_PATH, path_utilities # First check if file has already been downloaded - local_path = os.path.join(LOCAL_DOWNLOADED_EXAMPLES_PATH, directory, os.path.basename(filename)) + local_path = os.path.join(LOCAL_DOWNLOADED_EXAMPLES_PATH, directory, + os.path.basename(filename)) local_path_no_zip = local_path.replace(".zip", "") if os.path.isfile(local_path_no_zip) or os.path.isdir(local_path_no_zip): return path_utilities.to_server_os(local_path_no_zip.replace( @@ -380,3 +380,28 @@ def download_extrapolation_2d_result() -> dict: } return dict + + +def download_hemisphere() -> str: + """Download an example result file from a static analysis and + return the download path. + + Examples files are downloaded to a persistent cache to avoid + re-downloading the same file twice. + + Returns + ------- + str + Path to the example file. + + Examples + -------- + Download an example result file and return the path of the file + + >>> from ansys.dpf.core import examples + >>> path = examples.download_hemisphere() + >>> path + 'C:/Users/user/AppData/local/temp/hemisphere.rst' + + """ + return _download_file("hemisphere", "hemisphere.rst") diff --git a/examples/03-advanced/06-stress_gradient_path.py b/examples/03-advanced/06-stress_gradient_path.py new file mode 100644 index 00000000000..a01a94512d9 --- /dev/null +++ b/examples/03-advanced/06-stress_gradient_path.py @@ -0,0 +1,145 @@ +""" +.. _stress_gradient_path: + +Stress gradient normal to a defined node. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This example shows how to plot a stress gradient normal to a selected node. +As the example is based on creating a path along the normal, the selected node +must be on the surface of the geometry. +A path is created of a defined length. + +""" + +############################################################################### +# First, import the DPF-Core module as ``dpf`` and import the +# included examples file and ``DpfPlotter`` +# +import matplotlib.pyplot as plt +from ansys.dpf import core as dpf +from ansys.dpf.core import operators as ops +from ansys.dpf.core.plotter import DpfPlotter +from ansys.dpf.core import examples + +############################################################################### +# Next, open an example and print out the ``model`` object. The +# :class:`Model class helps to organize access +# methods for the result by keeping track of the operators and data sources +# used by the result file. +# +# Printing the model displays: +# +# - Analysis type +# - Available results +# - Size of the mesh +# - Number of results +# - Unit +# +path = examples.download_hemisphere() +model = dpf.Model(path) +print(model) +############################################################################### +# Define the `node_id` normal to which a stress gradient should be plotted. +# +node_id = 1928 +############################################################################### +# The following command prints the mesh unit +# +unit = model.metadata.meshed_region.unit +print("Unit: %s" % unit) +############################################################################### +# `depth` defines the path length / depth to which the path will penetrate. +# While defining `depth` make sure you use the correct mesh unit. +# `delta` defines distance between consecutive points on the path. +depth = 10 # in mm +delta = 0.1 # in mm +############################################################################### +# Get the meshed region +# +mesh = model.metadata.meshed_region +############################################################################### +# Get Equivalent stress fields container. +# +stress_fc = model.results.stress().eqv().outputs.fields_container() +############################################################################### +# Define Nodal scoping. +# Make sure to define ``"Nodal"`` as the requested location, important for the +# `normals` operator. +# +nodal_scoping = dpf.Scoping(location=dpf.locations.nodal) +nodal_scoping.ids = [node_id] +############################################################################### +# Get Skin Mesh because `normals` operator requires Shells as input. +# +skin_mesh = ops.mesh.skin(mesh=mesh) +skin_meshed_region = skin_mesh.outputs.mesh.get_data() +############################################################################### +# Get normal at a node using `normals` operator. +# +normal = ops.geo.normals() +normal.inputs.mesh.connect(skin_meshed_region) +normal.inputs.mesh_scoping.connect(nodal_scoping) +normal_vec_out_field = normal.outputs.field.get_data() +############################################################################### +# Normal vector is along the surface normal. We need to invert the vector +# using `math.scale` operator inwards in the geometry, to get the path +# direction. +# +normal_vec_in_field = ops.math.scale(field=normal_vec_out_field, + ponderation=-1.0) +normal_vec_in = normal_vec_in_field.outputs.field.get_data().data[0] +############################################################################### +# Get Nodal coordinates, they serve as the first point on the line. +# +node = mesh.nodes.node_by_id(node_id) +line_fp = node.coordinates +############################################################################### +# Create 3D line equation. +# +fx = lambda t: line_fp[0] + normal_vec_in[0] * t +fy = lambda t: line_fp[1] + normal_vec_in[1] * t +fz = lambda t: line_fp[2] + normal_vec_in[2] * t +############################################################################### +# Create coordinates using 3D line equation- +# +coordinates = [[fx(t * delta), fy(t * delta), fz(t * delta)] for t in + range(int(depth / delta))] +flat_coordinates = [entry for data in coordinates for entry in data] +############################################################################### +# Create Field for coordinates of the path. +# +field_coord = dpf.fields_factory.create_3d_vector_field(len(coordinates)) +field_coord.data = flat_coordinates +field_coord.scoping.ids = list(range(1, len(coordinates) + 1)) +############################################################################### +# Let's now map results on the path. +mapping_operator = ops.mapping.on_coordinates( + fields_container=stress_fc, + coordinates=field_coord, + create_support=True, + mesh=mesh) +fields_mapped = mapping_operator.outputs.fields_container() +############################################################################### +# Here, we request the mapped field data and its mesh +field_m = fields_mapped[0] +mesh_m = field_m.meshed_region +############################################################################### +# Create stress vs length chart. +# +x_initial = 0.0 +length = [x_initial + delta * index for index in range(len(field_m.data))] +plt.plot(length, field_m.data, "r") +plt.xlabel("Length (%s)" % mesh.unit) +plt.ylabel("Stress (%s)" % field_m.unit) +plt.show() +############################################################################### +# To create a plot we need to add both the meshes +# `mesh_m` - mapped mesh +# `mesh` - original mesh +pl = DpfPlotter() +pl.add_field(field_m, mesh_m) +pl.add_mesh(mesh, style="surface", show_edges=True, + color="w", opacity=0.3) +pl.show_figure(show_axes=True, cpos=[ + (62.687, 50.119, 67.247), + (5.135, 6.458, -0.355), + (-0.286, 0.897, -0.336)]) diff --git a/setup.py b/setup.py index 6d6fb55211f..f5e6ef1a349 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,6 @@ install_requires = ["psutil", "progressbar2", "numpy", "ansys.grpc.dpf>=0.2.3"] - # Get version from version info filepath = os.path.dirname(__file__) __version__ = None