{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import pyvista\npyvista.OFF_SCREEN = True\npyvista.set_jupyter_backend('static')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# BEAM2 reference geometry \u2014 Hermite cubic shape functions\n\nThe 2-node 3D beam element (Euler-Bernoulli slender-beam limit)\nmaps to the natural-coordinate segment $s \\in [-1, +1]$.\nTranslations and torsion use linear shape functions; transverse\ndisplacement and slope use **Hermite cubics** so $C^1$\ncontinuity holds across element boundaries.\n\nLinear shape functions (axial + torsion):\n\n\\begin{align}N_1^L(s) = \\tfrac{1 - s}{2}, \\qquad\n    N_2^L(s) = \\tfrac{1 + s}{2}.\\end{align}\n\nHermite cubic shape functions (transverse displacement and\nslope, mapped to the physical length $L$):\n\n\\begin{align}H_1(\\xi) = 2\\xi^{3} - 3\\xi^{2} + 1, \\quad\n    H_2(\\xi) = L\\,(\\xi^{3} - 2\\xi^{2} + \\xi),\\end{align}\n\n\\begin{align}H_3(\\xi) = -2\\xi^{3} + 3\\xi^{2}, \\quad\n    H_4(\\xi) = L\\,(\\xi^{3} - \\xi^{2}),\\end{align}\n\nwith $\\xi = (s + 1)/2 \\in [0, 1]$.  ``H_1`` and ``H_3``\ninterpolate the two nodal displacements; ``H_2`` and ``H_4``\ninterpolate the two nodal slopes.\n\n## References\n* Cook, R. D., Malkus, D. S., Plesha, M. E., Witt, R. J. (2002)\n  *Concepts and Applications of Finite Element Analysis*, 4th\n  ed., Wiley, \u00a72.4\u2013\u00a72.6, Table 16.3-1.\n* Zienkiewicz, O. C., Taylor, R. L. (2013) *The Finite Element\n  Method: Its Basis and Fundamentals*, 7th ed., \u00a72.5.1\n  eqs. (2.26)\u2013(2.27).\n* Przemieniecki, J. S. (1968) *Theory of Matrix Structural\n  Analysis*, McGraw-Hill, \u00a75.\n\nImplementation: :class:`femorph_solver.elements.beam2.Beam2`.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from __future__ import annotations\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pyvista as pv"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Hermite cubic basis on [0, 1]\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "L = 1.0  # element length\nxi = np.linspace(0.0, 1.0, 200)\nH1 = 2.0 * xi**3 - 3.0 * xi**2 + 1.0\nH2 = L * (xi**3 - 2.0 * xi**2 + xi)\nH3 = -2.0 * xi**3 + 3.0 * xi**2\nH4 = L * (xi**3 - xi**2)\n\nfig, ax = plt.subplots(1, 1, figsize=(6.4, 4.0))\nax.plot(xi, H1, label=\"$H_1$ \u2014 node-1 displacement\", color=\"#1f77b4\", lw=2)\nax.plot(xi, H2, label=\"$H_2$ \u2014 node-1 slope ($\\\\theta_1$)\", color=\"#ff7f0e\", lw=2)\nax.plot(xi, H3, label=\"$H_3$ \u2014 node-2 displacement\", color=\"#2ca02c\", lw=2)\nax.plot(xi, H4, label=\"$H_4$ \u2014 node-2 slope ($\\\\theta_2$)\", color=\"#d62728\", lw=2)\nax.axhline(0.0, color=\"black\", lw=0.5)\nax.axhline(1.0, color=\"grey\", lw=0.5, ls=\":\")\nax.scatter([0.0, 1.0], [0.0, 0.0], color=\"black\", zorder=5)\nax.set_xlabel(r\"$\\xi = (s + 1) / 2$\")\nax.set_ylabel(\"$H_i(\\\\xi)$\")\nax.set_title(\"BEAM2 Hermite cubic shape functions ($L = 1$)\")\nax.legend(loc=\"upper center\", ncol=2, fontsize=9, framealpha=0.95)\nax.set_xlim(0.0, 1.0)\nax.set_ylim(-0.2, 1.05)\nax.grid(True, ls=\":\", alpha=0.5)\nfig.tight_layout()\nfig.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Sanity \u2014 boundary conditions of the Hermite basis\n\nThe four Hermite cubics satisfy a Kronecker-delta-like property\nat the two endpoints $\\xi = 0$ (node 1) and\n$\\xi = 1$ (node 2):\n\n+-------+--------+--------+--------+--------+\n|       | H1     | H2     | H3     | H4     |\n+-------+--------+--------+--------+--------+\n|  \u03be=0  |  1     |  0     |  0     |  0     |\n|  H'   |  0     |  L     |  0     |  0     |\n|  \u03be=1  |  0     |  0     |  1     |  0     |\n|  H'   |  0     |  0     |  0     |  L     |\n+-------+--------+--------+--------+--------+\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "vals = np.array([[H1[0], H2[0], H3[0], H4[0]], [H1[-1], H2[-1], H3[-1], H4[-1]]])\nnp.testing.assert_allclose(vals[0], [1, 0, 0, 0], atol=1e-12)\nnp.testing.assert_allclose(vals[1], [0, 0, 1, 0], atol=1e-12)\nprint(\"OK \u2014 Hermite cubics interpolate displacements and slopes at the nodes.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2-node beam reference (s \u2208 [-1, +1])\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "cells = np.array([2, 0, 1])\ncell_types = np.array([pv.CellType.LINE], dtype=np.uint8)\nbeam = pv.UnstructuredGrid(cells, cell_types, np.array([[-1.0, 0.0, 0.0], [+1.0, 0.0, 0.0]]))\n\n# 2-point Gauss-Legendre on [-1, +1].\ng = 1.0 / np.sqrt(3.0)\ngauss = np.array([[-g, 0.0, 0.0], [+g, 0.0, 0.0]])\n\nplotter = pv.Plotter(off_screen=True, window_size=(640, 240))\nplotter.add_mesh(beam, color=\"black\", line_width=4)\nplotter.add_points(\n    np.array([[-1.0, 0.0, 0.0], [+1.0, 0.0, 0.0]]),\n    render_points_as_spheres=True,\n    point_size=18,\n    color=\"black\",\n    label=\"end nodes (2)\",\n)\nplotter.add_points(\n    gauss,\n    render_points_as_spheres=True,\n    point_size=14,\n    color=\"#d62728\",\n    label=\"2-pt Gauss-Legendre\",\n)\nplotter.view_xy()\nplotter.camera.zoom(1.6)\nplotter.show()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}