Skip to content

Commit b305f87

Browse files
Stress gradient path example (#179)
* stress gradient example * added hemisphere.rst to examples * added comments * newline at the end[F * comment correction * comment correction * added hemisphere.rst to setup.py * updated comments
1 parent 18bc234 commit b305f87

File tree

3 files changed

+172
-3
lines changed

3 files changed

+172
-3
lines changed

ansys/dpf/core/examples/downloads.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import os
44
import urllib.request
55

6-
76
EXAMPLE_REPO = "https://github.com/pyansys/example-data/raw/master/result_files/"
87

98

@@ -22,7 +21,8 @@ def _retrieve_file(url, filename, directory):
2221
"""Download a file from a url"""
2322
from ansys.dpf.core import LOCAL_DOWNLOADED_EXAMPLES_PATH, path_utilities
2423
# First check if file has already been downloaded
25-
local_path = os.path.join(LOCAL_DOWNLOADED_EXAMPLES_PATH, directory, os.path.basename(filename))
24+
local_path = os.path.join(LOCAL_DOWNLOADED_EXAMPLES_PATH, directory,
25+
os.path.basename(filename))
2626
local_path_no_zip = local_path.replace(".zip", "")
2727
if os.path.isfile(local_path_no_zip) or os.path.isdir(local_path_no_zip):
2828
return path_utilities.to_server_os(local_path_no_zip.replace(
@@ -380,3 +380,28 @@ def download_extrapolation_2d_result() -> dict:
380380
}
381381

382382
return dict
383+
384+
385+
def download_hemisphere() -> str:
386+
"""Download an example result file from a static analysis and
387+
return the download path.
388+
389+
Examples files are downloaded to a persistent cache to avoid
390+
re-downloading the same file twice.
391+
392+
Returns
393+
-------
394+
str
395+
Path to the example file.
396+
397+
Examples
398+
--------
399+
Download an example result file and return the path of the file
400+
401+
>>> from ansys.dpf.core import examples
402+
>>> path = examples.download_hemisphere()
403+
>>> path
404+
'C:/Users/user/AppData/local/temp/hemisphere.rst'
405+
406+
"""
407+
return _download_file("hemisphere", "hemisphere.rst")
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""
2+
.. _stress_gradient_path:
3+
4+
Stress gradient normal to a defined node.
5+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6+
This example shows how to plot a stress gradient normal to a selected node.
7+
As the example is based on creating a path along the normal, the selected node
8+
must be on the surface of the geometry.
9+
A path is created of a defined length.
10+
11+
"""
12+
13+
###############################################################################
14+
# First, import the DPF-Core module as ``dpf`` and import the
15+
# included examples file and ``DpfPlotter``
16+
#
17+
import matplotlib.pyplot as plt
18+
from ansys.dpf import core as dpf
19+
from ansys.dpf.core import operators as ops
20+
from ansys.dpf.core.plotter import DpfPlotter
21+
from ansys.dpf.core import examples
22+
23+
###############################################################################
24+
# Next, open an example and print out the ``model`` object. The
25+
# :class:`Model <ansys.dpf.core.model.Model> class helps to organize access
26+
# methods for the result by keeping track of the operators and data sources
27+
# used by the result file.
28+
#
29+
# Printing the model displays:
30+
#
31+
# - Analysis type
32+
# - Available results
33+
# - Size of the mesh
34+
# - Number of results
35+
# - Unit
36+
#
37+
path = examples.download_hemisphere()
38+
model = dpf.Model(path)
39+
print(model)
40+
###############################################################################
41+
# Define the `node_id` normal to which a stress gradient should be plotted.
42+
#
43+
node_id = 1928
44+
###############################################################################
45+
# The following command prints the mesh unit
46+
#
47+
unit = model.metadata.meshed_region.unit
48+
print("Unit: %s" % unit)
49+
###############################################################################
50+
# `depth` defines the path length / depth to which the path will penetrate.
51+
# While defining `depth` make sure you use the correct mesh unit.
52+
# `delta` defines distance between consecutive points on the path.
53+
depth = 10 # in mm
54+
delta = 0.1 # in mm
55+
###############################################################################
56+
# Get the meshed region
57+
#
58+
mesh = model.metadata.meshed_region
59+
###############################################################################
60+
# Get Equivalent stress fields container.
61+
#
62+
stress_fc = model.results.stress().eqv().outputs.fields_container()
63+
###############################################################################
64+
# Define Nodal scoping.
65+
# Make sure to define ``"Nodal"`` as the requested location, important for the
66+
# `normals` operator.
67+
#
68+
nodal_scoping = dpf.Scoping(location=dpf.locations.nodal)
69+
nodal_scoping.ids = [node_id]
70+
###############################################################################
71+
# Get Skin Mesh because `normals` operator requires Shells as input.
72+
#
73+
skin_mesh = ops.mesh.skin(mesh=mesh)
74+
skin_meshed_region = skin_mesh.outputs.mesh.get_data()
75+
###############################################################################
76+
# Get normal at a node using `normals` operator.
77+
#
78+
normal = ops.geo.normals()
79+
normal.inputs.mesh.connect(skin_meshed_region)
80+
normal.inputs.mesh_scoping.connect(nodal_scoping)
81+
normal_vec_out_field = normal.outputs.field.get_data()
82+
###############################################################################
83+
# Normal vector is along the surface normal. We need to invert the vector
84+
# using `math.scale` operator inwards in the geometry, to get the path
85+
# direction.
86+
#
87+
normal_vec_in_field = ops.math.scale(field=normal_vec_out_field,
88+
ponderation=-1.0)
89+
normal_vec_in = normal_vec_in_field.outputs.field.get_data().data[0]
90+
###############################################################################
91+
# Get Nodal coordinates, they serve as the first point on the line.
92+
#
93+
node = mesh.nodes.node_by_id(node_id)
94+
line_fp = node.coordinates
95+
###############################################################################
96+
# Create 3D line equation.
97+
#
98+
fx = lambda t: line_fp[0] + normal_vec_in[0] * t
99+
fy = lambda t: line_fp[1] + normal_vec_in[1] * t
100+
fz = lambda t: line_fp[2] + normal_vec_in[2] * t
101+
###############################################################################
102+
# Create coordinates using 3D line equation-
103+
#
104+
coordinates = [[fx(t * delta), fy(t * delta), fz(t * delta)] for t in
105+
range(int(depth / delta))]
106+
flat_coordinates = [entry for data in coordinates for entry in data]
107+
###############################################################################
108+
# Create Field for coordinates of the path.
109+
#
110+
field_coord = dpf.fields_factory.create_3d_vector_field(len(coordinates))
111+
field_coord.data = flat_coordinates
112+
field_coord.scoping.ids = list(range(1, len(coordinates) + 1))
113+
###############################################################################
114+
# Let's now map results on the path.
115+
mapping_operator = ops.mapping.on_coordinates(
116+
fields_container=stress_fc,
117+
coordinates=field_coord,
118+
create_support=True,
119+
mesh=mesh)
120+
fields_mapped = mapping_operator.outputs.fields_container()
121+
###############################################################################
122+
# Here, we request the mapped field data and its mesh
123+
field_m = fields_mapped[0]
124+
mesh_m = field_m.meshed_region
125+
###############################################################################
126+
# Create stress vs length chart.
127+
#
128+
x_initial = 0.0
129+
length = [x_initial + delta * index for index in range(len(field_m.data))]
130+
plt.plot(length, field_m.data, "r")
131+
plt.xlabel("Length (%s)" % mesh.unit)
132+
plt.ylabel("Stress (%s)" % field_m.unit)
133+
plt.show()
134+
###############################################################################
135+
# To create a plot we need to add both the meshes
136+
# `mesh_m` - mapped mesh
137+
# `mesh` - original mesh
138+
pl = DpfPlotter()
139+
pl.add_field(field_m, mesh_m)
140+
pl.add_mesh(mesh, style="surface", show_edges=True,
141+
color="w", opacity=0.3)
142+
pl.show_figure(show_axes=True, cpos=[
143+
(62.687, 50.119, 67.247),
144+
(5.135, 6.458, -0.355),
145+
(-0.286, 0.897, -0.336)])

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
install_requires = ["psutil", "progressbar2", "numpy", "ansys.grpc.dpf>=0.2.3"]
99

10-
1110
# Get version from version info
1211
filepath = os.path.dirname(__file__)
1312
__version__ = None

0 commit comments

Comments
 (0)