{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "# 3D Solid Mechanics\n",
    "\n",
    "* Open Cascade Techonology geometry kernel\n",
    "* Netgen mesh generator\n",
    "* NGSolve Finite Element libraray"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from netgen.occ import *\n",
    "from ngsolve import *\n",
    "from ngsolve.webgui import Draw "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2",
   "metadata": {},
   "outputs": [],
   "source": [
    "box = Box((0,0,0), (3,0.6,1))\n",
    "box.faces.name=\"outer\"\n",
    "cyl = sum( [Cylinder((0.5+i,0,0.5), Y, 0.25,0.8) for i in range(3)] )\n",
    "cyl.faces.name=\"cyl\"\n",
    "geo = box-cyl\n",
    "\n",
    "ea = { \"euler_angles\" : [-70,5,30] }\n",
    "Draw(geo, **ea);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3",
   "metadata": {},
   "source": [
    "find edges between box and cylinder, and build chamfers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {
    "tags": [
     "raises-exception"
    ]
   },
   "outputs": [],
   "source": [
    "cylboxedges = geo.faces[\"outer\"].edges * geo.faces[\"cyl\"].edges\n",
    "cylboxedges.name = \"cylbox\"\n",
    "geo = geo.MakeChamfer(cylboxedges, 0.03)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5",
   "metadata": {},
   "source": [
    "name faces for boundary conditions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {},
   "outputs": [],
   "source": [
    "geo.faces.Min(X).name = \"fix\"\n",
    "geo.faces.Max(X).name = \"force\"\n",
    "\n",
    "Draw(geo, **ea);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7",
   "metadata": {},
   "outputs": [],
   "source": [
    "mesh = geo.GenerateMesh(maxh=0.1).Curve(3)\n",
    "Draw (mesh, **ea);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8",
   "metadata": {},
   "source": [
    "## Linear elasticity\n",
    "\n",
    "Displacement: $u : \\Omega \\rightarrow {\\mathbb R}^3$\n",
    "\n",
    "Linear strain:\n",
    "\n",
    "$$\n",
    "\\varepsilon(u) := \\tfrac{1}{2} ( \\nabla u + (\\nabla u)^T )\n",
    "$$\n",
    "\n",
    "Stress by Hooke's law:\n",
    "\n",
    "$$\n",
    "\\sigma = 2 \\mu \\varepsilon + \\lambda \\operatorname{tr} \\varepsilon I\n",
    "$$\n",
    "\n",
    "Equilibrium of forces:\n",
    "\n",
    "$$\n",
    "\\operatorname{div} \\sigma = f\n",
    "$$\n",
    "\n",
    "Displacement boundary conditions:\n",
    "\n",
    "$$\n",
    "u = u_D \\qquad \\text{on} \\, \\Gamma_D\n",
    "$$\n",
    "\n",
    "Traction boundary conditions:\n",
    "\n",
    "$$\n",
    "\\sigma n = g \\qquad \\text{on} \\, \\Gamma_N\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9",
   "metadata": {},
   "source": [
    "## Variational formulation:\n",
    " \n",
    "Find: $u \\in H^1(\\Omega)^3$ such that $u = u_D$ on $\\Gamma_D$\n",
    "\n",
    "$$\n",
    "\\int_\\Omega \\sigma(\\varepsilon(u)) : \\varepsilon(v) \\, dx = \\int_\\Omega f v dx + \\int_{\\Gamma_N} g v ds\n",
    "$$\n",
    "\n",
    "holds for all $v = 0$ on $\\Gamma_D$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {},
   "outputs": [],
   "source": [
    "E, nu = 210, 0.2\n",
    "mu  = E / 2 / (1+nu)\n",
    "lam = E * nu / ((1+nu)*(1-2*nu))\n",
    "\n",
    "def Stress(strain):\n",
    "    return 2*mu*strain + lam*Trace(strain)*Id(3)    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11",
   "metadata": {},
   "outputs": [],
   "source": [
    "fes = VectorH1(mesh, order=3, dirichlet=\"fix\")\n",
    "u,v = fes.TnT()\n",
    "gfu = GridFunction(fes)\n",
    "\n",
    "with TaskManager():\n",
    "    a = BilinearForm(InnerProduct(Stress(Sym(Grad(u))), Sym(Grad(v))).Compile()*dx)\n",
    "    pre = preconditioners.BDDC(a)\n",
    "    a.Assemble()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": [
    "force = CF( (1e-3,0,0) )\n",
    "f = LinearForm(force*v*ds(\"force\")).Assemble()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13",
   "metadata": {},
   "outputs": [],
   "source": [
    "inv = solvers.CGSolver(a.mat, pre, plotrates=True, tol=1e-8)\n",
    "gfu.vec.data = inv * f.vec"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14",
   "metadata": {},
   "outputs": [],
   "source": [
    "with TaskManager():\n",
    "    fesstress = MatrixValued(H1(mesh,order=3), symmetric=True)\n",
    "    gfstress = GridFunction(fesstress)\n",
    "    gfstress.Interpolate (Stress(Sym(Grad(gfu))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15",
   "metadata": {},
   "outputs": [],
   "source": [
    "Draw (gfu, mesh, deformation=True, scale=3e4, draw_vol=False, **ea);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16",
   "metadata": {},
   "outputs": [],
   "source": [
    "Draw (Norm(gfstress), mesh, deformation=1e4*gfu, draw_vol=False, order=3, **ea);"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.13.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
