.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gallery/post-processing/example_strain_recovery.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_gallery_post-processing_example_strain_recovery.py: .. _ref_solid185_strain_example: SOLID185 — elastic-strain post-processing ========================================= Solve a SOLID185 flat plate under uniaxial tension and recover the full 6-component elastic-strain tensor on the mesh with :meth:`femorph_solver.Model.eel` — the MAPDL ``S,EPEL`` equivalent. ``Model.eel(u)`` returns the nodal-averaged Voigt strain ``(n_nodes, 6)`` (``PLNSOL``-style, default) or the per-element dict ``{elem_num: (n_nodes_in_elem, 6)}`` (``PLESOL``-style) when called with ``nodal_avg=False``. Strain is computed at each element's own nodes as :math:`\varepsilon(\xi_\text{node}) = B(\xi_\text{node})\cdot u_e` — no RST round-trip, no disk write. .. GENERATED FROM PYTHON SOURCE LINES 18-26 .. code-block:: Python from __future__ import annotations import numpy as np import pyvista as pv import femorph_solver .. GENERATED FROM PYTHON SOURCE LINES 27-34 Problem setup ------------- A 1 m × 0.4 m × 0.05 m steel plate meshed as a 20 × 8 × 1 SOLID185 brick (160 elements). The ``x = 0`` face is held in ``UX`` (symmetry), a single pin at the origin kills the ``UY`` / ``UZ`` rigid-body modes, and the ``x = LX`` face is pulled by a total force ``F`` split over its corner nodes. .. GENERATED FROM PYTHON SOURCE LINES 34-70 .. code-block:: Python E = 2.1e11 # Pa NU = 0.30 RHO = 7850.0 LX, LY, LZ = 1.0, 0.4, 0.05 NX, NY, NZ = 20, 8, 1 F_TOTAL = 1.0e5 # N xs = np.linspace(0.0, LX, NX + 1) ys = np.linspace(0.0, LY, NY + 1) zs = np.linspace(0.0, LZ, NZ + 1) xx, yy, zz = np.meshgrid(xs, ys, zs, indexing="ij") points = np.stack([xx.ravel(), yy.ravel(), zz.ravel()], axis=1) # Hex connectivity in MAPDL / VTK order. def _node_id(i: int, j: int, k: int) -> int: return (i * (NY + 1) + j) * (NZ + 1) + k + 1 # 1-based cells = [] for i in range(NX): for j in range(NY): for k in range(NZ): cells.append( [ _node_id(i, j, k), _node_id(i + 1, j, k), _node_id(i + 1, j + 1, k), _node_id(i, j + 1, k), _node_id(i, j, k + 1), _node_id(i + 1, j, k + 1), _node_id(i + 1, j + 1, k + 1), _node_id(i, j + 1, k + 1), ] ) .. GENERATED FROM PYTHON SOURCE LINES 71-73 Build the femorph-solver model ------------------------------ .. GENERATED FROM PYTHON SOURCE LINES 73-95 .. code-block:: Python m = femorph_solver.Model() m.et(1, "SOLID185") m.mp("EX", 1, E) m.mp("PRXY", 1, NU) m.mp("DENS", 1, RHO) for nn, (x, y, z) in enumerate(points, start=1): m.n(nn, x, y, z) for conn in cells: m.e(*conn) # Symmetry BC: x=0 face clamped in UX; single pin at the origin in UY/UZ. x0_nodes = [nn for nn, p in enumerate(points, start=1) if p[0] < 1e-9] for nn in x0_nodes: m.d(nn, "UX") m.d(1, "UY") m.d(1, "UZ") # Traction on x=LX face: split F_TOTAL over its nodes. x_end_nodes = [nn for nn, p in enumerate(points, start=1) if p[0] > LX - 1e-9] for nn in x_end_nodes: m.f(nn, "FX", F_TOTAL / len(x_end_nodes)) .. rst-class:: sphx-glr-script-out .. code-block:: none /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:74: DeprecationWarning: Model.et(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).et(et_id, name)` for line-by-line APDL deck porting, or the native `Model.assign("HEX8", material)` for new code. m.et(1, "SOLID185") /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:75: DeprecationWarning: Model.mp(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).mp(prop, mat_id, value)` for line-by-line APDL deck porting, or the native `Model.assign(element, {prop: value, ...})` for new code. m.mp("EX", 1, E) /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:76: DeprecationWarning: Model.mp(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).mp(prop, mat_id, value)` for line-by-line APDL deck porting, or the native `Model.assign(element, {prop: value, ...})` for new code. m.mp("PRXY", 1, NU) /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:77: DeprecationWarning: Model.mp(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).mp(prop, mat_id, value)` for line-by-line APDL deck porting, or the native `Model.assign(element, {prop: value, ...})` for new code. m.mp("DENS", 1, RHO) /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:79: DeprecationWarning: Model.n(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).n(num, x, y, z)` for line-by-line APDL deck porting, or the native `Model.from_grid(pv_grid)` for new code. m.n(nn, x, y, z) /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:81: DeprecationWarning: Model.e(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).e(*node_nums)` for line-by-line APDL deck porting, or the native `Model.from_grid(pv_grid)` for new code. m.e(*conn) /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:86: DeprecationWarning: Model.d(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).d(node, label, value)` for line-by-line APDL deck porting, or the native `Model.fix(nodes=..., where=..., dof=...)` for new code. m.d(nn, "UX") /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:87: DeprecationWarning: Model.d(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).d(node, label, value)` for line-by-line APDL deck porting, or the native `Model.fix(nodes=..., where=..., dof=...)` for new code. m.d(1, "UY") /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:88: DeprecationWarning: Model.d(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).d(node, label, value)` for line-by-line APDL deck porting, or the native `Model.fix(nodes=..., where=..., dof=...)` for new code. m.d(1, "UZ") /home/runner/_work/solver/solver/examples/post-processing/example_strain_recovery.py:93: DeprecationWarning: Model.f(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).f(node, label, value)` for line-by-line APDL deck porting, or the native `Model.apply_force(node, fx=..., fy=..., fz=...)` for new code. m.f(nn, "FX", F_TOTAL / len(x_end_nodes)) .. GENERATED FROM PYTHON SOURCE LINES 96-98 Static solve ------------ .. GENERATED FROM PYTHON SOURCE LINES 98-100 .. code-block:: Python res = m.solve() .. GENERATED FROM PYTHON SOURCE LINES 101-106 Recover elastic strain ---------------------- Default call returns nodal-averaged strain of shape ``(n_nodes, 6)``: columns are ``[εxx, εyy, εzz, γxy, γyz, γxz]`` with *engineering* shears (matching MAPDL's ``S,EPEL`` output). .. GENERATED FROM PYTHON SOURCE LINES 106-119 .. code-block:: Python eps = m.eel(res.displacement) print(f"eps shape: {eps.shape}") # Analytical: uniform σxx = F_TOTAL / (LY · LZ), εxx = σ / E, # εyy = εzz = -ν · εxx. sigma_xx = F_TOTAL / (LY * LZ) eps_xx_expected = sigma_xx / E eps_yy_expected = -NU * eps_xx_expected print(f"εxx expected = {eps_xx_expected:.3e}") print(f"εxx recovered (mean over nodes) = {eps[:, 0].mean():.3e}") print(f"εyy recovered (mean) = {eps[:, 1].mean():.3e}") print(f"εyy analytical = {eps_yy_expected:.3e}") .. rst-class:: sphx-glr-script-out .. code-block:: none eps shape: (378, 6) εxx expected = 2.381e-05 εxx recovered (mean over nodes) = 2.416e-05 εyy recovered (mean) = -7.365e-06 εyy analytical = -7.143e-06 .. GENERATED FROM PYTHON SOURCE LINES 120-123 ``nodal_avg=False`` returns per-element arrays keyed by element number — the ``PLESOL`` equivalent. Useful when you want to see jumps at element boundaries or compute element-wise strain norms. .. GENERATED FROM PYTHON SOURCE LINES 123-131 .. code-block:: Python per_elem = m.eel(res.displacement, nodal_avg=False) first_elem = next(iter(per_elem)) print( f"per-element dict has {len(per_elem)} elements; " f"first key = {first_elem}, " f"strain block shape = {per_elem[first_elem].shape}" ) .. rst-class:: sphx-glr-script-out .. code-block:: none per-element dict has 160 elements; first key = 1, strain block shape = (8, 6) .. GENERATED FROM PYTHON SOURCE LINES 132-139 Visualise εxx on the deformed mesh ---------------------------------- :func:`femorph_solver.io.static_result_to_grid` scatters the DOF-indexed displacement vector onto ``(n_points, 3)`` UX/UY/UZ point data in one call — no hand-rolled dof-map loop required. We then paint εxx onto the same grid by mapping the ``eel`` output (indexed by :attr:`femorph_solver.Model.node_numbers`) onto ``ansys_node_num``. .. GENERATED FROM PYTHON SOURCE LINES 139-168 .. code-block:: Python grid = femorph_solver.io.static_result_to_grid(m, res) node_nums = m.node_numbers node_to_idx = {int(nn): i for i, nn in enumerate(node_nums)} point_eps_xx = np.array([eps[node_to_idx[int(nn)], 0] for nn in grid.point_data["ansys_node_num"]]) grid.point_data["eps_xx"] = point_eps_xx warped = grid.warp_by_vector("displacement", factor=200.0) plotter = pv.Plotter(off_screen=True) plotter.add_mesh( grid, style="wireframe", color="gray", line_width=1, opacity=0.4, label="undeformed", ) plotter.add_mesh( warped, scalars="eps_xx", show_edges=True, cmap="viridis", scalar_bar_args={"title": "εxx"}, label="εxx (deformed ×200)", ) plotter.add_legend() plotter.add_axes() plotter.view_xy() plotter.show() .. image-sg:: /gallery/post-processing/images/sphx_glr_example_strain_recovery_001.png :alt: example strain recovery :srcset: /gallery/post-processing/images/sphx_glr_example_strain_recovery_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.246 seconds) .. _sphx_glr_download_gallery_post-processing_example_strain_recovery.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: example_strain_recovery.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: example_strain_recovery.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: example_strain_recovery.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_