|
| 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)]) |
0 commit comments