{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "raw_mimetype": "text/latex"
   },
   "source": [
    "Contenido bajo licencia Creative Commons BY 4.0 y código bajo licencia MIT. © Juan Gómez y Nicolás Guarín-Zapata 2020. Este material es parte del curso Modelación Computacional en el programa de Ingeniería Civil de la Universidad EAFIT."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Cerchas planas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introducción"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En este Notebook se parte del programa de resortes simple discutido anteriormente y tras realizar cambios menores en algunos parámetros este se modifica para que resuelva estructuras correspondientes a cerchas planas. Además se introducen los conceptos de sistemas de referencia locales a los elementos y global para la estructura así como la relación entre ambos.\n",
    "\n",
    "**Al completar este notebook usted debería estar en la capacidad de:**\n",
    "\n",
    "* Entender el algoritmo de solución de estructuras como un proceso general de ensamblaje de rigideces en un sistema global de ecuaciones.\n",
    "\n",
    "* Reconocer la diferencia entre los sistemas de referencia local (propio de cada elemento) y global (para toda la estructura) y la necesidad de expresar las rigideces en único sistema de referencia.\n",
    "\n",
    "* Expresar relaciones fuerza-desplazamiento en los sistemas de referencia local y global.\n",
    "\n",
    "* Reconocer las modificaciones necesarias para convertir un programa fundamental de ensamblaje de resortes en uno para estructuras mas complejas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ensamblaje de elementos cercha"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En lo que sigue se usarán las siguientes condiciones.\n",
    "\n",
    "* Se asumirán estructuras correspondientes a ensamblajes de elementos barra conectados por articulaciones en sus uniones.\n",
    "\n",
    "* Por sus condiciones geométricas se asume que los elementos solo tienen rigidez axial y que por ende estos pueden entenderse como equivalentes a resortes de rigidez $k=\\frac{AE}{l}$.\n",
    "\n",
    "La siguiente figura muestra un elemento típico. El eje de referencia $x$ dispuesto en la dirección longitudinal del elemento representa el sistema **local** de referencia. Nótese que si se estudia el elemento en sus sistema local este es completamente equivalente al resorte discutido anteriormente.\n",
    "\n",
    "<center>\n",
    "    <img src=\"img/cercha_local.svg\"\n",
    "         alt=\"Desplazamientos en el sistema de referencia local\"\n",
    "         style=\"width:400px\">\n",
    "</center>\n",
    "\n",
    "Usando $k = \\frac{AE}{l}$ se tiene que la relación fuerza-desplazamiento en el sistema local de referencia es de la forma:\n",
    "\n",
    "$$\n",
    "\\begin{Bmatrix}f_1\\\\f_2\\end{Bmatrix} = \\frac{AE}{l}\n",
    "\\begin{bmatrix}\n",
    "1&-1\\\\\n",
    "-1&1\\end{bmatrix}\n",
    "\\begin{Bmatrix}u_1\\\\u_2\\end{Bmatrix}\\, .\n",
    "$$\n",
    "\n",
    "Considere ahora la siguiente estructura o ensamblaje conformado por 2 elementos tipo barra. Asumiendo que se conocen las relaciones fuerza-desplazamiento para cada una de las barras de la cercha se requiere determinar el desplazamiento del vértice superior de la cercha y las fuerzas internas en los elementos.\n",
    "\n",
    "<center>\n",
    "    <img src=\"img/cercha_ejemplo.svg\"\n",
    "         alt=\"files\"\n",
    "         style=\"width:500px\">\n",
    "</center>\n",
    "\n",
    "Nótese que además del sistema de referencia local de cada elemento ahora se tiene un sistema de referencia único denotado como $X-Y$ y denominado el sistema de referencia **Global**.\n",
    "\n",
    "Para obtener la rigidez total de la estructura es necesario considerar la contribución de todos los elementos en el sistema de referencia de la cercha. Será necesario entonces transformar la matriz de rigidez de cada elemento del sistema local al sistema común o global. Para esto es conveniente definir:\n",
    "\n",
    "* $U, F$ : Desplazamientos (o grados de libertad) y fuerzas en el sistema de referencia global.\n",
    "\n",
    "* $u, f$ : Desplazamientos (o grados de libertad) y fuerzas en el sistema de referencia local.\n",
    "\n",
    "Estas variables se relacionan mediante la matriz de rotación $\\lambda$ de acuerdo con;\n",
    "\n",
    "$$u=\\lambda U\\, .$$\n",
    "\n",
    "Suponiendo que aplicamos ahora un desplazamiento $\\delta u$ y determinamos el trabajo de las fuerzas a lo largo de este desplazamiento de acuerdo con:\n",
    "\n",
    "$$\n",
    "W = \\delta u^T f\\, .\n",
    "$$\n",
    "\n",
    "Ahora, dado que el trabajo es una cantidad escalar y por lo tanto independiente del sistema de referencia es válido escribir:\n",
    "\n",
    "$$W = \\delta U^T F=\\delta u^T f\\, .$$\n",
    "\n",
    "Usando la ecuación de transformación bajo rotación en esta última se obtiene:\n",
    "\n",
    "$$\\delta U^T F=\\delta U^T \\lambda^T f$$\n",
    "\n",
    "de donde se concluye que\n",
    "\n",
    "$$F =\\lambda^T f\\, .$$\n",
    "\n",
    "Si ahora se consideran las relaciones fuerza-desplazamiento\n",
    "\n",
    "$$f = ku\\, ,$$\n",
    "\n",
    "donde $k$ es la matriz de rigidez local se tiene que podemos escribir:\n",
    "\n",
    "\\begin{align}\n",
    "&\\lambda^Tf = \\lambda^T k u\\\\\n",
    "&\\lambda^Tf = \\lambda^T k \\lambda U\\\\\n",
    "&F = KU\n",
    "\\end{align}\n",
    "\n",
    "de donde se obtiene la ecuación de transformación bajo rotación de las relaciones fuerza-desplazamiento en un sistema de referencia local y un sistema global\n",
    "\n",
    "$$K=\\lambda^T k\\lambda\\, .$$\n",
    "\n",
    "Es importante observar que en el sistema global el elemento parece tener 2 grados de libertad por nodo mientras que en el sistema local solo existe el desplazamiento axial.\n",
    "\n",
    "La información requerida para calcular $K$ es entregada como parámetros de entrada a la función `uel` como se describe a continuación.\n",
    "\n",
    "<div class=\"alert alert-warning\">\n",
    "\n",
    "Encuentre la matriz de transformación bajo rotación $\\lambda$ requerida para la formulación de la matriz de rigidez en el sistema de referencia global.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejemplo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las modificaciones que se deben aplicar al programa de resortes son aquellas relacionadas con el hecho de tener 2 grados de libertad por nodo.\n",
    "\n",
    "(Los archivos de texto con los datos de entrada para las partículas, elementos, coeficientes de rigidez y cargas de este problema están almacenados en la carpeta `files` de este repositorio)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lea los archivos de entrada de la carpeta `files`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def readin():\n",
    "    nodes = np.loadtxt('files/Cnodes.txt', ndmin=2)\n",
    "    mats = np.loadtxt('files/Cmater.txt', ndmin=2)\n",
    "    elements = np.loadtxt('files/Celes.txt', ndmin=2)\n",
    "    loads = np.loadtxt('files/Cloads.txt', ndmin=2)\n",
    "    return nodes, mats, elements, loads"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La subrutina `eqcounter` cuenta ecuaciones y genera el arreglo de condiciones de frontera."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def eqcounter(nodes):\n",
    "    nnodes = nodes.shape[0]\n",
    "    IBC = np.zeros((nnodes, 2), dtype=np.integer)\n",
    "    for node in range(nnodes):\n",
    "        for dof in range(2):\n",
    "            IBC[node, dof] = int(nodes[node, dof + 3])\n",
    "    neq = 0\n",
    "    for node in range(nnodes):\n",
    "        for dof in range(2):\n",
    "            if IBC[node, dof] == 0:\n",
    "                IBC[node, dof] = neq\n",
    "                neq = neq + 1\n",
    "    return neq, IBC"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora la función `DME` calcula la matriz de ensamblaje de ecuaciones."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def DME(nodes, elements):\n",
    "    nels = elements.shape[0]\n",
    "    IELCON = np.zeros((nels, 2), dtype=np.integer)\n",
    "    DME = np.zeros((nels, 4), dtype=np.integer)\n",
    "    neq, IBC = eqcounter(nodes)\n",
    "    ndof = 4\n",
    "    nnodes = 2\n",
    "    for ele in range(nels):\n",
    "        for node in range(nnodes):\n",
    "            IELCON[ele, node] = elements[ele, node + 3]\n",
    "            glob_num = IELCON[ele, node]\n",
    "            for loc_num in range(2):\n",
    "                DME[ele, 2*node + loc_num] = IBC[glob_num, loc_num]\n",
    "    return DME, IBC, neq"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La función `assembly` usa el modelo y la matriz `DME` para calcular la matriz de rigidez global."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def assembly(elements, mats, nodes, neq, DME):\n",
    "    IELCON = np.zeros((2), dtype=np.integer)\n",
    "    KG = np.zeros((neq, neq))\n",
    "    nels = elements.shape[0]\n",
    "    nnodes = 2\n",
    "    ndof = 4\n",
    "    for el in range(nels):\n",
    "        elcoor = np.zeros((nnodes, 2))\n",
    "        im = np.int(elements[el , 2])\n",
    "        par0 = mats[im, 0]\n",
    "        par1 = mats[im, 1]\n",
    "        for j in range(nnodes):\n",
    "            IELCON[j] = elements[el , j+3]\n",
    "            elcoor[j , 0] = nodes[IELCON[j], 1]\n",
    "            elcoor[j , 1] = nodes[IELCON[j], 2]\n",
    "        kloc = ueltruss2D(elcoor, par0, par1)\n",
    "        dme = DME[el, :ndof]\n",
    "        for row in range(ndof):\n",
    "            glob_row = dme[row]\n",
    "            if glob_row != -1:\n",
    "                for col in range(ndof):\n",
    "                    glob_col = dme[col]\n",
    "                    if glob_col != -1:\n",
    "                        KG[glob_row, glob_col] = KG[glob_row, glob_col] +\\\n",
    "                                                 kloc[row, col]\n",
    "    return KG"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La rutina `ueltruss2D` usa las coordenadas de los nudos y los parámetros de material para calcular la matriz de rigidez local ya transformada al sistema de referencia global.\n",
    "\n",
    "\n",
    "<div class=\"alert alert-warning\">\n",
    "    \n",
    "Agregue un comentario a cada línea relevante de los códigos de las siguientes rutinas y úselos para escribir los pseudocódigos correspondientes. En particular identifique el calculo de la matriz de transformación bajo rotación $\\lambda$.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def ueltruss2D(coord, A, Emod):\n",
    "    vec = coord[1, :] - coord[0, :]\n",
    "    length = np.linalg.norm(vec)\n",
    "    nx = vec[0]/length\n",
    "    ny = vec[1]/length\n",
    "    Q = np.array([\n",
    "        [nx, ny , 0 , 0],\n",
    "        [0,  0, nx , ny]])\n",
    "    kl = (A*Emod/length) * np.array([\n",
    "        [1, -1],\n",
    "        [-1, 1]])\n",
    "    kG = Q.T @ kl @ Q\n",
    "    return kG"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La rutina `loadassem` forma el vector de cargas en los nudos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def loadasem(loads, IBC, neq, nl):\n",
    "    RHSG = np.zeros([neq])\n",
    "    for cont in range(nl):\n",
    "        il = int(loads[cont, 0])\n",
    "        ilx = IBC[il , 0]\n",
    "        ily = IBC[il , 1]\n",
    "        if ilx != -1:\n",
    "            RHSG[ilx] = loads[cont, 1]\n",
    "        if ily != -1:\n",
    "            RHSG[ily] = loads[cont, 2]\n",
    "    return RHSG"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El programa principal mantiene la misma estructura que el programa de resortes, es decir, se tienen siguientes pasos:\n",
    "\n",
    "* Lee el modelo.\n",
    "\n",
    "* Determina la matriz de ensamblaje `DME`.\n",
    "\n",
    "* Ensambla el sistema global de ecuaciones.\n",
    "\n",
    "* Determina los desplazamientos globales `UG` tras resolver el sistema global."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0.         -0.15018948]\n"
     ]
    }
   ],
   "source": [
    "nodes, mats, elements, loads = readin()\n",
    "DME, IBC, neq = DME(nodes, elements)\n",
    "KG = assembly(elements, mats, nodes, neq, DME)\n",
    "RHSG = loadasem(loads, IBC, neq, 1)\n",
    "UG = np.linalg.solve(KG, RHSG)\n",
    "print(UG)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Problemas propuestos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-warning\">\n",
    "\n",
    "#### Problema 1\n",
    "\n",
    "Implemente una función que calcule las fuerzas nodales en cada elemento y que verifique el equilibrio del sistema.\n",
    "\n",
    "#### Problema 2\n",
    "\n",
    "Determine la rigidez lateral de la estructura usando la relación:\n",
    "\n",
    "$$k = \\frac{P}{\\delta}\\, .$$\n",
    "\n",
    "#### Problema 3\n",
    "\n",
    "Introduzca las modificaciones necesarias a los archivos de entrada para adicionar una barra adicional al modelo y que conecte los nodos $0$ y $1$. Luego use este nuevo modelo para determinar los desplazamientos. Comente los resultados.\n",
    "\n",
    "#### Problema 4\n",
    "\n",
    "Repare la cercha mostrada en la figura adicionando elementos o imponiendo restricciones apropiadas a los desplazamientos. (Cree un nuevo paquete de archivos de datos).\n",
    "\n",
    "<center>\n",
    "    <img src=\"img/cercha_ejercicio.svg\"\n",
    "         alt=\"Cercha para el ejercicio\"\n",
    "         style=\"width:400px\">\n",
    "</center>\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Referencias"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Klaus-Jürgen Bathe (2006). Finite element procedures. Klaus-Jurgen Bathe. Prentice Hall International.\n",
    "\n",
    "* Juan Gómez, Nicolás Guarín-Zapata (2018). SolidsPy: 2D-Finite Element Analysis with Python, <https://github.com/AppliedMechanics-EAFIT/SolidsPy>."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Formato del notebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La siguiente celda cambia el formato del Notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<link href='http://fonts.googleapis.com/css?family=Fenix' rel='stylesheet' type='text/css'>\n",
       "<link href='http://fonts.googleapis.com/css?family=Alegreya+Sans:100,300,400,500,700,800,900,100italic,300italic,400italic,500italic,700italic,800italic,900italic' rel='stylesheet' type='text/css'>\n",
       "<link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:300,400' rel='stylesheet' type='text/css'>\n",
       "\n",
       "<style>\n",
       "\n",
       "/*\n",
       "Template for Notebooks for Modelación computacional.\n",
       "\n",
       "Based on Lorena Barba template available at:\n",
       "\n",
       "    https://github.com/barbagroup/AeroPython/blob/master/styles/custom.css\n",
       "*/\n",
       "\n",
       "/* Fonts */\n",
       "@font-face {\n",
       "font-family: \"Computer Modern\";\n",
       "src: url('http://mirrors.ctan.org/fonts/cm-unicode/fonts/otf/cmunss.otf');\n",
       "}\n",
       "\n",
       "/* Text */\n",
       "div.cell{\n",
       "width:800px;\n",
       "margin-left:16% !important;\n",
       "margin-right:auto;\n",
       "}\n",
       "h1 {\n",
       "font-family: 'Alegreya Sans', sans-serif;\n",
       "}\n",
       "h2 {\n",
       "font-family: 'Fenix', serif;\n",
       "}\n",
       "h3{\n",
       "font-family: 'Fenix', serif;\n",
       "margin-top:12px;\n",
       "margin-bottom: 3px;\n",
       "}\n",
       "h4{\n",
       "font-family: 'Fenix', serif;\n",
       "}\n",
       "h5 {\n",
       "font-family: 'Alegreya Sans', sans-serif;\n",
       "}\t\n",
       "div.text_cell_render{\n",
       "font-family: 'Alegreya Sans',Computer Modern, \"Helvetica Neue\", Arial, Helvetica, Geneva, sans-serif;\n",
       "line-height: 135%;\n",
       "font-size: 120%;\n",
       "width:600px;\n",
       "margin-left:auto;\n",
       "margin-right:auto;\n",
       "}\n",
       ".CodeMirror{\n",
       "font-family: \"Source Code Pro\";\n",
       "font-size: 90%;\n",
       "}\n",
       "/* .prompt{\n",
       "display: None;\n",
       "}*/\n",
       ".text_cell_render h1 {\n",
       "font-weight: 200;\n",
       "font-size: 50pt;\n",
       "line-height: 100%;\n",
       "color:#CD2305;\n",
       "margin-bottom: 0.5em;\n",
       "margin-top: 0.5em;\n",
       "display: block;\n",
       "}\t\n",
       ".text_cell_render h5 {\n",
       "font-weight: 300;\n",
       "font-size: 16pt;\n",
       "color: #CD2305;\n",
       "font-style: italic;\n",
       "margin-bottom: .5em;\n",
       "margin-top: 0.5em;\n",
       "display: block;\n",
       "}\n",
       ".warning{\n",
       "color: rgb( 240, 20, 20 )\n",
       "}\n",
       "</style>\n",
       "\n",
       "<script>\n",
       "/* Equations */\n",
       "\n",
       "MathJax.Hub.Config({\n",
       "TeX: {\n",
       "extensions: [\"AMSmath.js\"]\n",
       "},\n",
       "tex2jax: {\n",
       "inlineMath: [ ['$','$'], [\"\\\\(\",\"\\\\)\"] ],\n",
       "displayMath: [ ['$$','$$'], [\"\\\\[\",\"\\\\]\"] ]\n",
       "},\n",
       "displayAlign: 'center', // Change this to 'center' to center equations.\n",
       "\"HTML-CSS\": {\n",
       "styles: {'.MathJax_Display': {\"margin\": 4}}\n",
       "}\n",
       "});\n",
       "</script>\n",
       "\n",
       "\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.core.display import HTML\n",
    "def css_styling():\n",
    "    styles = open('./nb_style.css', 'r').read()\n",
    "    return HTML(styles)\n",
    "css_styling()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Raw Cell Format",
  "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.7.1"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}