.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gallery/elements/mass21/example_mass21.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_elements_mass21_example_mass21.py: .. _ref_mass21_example: MASS21 — single-DOF spring-mass oscillator ========================================== Two nodes connected by a COMBIN14 spring. A MASS21 element sits on the free node. The first modal frequency is compared to the textbook SDOF result ``ω = √(k / m)``. .. GENERATED FROM PYTHON SOURCE LINES 11-19 .. code-block:: Python from __future__ import annotations import numpy as np import pyvista as pv import femorph_solver .. GENERATED FROM PYTHON SOURCE LINES 20-24 Problem data ------------ ``k = 1 kN/m``, ``m = 0.25 kg`` → ``ω = √(4000) ≈ 63.2456 rad/s`` (``f ≈ 10.065 Hz``). .. GENERATED FROM PYTHON SOURCE LINES 24-29 .. code-block:: Python k = 1000.0 mass = 0.25 omega_expected = np.sqrt(k / mass) f_expected = omega_expected / (2.0 * np.pi) .. GENERATED FROM PYTHON SOURCE LINES 30-36 Build the model --------------- Two element types: COMBIN14 (spring, TYPE 1) and MASS21 (point mass, TYPE 2). Each gets its own REAL set. The ``type`` / ``real`` verbs set the stamps that subsequent ``e`` calls inherit — the same mechanism MAPDL uses. .. GENERATED FROM PYTHON SOURCE LINES 36-59 .. code-block:: Python m = femorph_solver.Model() m.et(1, "COMBIN14") m.r(1, k) m.et(2, "MASS21") m.r(2, mass) m.n(1, 0.0, 0.0, 0.0) m.n(2, 1.0, 0.0, 0.0) m.type(1) m.real(1) m.e(1, 2) # spring from 1 to 2 m.type(2) m.real(2) m.e(2) # point mass at node 2 (single-node element) m.d(1, "ALL") # clamp the spring base m.d(2, "UY") # kill transverse rigid-body modes m.d(2, "UZ") .. rst-class:: sphx-glr-script-out .. code-block:: none /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:38: 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, "COMBIN14") /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:39: DeprecationWarning: Model.r(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).r(real_id, *values)` for line-by-line APDL deck porting, or the native `Model.assign(element, material, real=[...])` for new code. m.r(1, k) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:41: 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(2, "MASS21") /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:42: DeprecationWarning: Model.r(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).r(real_id, *values)` for line-by-line APDL deck porting, or the native `Model.assign(element, material, real=[...])` for new code. m.r(2, mass) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:44: 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(1, 0.0, 0.0, 0.0) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:45: 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(2, 1.0, 0.0, 0.0) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:47: DeprecationWarning: Model.type(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).type(et_id)` for line-by-line APDL deck porting, or the native `Model.from_grid(pv_grid)` for new code. m.type(1) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:48: DeprecationWarning: Model.real(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).real(real_id)` for line-by-line APDL deck porting, or the native `Model.assign("HEX8", material, real=[...])` for new code. m.real(1) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:49: 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(1, 2) # spring from 1 to 2 /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:51: DeprecationWarning: Model.type(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).type(et_id)` for line-by-line APDL deck porting, or the native `Model.from_grid(pv_grid)` for new code. m.type(2) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:52: DeprecationWarning: Model.real(...) is a MAPDL-dialect shortcut and has moved off the Model public surface. Use `APDL(model).real(real_id)` for line-by-line APDL deck porting, or the native `Model.assign("HEX8", material, real=[...])` for new code. m.real(2) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:53: 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(2) # point mass at node 2 (single-node element) /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:55: 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, "ALL") # clamp the spring base /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:56: 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(2, "UY") # kill transverse rigid-body modes /home/runner/_work/solver/solver/examples/elements/mass21/example_mass21.py:57: 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(2, "UZ") .. GENERATED FROM PYTHON SOURCE LINES 60-64 Modal solve + analytical comparison ----------------------------------- Only one physical DOF is free (UX at node 2). ``modal_solve`` returns a single positive eigenvalue whose square root is ω. .. GENERATED FROM PYTHON SOURCE LINES 64-72 .. code-block:: Python res = m.modal_solve(n_modes=1) omega_computed = float(np.sqrt(res.omega_sq[0])) f_computed = float(res.frequency[0]) print(f"Expected ω = {omega_expected:.6f} rad/s, f = {f_expected:.6f} Hz") print(f"Computed ω = {omega_computed:.6f} rad/s, f = {f_computed:.6f} Hz") assert np.isclose(omega_computed, omega_expected, rtol=1e-10) .. rst-class:: sphx-glr-script-out .. code-block:: none Expected ω = 63.245553 rad/s, f = 10.065842 Hz Computed ω = 63.245553 rad/s, f = 10.065842 Hz .. GENERATED FROM PYTHON SOURCE LINES 73-79 Plot the mode shape ------------------- Dilate the mode shape for visualisation. With MASS21 being a single-node element the mesh contains a VTK_LINE (the spring) and a VTK_VERTEX (the mass) — pyvista happily draws the combined unstructured grid. .. GENERATED FROM PYTHON SOURCE LINES 79-105 .. code-block:: Python dof = m.dof_map() mode = res.mode_shapes[:, 0] grid = m.grid.copy() displacement = np.zeros((grid.n_points, 3), dtype=np.float64) for i, nn in enumerate(grid.point_data["ansys_node_num"]): rows = np.where(dof[:, 0] == int(nn))[0] for r in rows: displacement[i, int(dof[r, 1])] = mode[r] # Scale so the peak is 0.3 m for clarity. peak = float(np.max(np.abs(displacement))) or 1.0 displacement *= 0.3 / peak grid.point_data["mode1"] = displacement warped = grid.warp_by_vector("mode1", factor=1.0) plotter = pv.Plotter(off_screen=True) plotter.add_mesh(grid, style="wireframe", color="gray", line_width=3) plotter.add_mesh( warped, color="tomato", line_width=6, render_points_as_spheres=True, point_size=18, ) plotter.add_text(f"f1 = {f_computed:.3f} Hz", font_size=12) plotter.add_axes() plotter.show() .. image-sg:: /gallery/elements/mass21/images/sphx_glr_example_mass21_001.png :alt: example mass21 :srcset: /gallery/elements/mass21/images/sphx_glr_example_mass21_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.542 seconds) .. _sphx_glr_download_gallery_elements_mass21_example_mass21.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: example_mass21.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: example_mass21.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: example_mass21.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_