├── 01 - FEM Basics 1D.ipynb ├── 02_2D_Shape_Function.ipynb ├── 03 - Heat Equation - large mesh.ipynb ├── 04 - Elasticity.ipynb ├── 04_Iterative_Solver_basic.ipynb ├── 3D7 - Basic FEM - heat equation.ipynb ├── 3D7 - Heat Equation - mesh from file.ipynb ├── 3D7 - Heat Equation - very large mesh.ipynb ├── Gmsh.ipynb ├── README.md └── micromesh.py /01 - FEM Basics 1D.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "kernelspec": { 6 | "name": "python3", 7 | "display_name": "Python 3", 8 | "language": "python" 9 | }, 10 | "language_info": { 11 | "mimetype": "text/x-python", 12 | "nbconvert_exporter": "python", 13 | "name": "python", 14 | "pygments_lexer": "ipython3", 15 | "version": "3.5.4", 16 | "file_extension": ".py", 17 | "codemirror_mode": { 18 | "version": 3, 19 | "name": "ipython" 20 | } 21 | }, 22 | "colab": { 23 | "name": "01 - FEM Basics 1D.ipynb", 24 | "provenance": [], 25 | "include_colab_link": true 26 | }, 27 | "widgets": { 28 | "application/vnd.jupyter.widget-state+json": { 29 | "dfdaf8ee303f43bbbda0a76f09396220": { 30 | "model_module": "@jupyter-widgets/controls", 31 | "model_name": "VBoxModel", 32 | "state": { 33 | "_view_name": "VBoxView", 34 | "_dom_classes": [ 35 | "widget-interact" 36 | ], 37 | "_model_name": "VBoxModel", 38 | "_view_module": "@jupyter-widgets/controls", 39 | "_model_module_version": "1.5.0", 40 | "_view_count": null, 41 | "_view_module_version": "1.5.0", 42 | "box_style": "", 43 | "layout": "IPY_MODEL_cc3c2018633a4007b36230fe78352f4b", 44 | "_model_module": "@jupyter-widgets/controls", 45 | "children": [ 46 | "IPY_MODEL_3ccbd2cf9c634c31936efc3c95c0e8cd", 47 | "IPY_MODEL_a50d77c2c715494a90dfc9a6e491ddfc" 48 | ] 49 | } 50 | }, 51 | "cc3c2018633a4007b36230fe78352f4b": { 52 | "model_module": "@jupyter-widgets/base", 53 | "model_name": "LayoutModel", 54 | "state": { 55 | "_view_name": "LayoutView", 56 | "grid_template_rows": null, 57 | "right": null, 58 | "justify_content": null, 59 | "_view_module": "@jupyter-widgets/base", 60 | "overflow": null, 61 | "_model_module_version": "1.2.0", 62 | "_view_count": null, 63 | "flex_flow": null, 64 | "width": null, 65 | "min_width": null, 66 | "border": null, 67 | "align_items": null, 68 | "bottom": null, 69 | "_model_module": "@jupyter-widgets/base", 70 | "top": null, 71 | "grid_column": null, 72 | "overflow_y": null, 73 | "overflow_x": null, 74 | "grid_auto_flow": null, 75 | "grid_area": null, 76 | "grid_template_columns": null, 77 | "flex": null, 78 | "_model_name": "LayoutModel", 79 | "justify_items": null, 80 | "grid_row": null, 81 | "max_height": null, 82 | "align_content": null, 83 | "visibility": null, 84 | "align_self": null, 85 | "height": null, 86 | "min_height": null, 87 | "padding": null, 88 | "grid_auto_rows": null, 89 | "grid_gap": null, 90 | "max_width": null, 91 | "order": null, 92 | "_view_module_version": "1.2.0", 93 | "grid_template_areas": null, 94 | "object_position": null, 95 | "object_fit": null, 96 | "grid_auto_columns": null, 97 | "margin": null, 98 | "display": null, 99 | "left": null 100 | } 101 | }, 102 | "3ccbd2cf9c634c31936efc3c95c0e8cd": { 103 | "model_module": "@jupyter-widgets/controls", 104 | "model_name": "IntSliderModel", 105 | "state": { 106 | "_view_name": "IntSliderView", 107 | "style": "IPY_MODEL_663fdb6fa5b8424a9e5a8ce25a41da23", 108 | "_dom_classes": [], 109 | "description": "n", 110 | "step": 1, 111 | "_model_name": "IntSliderModel", 112 | "orientation": "horizontal", 113 | "max": 11, 114 | "_view_module": "@jupyter-widgets/controls", 115 | "_model_module_version": "1.5.0", 116 | "value": 5, 117 | "_view_count": null, 118 | "disabled": false, 119 | "_view_module_version": "1.5.0", 120 | "min": 1, 121 | "continuous_update": true, 122 | "readout_format": "d", 123 | "description_tooltip": null, 124 | "readout": true, 125 | "_model_module": "@jupyter-widgets/controls", 126 | "layout": "IPY_MODEL_de608d5fc21a4866b725cd74a04a6bd6" 127 | } 128 | }, 129 | "a50d77c2c715494a90dfc9a6e491ddfc": { 130 | "model_module": "@jupyter-widgets/output", 131 | "model_name": "OutputModel", 132 | "state": { 133 | "_view_name": "OutputView", 134 | "msg_id": "", 135 | "_dom_classes": [], 136 | "_model_name": "OutputModel", 137 | "outputs": [ 138 | { 139 | "output_type": "display_data", 140 | "metadata": { 141 | "tags": [], 142 | "needs_background": "light" 143 | }, 144 | "image/png": "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\n", 145 | "text/plain": "
" 146 | } 147 | ], 148 | "_view_module": "@jupyter-widgets/output", 149 | "_model_module_version": "1.0.0", 150 | "_view_count": null, 151 | "_view_module_version": "1.0.0", 152 | "layout": "IPY_MODEL_ff37659491e4482384481242650a91cf", 153 | "_model_module": "@jupyter-widgets/output" 154 | } 155 | }, 156 | "663fdb6fa5b8424a9e5a8ce25a41da23": { 157 | "model_module": "@jupyter-widgets/controls", 158 | "model_name": "SliderStyleModel", 159 | "state": { 160 | "_view_name": "StyleView", 161 | "handle_color": null, 162 | "_model_name": "SliderStyleModel", 163 | "description_width": "", 164 | "_view_module": "@jupyter-widgets/base", 165 | "_model_module_version": "1.5.0", 166 | "_view_count": null, 167 | "_view_module_version": "1.2.0", 168 | "_model_module": "@jupyter-widgets/controls" 169 | } 170 | }, 171 | "de608d5fc21a4866b725cd74a04a6bd6": { 172 | "model_module": "@jupyter-widgets/base", 173 | "model_name": "LayoutModel", 174 | "state": { 175 | "_view_name": "LayoutView", 176 | "grid_template_rows": null, 177 | "right": null, 178 | "justify_content": null, 179 | "_view_module": "@jupyter-widgets/base", 180 | "overflow": null, 181 | "_model_module_version": "1.2.0", 182 | "_view_count": null, 183 | "flex_flow": null, 184 | "width": null, 185 | "min_width": null, 186 | "border": null, 187 | "align_items": null, 188 | "bottom": null, 189 | "_model_module": "@jupyter-widgets/base", 190 | "top": null, 191 | "grid_column": null, 192 | "overflow_y": null, 193 | "overflow_x": null, 194 | "grid_auto_flow": null, 195 | "grid_area": null, 196 | "grid_template_columns": null, 197 | "flex": null, 198 | "_model_name": "LayoutModel", 199 | "justify_items": null, 200 | "grid_row": null, 201 | "max_height": null, 202 | "align_content": null, 203 | "visibility": null, 204 | "align_self": null, 205 | "height": null, 206 | "min_height": null, 207 | "padding": null, 208 | "grid_auto_rows": null, 209 | "grid_gap": null, 210 | "max_width": null, 211 | "order": null, 212 | "_view_module_version": "1.2.0", 213 | "grid_template_areas": null, 214 | "object_position": null, 215 | "object_fit": null, 216 | "grid_auto_columns": null, 217 | "margin": null, 218 | "display": null, 219 | "left": null 220 | } 221 | }, 222 | "ff37659491e4482384481242650a91cf": { 223 | "model_module": "@jupyter-widgets/base", 224 | "model_name": "LayoutModel", 225 | "state": { 226 | "_view_name": "LayoutView", 227 | "grid_template_rows": null, 228 | "right": null, 229 | "justify_content": null, 230 | "_view_module": "@jupyter-widgets/base", 231 | "overflow": null, 232 | "_model_module_version": "1.2.0", 233 | "_view_count": null, 234 | "flex_flow": null, 235 | "width": null, 236 | "min_width": null, 237 | "border": null, 238 | "align_items": null, 239 | "bottom": null, 240 | "_model_module": "@jupyter-widgets/base", 241 | "top": null, 242 | "grid_column": null, 243 | "overflow_y": null, 244 | "overflow_x": null, 245 | "grid_auto_flow": null, 246 | "grid_area": null, 247 | "grid_template_columns": null, 248 | "flex": null, 249 | "_model_name": "LayoutModel", 250 | "justify_items": null, 251 | "grid_row": null, 252 | "max_height": null, 253 | "align_content": null, 254 | "visibility": null, 255 | "align_self": null, 256 | "height": null, 257 | "min_height": null, 258 | "padding": null, 259 | "grid_auto_rows": null, 260 | "grid_gap": null, 261 | "max_width": null, 262 | "order": null, 263 | "_view_module_version": "1.2.0", 264 | "grid_template_areas": null, 265 | "object_position": null, 266 | "object_fit": null, 267 | "grid_auto_columns": null, 268 | "margin": null, 269 | "display": null, 270 | "left": null 271 | } 272 | } 273 | } 274 | } 275 | }, 276 | "cells": [ 277 | { 278 | "cell_type": "markdown", 279 | "metadata": { 280 | "id": "view-in-github", 281 | "colab_type": "text" 282 | }, 283 | "source": [ 284 | "\"Open" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "metadata": { 290 | "trusted": true, 291 | "id": "KwmgNaUBQSvg" 292 | }, 293 | "source": [ 294 | "from numpy import *\n", 295 | "set_printoptions(suppress=True, formatter={'all':lambda x: '%5.2f'%x})\n", 296 | "import matplotlib.pyplot as plt\n", 297 | "from ipywidgets import interact\n", 298 | "%matplotlib inline" 299 | ], 300 | "execution_count": 1, 301 | "outputs": [] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": { 306 | "id": "qCh1pLRUQSvm" 307 | }, 308 | "source": [ 309 | "# Finite Element basics 1D\n", 310 | "Let us look at the one-dimensional heat equation. Probably, you will be familiar with the steady-state form:\n", 311 | "\n", 312 | "$$ {d\\over dx}\\left(k A {dT\\over dx}\\right) = 0 $$\n", 313 | "\n", 314 | "where $T$ is the temperature, $A$ is the cross-sectional area, and $k$ is the thermal conductivity.\n", 315 | "\n", 316 | "For a simple example, let us solve this equation in a rod of constant cross-sectional area $A$ which extends from $x=0$ to $x=1$, with a thermal conductivity $k$. We can fix the temperature $T=100$ at $x=0$ and $T=0$ at $x=1$. Obviously, this has a trivial, linear, solution, but it is instructive to see how to get a numerical solution with Finite Element, and we can introduce more complexity later." 317 | ] 318 | }, 319 | { 320 | "cell_type": "markdown", 321 | "metadata": { 322 | "id": "BCC4aNeKQSvn" 323 | }, 324 | "source": [ 325 | "### Representation of a function by discrete variables\n", 326 | "\n", 327 | "First, let us consider how we can represent $T(x)$ computationally. Simply, we might choose to subdivide the domain $(0,1)$ into $n$ pieces (or \"elements\"), and represent $T(x)$ as having a continuous, piecewise linear values in each part. For example, suppose $T(x) = \\sin(5x)$, and $n=10$." 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "metadata": { 333 | "trusted": true, 334 | "id": "RonMUqVbQSvo", 335 | "outputId": "6c5928f3-e301-4b60-ec9a-ce18ec40c6ff", 336 | "colab": { 337 | "base_uri": "https://localhost:8080/", 338 | "height": 299 339 | } 340 | }, 341 | "source": [ 342 | "# Create an array of n+1 values for position x, from 0 to 1 inclusive\n", 343 | "n = 10\n", 344 | "x = linspace(0.0, 1.0, n + 1)\n", 345 | "T = sin(5*x)\n", 346 | "\n", 347 | "print(x)\n", 348 | "print(T)\n", 349 | "plt.plot(x,T, marker='o');\n", 350 | "\n", 351 | "# Plot a more accurate representation\n", 352 | "n = 1000\n", 353 | "x = linspace(0.0, 1.0, n + 1)\n", 354 | "T = sin(5*x)\n", 355 | "plt.plot(x,T);\n" 356 | ], 357 | "execution_count": 2, 358 | "outputs": [ 359 | { 360 | "output_type": "stream", 361 | "text": [ 362 | "[ 0.00 0.10 0.20 0.30 0.40 0.50 0.60 0.70 0.80 0.90 1.00]\n", 363 | "[ 0.00 0.48 0.84 1.00 0.91 0.60 0.14 -0.35 -0.76 -0.98 -0.96]\n" 364 | ], 365 | "name": "stdout" 366 | }, 367 | { 368 | "output_type": "display_data", 369 | "data": { 370 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYIAAAD4CAYAAADhNOGaAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3dd3iT9f7G8fenSVoKtJRRVpnKFpRCGUc8ArIdgIgsEUQEF0cEReCox60o5+ceiAuULSKgqMgGUZAiIsuyV1lllNWZ5Pv7I4FTsYVC0z5N8nldV64mz0juh5G7zxZjDEoppYJXiNUBlFJKWUuLQCmlgpwWgVJKBTktAqWUCnJaBEopFeTsVge4EmXKlDHVqlWzOoZSSvmVtWvXHjXGRF843C+LoFq1asTHx1sdQyml/IqI7MluuG4aUkqpIKdFoJRSQU6LQCmlgpwWgVJKBTktAqWUCnI+KQIR+VREjojIxhzGi4i8LSLbReQPEWmUZVx/EdnmffT3RR5lrdnrEmkxZjHVR82jxZjFzF6XaHUkpdRF+GqNYALQ8SLjOwE1vY/BwAcAIlIKeAZoBjQFnhGRkj7KpCwwe10io2dtIDE5FQMkJqcyetYGLQOlCjGfnEdgjFkuItUuMkkX4HPjueb1KhGJEpEKQCtggTHmOICILMBTKFN9kUsVvLHzE8jMTKeyHKOyJFGe4xRzp3Fw3jeQXAlsDrCHQVgkRFaEiPIQVRWKRFodXamgVVAnlMUA+7K83u8dltPwvxGRwXjWJqhSpUr+pFRX5tgO2LUcEtfyScpSaoQlYhf3X6dxAssu8h5RVaF8A6gYC1e1ggoNweaX5zsq5Xf85n+aMWY8MB4gLi5O76ZjJWNg7y+weS5smw/HdwKQ7ijBYaqx0NWIPaYc+000B0xpzphwzhBOBnbqlytK6xolaFU1lGtLpGA/cwiO74BDGzyPP7+FxS9AWAm4qiXU7wY1O0BoUYsXWqnAVVBFkAhUzvK6kndYIp7NQ1mHLy2gTOpyJe+D3yfD+qlwYjfYwnBX/ye/le/F2O0xrD4VRdVSRTl4Mp0M1//WCMIdITzapiYiwtKEI7z/yxHeXmmICLNzQ83KtKrdiFYdHqZcZBE4e9SzdrFzCWydD1vmgqMY1L0NmtwHleJAxLo/A6UCkPjqVpXefQTfGmPqZzPuFmAIcDOeHcNvG2OaencWrwXOHUX0G9D43D6DnMTFxRm91lABSvwNfnkXNs0G44bqN+K6thdz0hvzxvJE9h1P5bpKJRjWrhYta0Uz5/cDjJ2fwIHkVCpGhTOiQ226xv5vi9/ptExWbj/G0oQjLE1I4tCpNADqVoikVe1oWtcuS6MqUdjFwJ6VsGEmbJwFGac9m46aPQD1u+umI6Uuk4isNcbE/W24L4pARKbi+c2+DHAYz5FADgBjzDgREeBdPDuCU4ABxph477z3Av/2vtVLxpjPLvV5WgQFZP9aWPw87FwKoRHQuD+uJoOZs9vG24u2sftYCvVjIhnWthY31SmLXMFv6sYYEg6fZsmfSSxNOMLaPSdwug0RRez8s2YZWtUuS6ta0ZQNy4Q/psOvH0HSn1CyOrR8Ahr00EJQKpfytQgKmhZBPkvaCoue82yvL1oaWjyKq1F/vk04w1uLtrEz6Sx1K0QyrG1N2tUrd0UFkJNTaZms3HaUpQlJLN16hMOn0gGoVyGS1nWiaVUrmkZpq7AtGwOH/oBSV0P7F6F2J91kpNQlaBGoS0s/A8tfg1/eA3s4XP8v3M0e5LttZ3hz4Ta2HzlD7XIRDGtXk/b1yhMSkr9fvMYYthw8zdKtnk1Ia/ecwOU2RHrXFvqU2Eizne9iP74VrmoNHV9hdmLkRTdLKRXMtAjUxW2eCz+MglOJ0LAv7jbPMn+3kzcXbiPh8Glqli3O0LY1ubl+hXwvgJycTM1k5faj5/ctHDmdjh0nI0r9RP/0qTjcKYxzdeGtjC5keLZMEu6w8Uq3BloGSqFFoHKSchy+exw2fgXl6mNu/i8LzlTnjYXb2HLwFFdFF2Nom5rcem1FbBYVQHaMMWw+eIqlCUksS0hi5949jLJNprttOQnuSjyROZj1pgYAMVHhrBx1k8WJlbKeFoH6u+0LYc4QOJuEaTmSJWXu4vXFO9mYeIpqpYsytG1NOl8XU6gKICcnUzO57rkfaRWyjpcdn1COE7zr6srbzm64sbFrzC1WR1TKcjkVgR5uEYxcmbDwWfjlXUx0HdZe/wEvrHWwfv/vVC4Vztju13J7bAx2m/9cnLZEuIOYqHCWJsfSIf01/mP/nKH2r2kesoVXwh+zOp5ShZoWQbA5dRBmDoC9v3Cg1t08euIOfp2TQkxUOK/e0YBujSrh8KMCyGpEh9qMnrWB05lFGeF8gJXu+rzo+JSprsdhawmo1cHqiEoVSv75P15dmV0r4MN/4jqwnjdKjOT6Pzqx/5Sbl26vz5LHW9GzSRW/LQGArrExvNKtATFR4QjwS/E29JEx7HGWxEzpCcvHei6PoZT6C10jCGCz1yWeP5TyoeLLeMz5MQdtFemfMoLTIVfzQpca9GhSmTC7zeqoPtM1NuYvRwjtPZZCvw/L8UTGe9y8+EU4vAm6vAehxSxMqVThokUQoM7dFyA9M5N/26cwyPkdi1yx/NsM5YFbG9K7aRWKOAKnAHJSpXRRJt7fkp7jQtnmrMYjmyYjx7ZDnxmey2ArpXTTUKAaOz8BMs8yzvEGg+zf8ZmzA4MyH8NWJJIBLaoHRQmcU7V0Mabe/w+mOG5naMho3Md2wiftISnB6mhKFQpaBAEqJfkIU0Nfok3Ibzyb2Y/nnP1xE8LBk2lWR7NE9TLFmDqoOatsjbnb/QzOjDRPGexdZXU0pSynRRCITh3kyyIvUlf28kDmMCa4/ncX0YpR4RYGs9ZV0cWZMqg5W0Ou5o6MZ8kIKwmfd4E/51kdTSlLaREEmuO7SP2wHeVNEgMyn2CB+3/njoQ7bIzoUNvCcNarUbY4Uwc1I1HKc/PZ/5BWui5Mv9tzZrVSQUqLIJAkJZD5cXvSz5zg6RIv0aVrr/OHUsZEhes1d7xqlI1g6qBmJBNBx2OPkVqhCXx1H6yfZnU0pSyhl5gIFEe34fq0E8kpmQyxP8Mb/+pD+RJFrE5VqCUcOk2fj1ZRPCSdH8p9QPj+ldD5HWh0t9XRlMoXOV1iQtcIAsGxHZgJt3E6NZP+rqd5csAdWgK5ULt8BJMHNeO0O4xORx4mtUpLmDsE1k60OppSBconRSAiHUUkQUS2i8iobMa/ISK/ex9bRSQ5yzhXlnFzfZEnqBzfhZl4G2dSUuiZPpqhvW+jfkwJq1P5jTrlI5l8XzNOOu10OvQAqVVawzdDPbfHVCpI5LkIRMQGvAd0AuoBvUWkXtZpjDHDjDENjTENgXeAWVlGp54bZ4zpnNc8QSV5H0zsTFrKaXqkjuLOTu1pV6+c1an8Tt0KkUy6rxnJThu3HL6ftIrN4Ov7IeF7q6MpVSB8sUbQFNhujNlpjMkApgFdLjJ9b2CqDz43uJ09Bl/cTsbZE3Q/O5LYpv9k4A3VrU7lt66pWIJJA5txLMPGbcf+RUZ0fZjR33O/ZqUCnC+KIAbYl+X1fu+wvxGRqkB1YHGWwUVEJF5EVolI15w+REQGe6eLT0pK8kFsP5ZxFqb0wH1iD/1Sh1OqRhOe63yNT+8dHIzqx3jK4HC6gy4nh5NZ8iqY2hv2rbE6mlL5qqB3FvcCZhpjXFmGVfXuxe4DvCkiV2c3ozFmvDEmzhgTFx0dXRBZCydXJszohznwG8Pcj3K0dGPe7dPIr68aWpg0qFSCSfc1Y39aEbqfHYmzaFmY2hOO7bA6mlL5xhffHolA5SyvK3mHZacXF2wWMsYken/uBJYCsT7IFJjcbpjzMGxfyH8dD/KTrSmf3dOEEuEOq5MFlGsrRTFpYDN2phSlX8ZIXMbApDvgTJCviaqA5YsiWAPUFJHqIhKK58v+b0f/iEgdoCTwS5ZhJUUkzPu8DNAC2OyDTIFp0bPwx3SmFu/PRyn/ZHy/xlQuVdTqVAHpuspRfD6wKX+klGaIGYU5fQim9PBsllMqwOS5CIwxTmAIMB/YAswwxmwSkedFJOtRQL2AaeavZ7DVBeJFZD2wBBhjjNEiyM5vn8PKt1hZsjOjj7ZnbPdraVy1lNWpAlpslZJMvLcpy1Oq8rR9GObg7zBzILicVkdTyqf0zGJ/sGsFfNGVvSXiaH3wYR5pW5ehbWtanSpoxO8+Tv9Pf2Vw+CKGpo+HJoPglv9aHUupy6ZnFvurYztgel9OF6vKrQcH0jm2Co+0qWF1qqASV60UE+5tyoepbZjh6AprPoI1n1gdSymf0SIozFKOw5QeZBJC1xOPULtaJcbc0UAPE7VAk2ql+OyeJjyX1pPV9jjM90941tSUCgBaBIWVKxO+7I854bmnQGZkVT68Oy6g7i/sb5pdVZpPBjRjSPrD7KUC7ul3w/FdVsdSKs+0CAqrH5+GXcsZG/YQa1y1+fSeJpQqFmp1qqDX/KrSvHXPjQzKfIyz6Zk4J/eCtFNWx1IqT7QICqM/ZsDqD/ghohvjTzbjg76NqVG2uNWplNf1V5fhmf63McQ5FDm2lYyZgzzneCjlp7QICptDGzBzH2FXsYYMSerKi13r06JGGatTqQu0qFGGQf3u5WVXP0K3/0Dq4tesjqTUFbNbHUBlkXIcpt1Fiq04dx67n4Eta9GraRWrU6kc3FCzDO6+TzJ38jZu/WkMj6x28M3pOlSMCmdEh9p6NzjlN3SNoLBwu2DWINynDnD36SE0vqYWIzvUsTqVuoQba5clockLbHVX4tmM16nAURKTUxk9awOz1+V0pRWlChctgsJi6RjYvpDnnf3JrBDHGz0bEhKih4n6g9mbTvJg5qM4cPF+6FuEkklqpoux8xOsjqZUrmgRFAbbFsLy1/g25CbmF+nEx/3jKBqqW+38xYHkVHaZCjyeeT8NQ3bwtP2L88OV8gdaBFY7dQAzazC7bNX4j3MAn9zTlHKRer9hf1IxKhyA+e6mjHPeyt32hXQN+en8cKUKOy0CK7mcmJkDyUg7y6DUhxnbuxn1KkZanUpdphEdahPu8JzoN9bZk9XuOrzk+ISnmuvlwZV/0CKw0vLXkL0/Mzr9Hvrc3I42dfV+w/6oa2wMr3RrQExUOG5sPGsfRgYOYn99DJzpVsdT6pJ0Q7RVdi7DLHuNr1w3Uqzp3QxoUc3qRCoPusbG/OVw0YmfnaD/nlEcmfUEZXu8ZWEypS5N1wiscOYIGTMGssNU4Meqj/PMbfX0QnIBplvv+5huu42ymyeQselbq+ModVFaBAXN7SZl2kDcacmMjRjJf/tej13vNxxwIoo4qHTnq2xwV8M560E4ud/qSErlyCffQCLSUUQSRGS7iIzKZvw9IpIkIr97H/dlGddfRLZ5H/19kacwS1n2FkX3L+f1kAE8dW8PIovoDsVA1aJODAuveQW3M4NTk+/RO5upQivPRSAiNuA9oBNQD+gtIvWymXS6Maah9/Gxd95SwDNAM6Ap8IyIlMxrpsIqI3E9octe5Ed3Ezr2H633Gw4Cg7u25/WwB4g8soaMpWOtjqNUtnyxRtAU2G6M2WmMyQCmAV1yOW8HYIEx5rgx5gSwAOjog0yFxux1ibQYs5g6o75m7/g+HDfFMbe9RSO933BQKBZmp32vR/ja1QL7irGwf63VkZT6G18UQQywL8vr/d5hF7pDRP4QkZkiUvky50VEBotIvIjEJyUl+SB2/pu9LpHRszaQmJzKSPtUash+RjofINUeZXU0VYCaX1WahNj/cNBEkTr9Xsg4a3Ukpf6ioPZSfgNUM8Zci+e3/omX+wbGmPHGmDhjTFx0dLTPA+aHsfMTSM10cWPIegbY5/OZswNLXNfqNWiC0NBbmzA2/FHCTu8h8/t/Wx1Hqb/wRREkApWzvK7kHXaeMeaYMebcmTUfA41zO68/O5CcSklO8V/Hh2x1xzDG2fv8cBVcwkNt9O3Vl4+dN+NYNwG2zrc6klLn+aII1gA1RaS6iIQCvYC5WScQkQpZXnYGtnifzwfai0hJ707i9t5hAaFiiSK87PiEKE7zaObDpOO51aRegyY4xVUrxfFmT7DFXYWMWQ/B2aNWR1IK8EERGGOcwBA8X+BbgBnGmE0i8ryIdPZO9oiIbBKR9cAjwD3eeY8DL+ApkzXA895hAeE/ldfRybaGsc6ebDbVAAh32BjRoba1wZRlHu3YgP8r/hikJZM5ewgYY3UkpRDjh/8Q4+LiTHx8vNUxLir96G6c7zZnM1cxNPQ5Dp7K0DtXKQDW7T3Bd+Of5En7ZOj8DjTqZ3UkFSREZK0xJu7C4XqtofxgDIe+GERpY3B2fpefGzeyOpEqRGKrlGRBiyH8/PM6mn43Cnv1llCyqtWxVBDTaxvkg0NLxlH15K98V/5B/qEloLIxtF1t3o8aTrrThVM3ESmLaRH4mOvYLkosf5ZVNKBN379dbUMpAMLsNkb2bM8rzruw71kO8Z9aHUkFMS0CX3K7OTJpEE4jnGz3OqUj9E5jKmcNKpWg1I33s8JVH+f8p+DEHqsjqSClReBDJ5Z/QIUTa/iy9AO0v76J1XGUHxhyU00+LjmcNKch8+uHwe22OpIKQloEPmKO76LosudZYa6jfd8Ren8BlSuh9hBG9mrHGOddOPaugLW6iUgVPC0CX3C7OTb5PtLdwoEbX6NSqWJWJ1J+pF7FSMq1up/lrgY4f3gaTuy2OpIKMloEPnD2p/cpcyyeCZH30711M6vjKD/0QOsaTCg9nDSXIWOWbiJSBUuLIK+O78Sx5HmWuhvS8a7HsIXoJiF1+Ry2EEb1bscYZ19C9/2E0aOIVAHSIsgLYzgx/SHS3CFsbfoitcpHWp1I+bFa5SKIafMAy10NcM1/GpL3Wh1JBQktgjxIW/M5JQ//wifhA+jf8Xqr46gAMOjGq/giejgZLhfpcx7VE81UgdAiuFKnD2HmP8lqdx1u7PUYYXab1YlUALDbQhjVuz2vu3oRtmsR5o8ZVkdSQUCL4AqdmPkoIc40Vl/zDI2rlbE6jgogV0cXp0K7f7HOXYOMeU/o5apVvtMiuAKZG+dScs/3fGrvwb1d21sdRwWge26owcToxwnJOE3qN09YHUcFOC2Cy5WaTPrcYWx2V6XOHf+meJhewFX5ni1EeLR3Zz50dyX8z68wW3+0OpIKYD4pAhHpKCIJIrJdRP52pTURGS4im703r18kIlWzjHOJyO/ex9wL5y1sTs4dTXj6MeZVH03repWsjqMCWLUyxYhsN5Jt7hhSZv0L0k9bHUkFqDwXgYjYgPeATkA9oLeI1LtgsnVAnPfm9TOB17KMSzXGNPQ+OlOIuXcup8SWKUySWxlwZzer46gg0LdFLT4v+xjhqYc58/0zVsdRAcoXawRNge3GmJ3GmAxgGtAl6wTGmCXGmBTvy1V4blLvXzJTOTPzYXa7yxF18zOUKR5mdSIVBEJChMF9ejOV9hT9/VPM3tVWR1IByBdFEAPsy/J6v3dYTgYC32d5XURE4kVklYh0zWkmERnsnS4+KSkpb4mvwOn5LxKZspep5YbTuUmNAv98FbwqlyqKrd2zHDSlODn9AXCmWx1JBZgC3VksIn2BOGBslsFVvffQ7AO8KSJXZzevMWa8MSbOGBMXHR1dAGmzfPaBdRSNf5+v3K3p27ufXllUFbieLeoyucwwos7u5OSPr1odRwUYXxRBIlA5y+tK3mF/ISJtgSeBzsaY87/SGGMSvT93AkuBWB9k8h2Xk5PTH+K4iSCl9XNULlXU6kQqCIkId919H/PMDRT79S3ch/+0OpIKIL4ogjVATRGpLiKhQC/gL0f/iEgs8CGeEjiSZXhJEQnzPi8DtAA2+yCTz5xd+QFRJzfzWeSD9Gl5rdVxVBCLiQrH2e4lzpgwjkx9SC8/oXwmz0VgjHECQ4D5wBZghjFmk4g8LyLnjgIaCxQHvrzgMNG6QLyIrAeWAGOMMYWnCE4mYlv6Esvc19G5z0N6ZVFluc4trmNW6cGUT17L0RV6hVLlG2L88LeKuLg4Ex8fn++fk/RJDyL2LmZS4xnc1/mmfP88pXLjUHIKB95sTU1JpOjwddgiCnafmfJfIrLWu0/2L/TM4hykbZxH9L75TArrxd03t7Q6jlLnlY8qyrHWrxLmTmHH5EetjqMCgBZBdjLOkjZ3OFvdMTTs+ZReWVQVOm1vbMmCqJ7UOvQtievmWx1H+Tktgmwc/uY5ojIOsazWk8RdXd7qOEr9jYjQpP/L7KMcrrmP0uqV76k+ah4txixm9rq/HbSn1EVpEVwgM/EPymz4iLkhbejVvYfVcZTKUdlSJVl09UiqmAN0PTsDAyQmpzJ61gYtA3VZtAiycrs5Ou0hkk0xStz2MhFFHFYnUuqiPkqsxhzX9Txom8tVcgCA1EwXY+cnWJxM+RMtgiyOLB1HhdMbmFfhYVrG1rE6jlKXdCA5jRcz+5JGKC/ZPwWMd3iqtcGUX9Ei8HKfOkSxFS/yK9fQqc8wq+MolSsVo8JJIooxzt78w7aZO0JWnB+uVG5pEXjtmfIodnc6x1u/SnRkEavjKJUrIzrUJtxhY5qrNfHuWjzpmER5+xlGdKhtdTTlR7QIgGPrv6f6oe+ZV6IXHW68weo4SuVa19gYXunWgIpRxXgq814iSOWl4l/SNfZiFwBW6q+C/j6LJiMF5zfD2W0q0KTvC3plUeV3usbGeL/4b2LNx3/SZv8E1q34hth/3mZ1NOUngn6NYNtXz1HOeYBNjZ6lctlSVsdRKk+uvetFDkg5Si4eSWpKyqVnUIogL4KTezdSPeFjloS1psOtes6A8n9h4RGcumkM1Uwia6borS1V7gRvERhD0rSHOWvCqNjjdey24P2jUIGlzj+7sT6yNc32fcaOP9dbHUf5gaD99kuY/yE1Un5n9dWPUPvqq6yOo5RPVb3rbTLEwemvHsHtclsdRxVyQVkEKcmHKbfqRTaE1KVlr8esjqOUz0WVq8KOBsNomPk7K+eMszqOKuSCsgi2fjGMYiYFc8vrFAnVy0iowHRd1+Fsd9Sm3voxJB05ZHUcVYj5pAhEpKOIJIjIdhEZlc34MBGZ7h2/WkSqZRk32js8QUQ6+CJPdmavS6TFmMX0HP1fGh6bx/cRd3Bt4+vz6+OUspzY7IR3e4cSnCZhsq75qpzluQhExAa8B3QC6gG9RaTeBZMNBE4YY2oAbwCveueth+cex9cAHYH3ve/nU7PXJTJ61gaOJJ/mRcen7HNH85+Tt+gVGlXAi6nbjA2VenPDyW9Z+9MPVsdRhZQv1giaAtuNMTuNMRnANKDLBdN0ASZ6n88E2ojnzK0uwDRjTLoxZhew3ft+PjV2fgKpmS4G276lZkgiTzvvITnToVdoVEHhmrvGcETKELVoBCmpejE69Xe+KIIYYF+W1/u9w7Kdxnuz+5NA6VzOC4CIDBaReBGJT0pKuqyA567EWEGO8a2rGUvdsX8ZrlQgCy0aSXKrl7ja7GXVlBesjqMKIb/ZWWyMGW+MiTPGxEVHX97Nus9difEp50CGZg7523ClAl2tlr3YFHkD/9j7EdsSNlkdRxUyviiCRKBylteVvMOynUZE7EAJ4Fgu582zc1doBHDh+RnusOkVGlVQqdTnXYyEkPzVUFx6boHKwhdFsAaoKSLVRSQUz87fuRdMMxfo733eHVhsjDHe4b28RxVVB2oCv/og01+cu0JjTFQ4AsREhfNKtwZ6hUYVVEqUr86Oax6hScYaVsz91Oo4qhARz/dxHt9E5GbgTcAGfGqMeUlEngfijTFzRaQI8AUQCxwHehljdnrnfRK4F3ACjxpjvr/U58XFxZn4+Pg851Yq2BhXJnvGNCM84zgyZA1lL3Mzq/JvIrLWGBP3t+G+KIKCpkWg1JU7uOknys24lWUlb6f1o59ZHUcVoJyKwG92FiulfKPCNTewKaY7N574ml9XLrQ6jioEtAiUCkK1+4wlOSSKyIUjOJuaZnUcZTEtAqWCUGjxkiTf+Dx1zE5WTnnF6jjKYloESgWpq1vdTUJEM67fO44/E/60Oo6ykBaBUsFKhAq938Mubo5/NQyX2/8OHFG+oUWgVBCLrFiTnfUe5vqMn1ky93Or4yiLaBEoFeTqdhvNfkdV6q17nkNHj1odR1lAi0CpICf2MByd36KiHOWPSf+2Oo6ygBaBUopyDVqzuXwXbjrxJb/8vMzqOKqAaREopQCoedfrnAkpTvEFj3MmLcPqOKoAaREopQBwRJThxA3P0MBsZdnUsVbHUQVIi0ApdV71mwayo3gjbtj9Llu2bbc6jiogWgRKqf8RoWyv9wiXDA5/+RhOvW9BUNAiUEr9RUSleuyucz+tMpay8NupVsdRBUCLQCn1NzXveJpD9hjq/fYcB44etzqOymdaBEqpvxFHOCG3vUkVOcxvk562Oo7KZ3kqAhEpJSILRGSb92fJbKZpKCK/iMgmEflDRHpmGTdBRHaJyO/eR8O85FFK+U7Z69qztdzNtD8xlZ9+WWl1HJWP8rpGMApYZIypCSzyvr5QCtDPGHMN0BF4U0SisowfYYxp6H38nsc8Sikfqn7Xm6SHhFP0xxGcScu0Oo7KJ3ktgi7ARO/ziUDXCycwxmw1xmzzPj8AHAH0RqlK+QFHZDmOX/8kjcwmFk59w+o4Kp/ktQjKGWMOep8fAspdbGIRaQqEAjuyDH7Ju8noDREJu8i8g0UkXkTik5KS8hhbKZVbVds8wJ5i19Jy91ts3rrN6jgqH1yyCERkoYhszObRJet0xhgD5HhBcxGpAHwBDDDGnDs4eTRQB2gClAJG5jS/MWa8MSbOGBMXHa0rFEoVmJAQSvceRzFJ4+jMYXpuQQC6ZBEYY9oaY+pn85gDHPZ+wZ/7oj+S3XuISCQwD3jSGLMqy3sfNB7pwGdAU18slFLKt4pXuoZd1wzhxowVLJnzmdVxlI/lddPQXKC/93l/YM6FE4hIKPA18LkxZuYF486ViODZv7Axj3mUUvmk1u1Pss9xFdetf4EDhw5ZHUf5UF6LYAzQTkS2AW29rxGROBH52DtND+BG4L6SfoAAABJRSURBVJ5sDhOdLCIbgA1AGeDFPOZRSuUTsYfiuON9SpPM9kmP4tkarAKB+ONfZlxcnImPj7c6hlJB6fdPH6Hh3on8+s/PaNqmm9Vx1GUQkbXGmLgLh+uZxUqpy1K/zyvsD6lIxRWjOH0q2eo4yge0CJRSl8VepBipHd+kEof56v8eovqoebQYs5jZ6xKtjqaukBaBUuqybXLUZ5KrLXfzHdfJdhKTUxk9a4OWgZ/SIlBKXbax8xMYk9mLw5TkVcd4QskkNdPF2PkJVkdTV0CLQCl12Q4kp3KGojyZOZDaIft5yD7n/HDlf7QIlFKXrWJUOABL3LF87WrBQ7Y51JU954cr/6JFoJS6bCM61CbcYQPgucx+nKQ4rzs+oF/TChYnU1dCi0Apddm6xsbwSrcGxESFc5IIXnM8SN2QvRRb9Tpn051Wx1OXyW51AKWUf+oaG0PX2Bjvq1s48vlWeu2YyVtTbmL4Pb3wXDlG+QNdI1BK+UTZO98gLawMnXe9wPRftlsdR10GLQKllG+ER1H0znHUDEnkzA/PsjHxpNWJVC5pESilfCakZhvSruvHvSHzGPfFJE7p7S39ghaBUsqnitz8MpnFYxiR+hZPz1itVyn1A1oESinfCosgrPuHVJXDNNr6JhN+3m11InUJWgRKKd+rdgOm2QP0ty9g+ffTWbf3hNWJ1EXkqQhEpJSILBCRbd6fJXOYzpXlpjRzswyvLiKrRWS7iEz33s1MKRUApO2zuErXZqx9HE9OXsaJsxlWR1I5yOsawShgkTGmJrDI+zo7qcaYht5H5yzDXwXeMMbUAE4AA/OYRylVWDjCsXX/mFIhZxme+g7Dp6/D7db9BYVRXougCzDR+3winvsO54r3PsU3AefuY3xZ8yul/ECFawlp+wxtQ+Ipv2M6Hy7faXUilY28FkE5Y8xB7/NDQLkcpisiIvEiskpEzn3ZlwaSjTHnzkffD8RkPzuIyGDve8QnJSXlMbZSqsA0fwhzVWueC53ErB8Xs3rnMasTqQtcsghEZKGIbMzm0SXrdMZzjFhO631VvffJ7AO8KSJXX25QY8x4Y0ycMSYuOjr6cmdXSlklJATp+gGOIsV4r8gHDJvyK0fPpFudSmVxySIwxrQ1xtTP5jEHOCwiFQC8P4/k8B6J3p87gaVALHAMiBKRc9c7qgTo7Y2UCkSRFZDO71DLvYMBGZMZOm0dLt1fUGjkddPQXKC/93l/YM6FE4hISREJ8z4vA7QANnvXIJYA3S82v1IqQNS9FRrfw30h3+LeuZy3F22zOpHyymsRjAHaicg2oK33NSISJyIfe6epC8SLyHo8X/xjjDGbveNGAsNFZDuefQaf5DGPUqow6/AylK7BuKLjmLw4nhXbdH9fYSD+ePp3XFyciY+PtzqGUupKHNqI+bgNv1GHB9yj+WZoK8qXKGJ1qqAgImu9+2v/Qs8sVkoVrPL1kU6v0tj5O32dX/Gvqb/hdLmtThXUtAiUUgWvUX9ocCePhMzEtncl//1xq9WJgpoWgVKq4InArW8ipa/mo6IfMHPZbyzactjqVEFLi0ApZY2w4nDnBIpzlo+Kf8jj039j3/EUq1MFJS0CpZR1ytdHOr1GrPN3BpmZDJnyGxlO3V9Q0LQIlFLWatQPruvNQzKT0geW8PJ3W6xOFHS0CJRS1hKBW9+ACtfxfvgHLPvlF77bcPDS8ymf0SJQSlnPEQ49JxEWVoTPi77JszNXs/voWatTBQ0tAqVU4RBVBblzApXcB3hZ3ufBSWtJy3RZnSooaBEopQqP6jci7V+gLatpnTSJ577ZZHWioKBFoJQqXJo/BA3uZIRjBkfjv2bWb/utThTwtAiUUoWLCNz2NlSM5Z2w95j09TdsO3za6lQBTYtAKVX4hBZFek/DUbwMH9he46kvFpCS4bz0fOqKaBEopQqniHLY7ppBGXs6T516jue/WoM/Xi3ZH2gRKKUKr/L1sfWYwDUhe2m9+Smm/brH6kQBSYtAKVW41WoPHV6mgy2ezHkj2ZSYbHWigJOnIhCRUiKyQES2eX+WzGaa1iLye5ZHmoh09Y6bICK7soxrmJc8SqnAFNL8AVIbP0C/kB/4ecK/OZWWaXWkgGK/9CQXNQpYZIwZIyKjvK9HZp3AGLMEaAie4gC2Az9mmWSEMWZmHnMopQKZCOG3vMKxE4cYtHMy775bkqnOmziQnEbFqHBGdKhN19gYq1P6rbxuGuoCTPQ+nwh0vcT03YHvjTF6rVml1OUJCaH0XR+zqVgzHjz9HvVPrcAAicmpjJ61gdnrEq1O6LfyWgTljDHnrg51CCh3iel7AVMvGPaSiPwhIm+ISFhOM4rIYBGJF5H4pCS94bVSQcnm4BHnUNabq3nb8S7NQzYDkJrpYuz8BIvD+a9LFoGILBSRjdk8umSdzniO68rx2C4RqQA0AOZnGTwaqAM0AUpxwWalC95/vDEmzhgTFx0dfanYSqkAtfMk3Jsxgj2mLJ84xhInfwJwIDnV4mT+65JFYIxpa4ypn81jDnDY+wV/7ov+yEXeqgfwtTHm/F4eY8xB45EOfAY0zdviKKUCXcWocJKJ4K6MJzlkSjEh9DUayVbKFM9xg4K6hLxuGpoL9Pc+7w/Muci0vblgs1CWEhE8+xc25jGPUirAjehQm3CHjSSi6J3xFEdMFBNDX6XS2U28s2gbLreedHa58loEY4B2IrINaOt9jYjEicjH5yYSkWpAZWDZBfNPFpENwAagDPBiHvMopQJc19gYXunWgJiocJIoybDwF6FYGaaEv8rChd/Re/wqEnUz0WURfzxlOy4uzsTHx1sdQylVWJzcDxNuIfPUEQY7H2et1Oflbg249dqKVicrVERkrTEm7sLhemaxUsr/lagEA37AUaoKn9pfpWfkRoZMWceIL9dzNl0vVncpWgRKqcAQWQEGfI+Ur8+/T7/E+w22MfO3/dzy9grW79PLUlyMFoFSKnAULQX95iDVWnDztmdY1nwdGZku7vjgZ95ful13JOdAi0ApFVjCIqDPl1D/DqqsG8vS2rO4+ZrSvPZDAn0/Xs3Bk7oj+UJaBEqpwOMoAnd8Ai1HErphMm9lvsAbnauyfn8yHd9cwQ8bD176PYKIFoFSKjCJQOt/w+3jkX2ruT3+bn7sU4aqpYvywKTfGD3rD73rmZcWgVIqsF3XE/p/AxkpVJp5K7Ou38uDra5m2pp93PrOT2xMPGl1QstpESilAl+V5nD/cohpjH3ug4zMHMfUe64jJd3F7e+v5KPlO3EH8Y5kLQKlVHCIKAf95kCLR2HtZzRf0I0fe0ZyU52yvPTdFvp/9itHTqVZndISWgRKqeBhs0O756DvLEg/TeTkjoyrvIhXutRlze7jdHxrBQs3H7Y6ZYHTIlBKBZ8abeChn6FeV2Tpy/Re348FdxalQoki3Pd5PE/N3kBqhsvqlAVGi0ApFZzCS0L3T6DH53D2GJVndWZu1Rk88o9STFq1l87v/sSWg6esTlkg9KJzSimVfhqWjoFVH0BYBLvqDObujQ05khrCqE51GNCiGp6r5Vtj9rpExs5P4EByap7u0ZzTRee0CJRS6pzDm2Dhs7DtR9zFyjGlSA9eSGxM81ox/PfO64iOKPib38xel8joWRtIzXThwEkmdsIdNl7p1uCyy0CLQCmlcmv3Slj0HOxbTVpoScantmVOaCfaNKrLvA2H8vSbudttOJPh5HSak1Opmf/7mZ7JqVQnp9MyOZXm/ZnqZOGWw1Ry7aOf7Uc6236hffqrJFGSmKhwVo666bI+O6cisF/WuyilVDCo1gLunQ97VlLk53d4ZOuXPOD6mgWrGrHN1ZJDXEticiojv/qDPw+dokFMFKfSMs9/eV/4Ze4Z5/l5Jt3JpX7/DrOHUKFIJu3tv/Gp/EiLsE2kGzvfupsTiudsaF/eozlPRSAidwLPAnWBpsaYbH9NF5GOwFuADfjYGHPuTmbVgWlAaWAtcLcxJiMvmZRSyidEoNoNnseRP/n6w+dpa5Zzi+1XjppIlrobstjVkMnLznCaoudnCxGIKOIgooidSO/PyqWKnn8dWcROZHjW8Q4iw+1EFHEQlXGIiAM/Y98xH7b+CGnpHLRF81pmT6a5WnOcyPOfUzEq3GeLmtc1go1AN+DDnCYQERvwHtAO2A+sEZG5xpjNwKvAG8aYaSIyDhgIfJDHTEop5Vtl6zDq7F3Y6UmrkN+51baKtiFr6W5bjssIztK1cVZsjK1SI8LK1kBKVofIGM95C9nJOAunD8GJrXBoo2ffRGI8HN/pGV+8HDS+B+rfweqjFfns602kuv53OGu4w8aIDrV9tnh5KgJjzBbgUnvTmwLbjTE7vdNOA7qIyBbgJqCPd7qJeNYutAiUUoVOxahwEpNTWeCOY4E7DhsuYmUbnYomMLDUMcJ2fAcbJ2eZQzyXxA4tDqFFwe0EZwZknIH0Cw5LjYyBCtdB08FQvSWUretZIwG6VgEkxCdHDeWkIPYRxAD7srzeDzTDszko2RjjzDI8xyUTkcHAYIAqVarkT1KllMrBiA61zx+9A+DCxib7NfS9tRfExoAxcHIfnNjteZzc7zksNf0UZKSAzeF5OIpBRHmIrOi5xWbZep4b6lxE19gYn37xX+iSRSAiC4Hy2Yx60hgzx/eRsmeMGQ+MB89RQwX1uUopBZz/Is7xN3MRiKrieVS/0cKkl++SRWCMaZvHz0gEKmd5Xck77BgQJSJ271rBueFKKVUo5fdv5lYpiEtMrAFqikh1EQkFegFzjecEhiVAd+90/YECW8NQSinlkaciEJHbRWQ/8A9gnojM9w6vKCLfAXh/2x8CzAe2ADOMMZu8bzESGC4i2/HsM/gkL3mUUkpdPj2zWCmlgkROZxbr1UeVUirIaREopVSQ0yJQSqkgp0WglFJBzi93FotIErDnCmcvAxz1YRx/oMscHHSZA19el7eqMSb6woF+WQR5ISLx2e01D2S6zMFBlznw5dfy6qYhpZQKcloESikV5IKxCMZbHcACuszBQZc58OXL8gbdPgKllFJ/FYxrBEoppbLQIlBKqSAXsEUgIh1FJEFEtovIqGzGh4nIdO/41SJSreBT+lYulnm4iGwWkT9EZJGIVLUipy9dapmzTHeHiBgR8etDDXOzvCLSw/v3vElEphR0Rl/Lxb/rKiKyRETWef9t32xFTl8SkU9F5IiIbMxhvIjI294/kz9EpFGePtAYE3APwAbsAK4CQoH1QL0LpnkIGOd93guYbnXuAljm1kBR7/MHg2GZvdNFAMuBVUCc1bnz+e+4JrAOKOl9Xdbq3AWwzOOBB73P6wG7rc7tg+W+EWgEbMxh/M3A94AAzYHVefm8QF0jaApsN8bsNMZkANOALhdM0wWY6H0+E2gj4r1btH+65DIbY5YYY1K8L1fhuSucP8vN3zPAC8CrQFpBhssHuVneQcB7xpgTAMaYIwWc0ddys8wGiPQ+LwEcKMB8+cIYsxw4fpFJugCfG49VeO72WOFKPy9QiyAG2Jfl9X7vsGynMZ6b55zEc3Mcf5WbZc5qIJ7fKPzZJZfZu8pc2RgzryCD5ZPc/B3XAmqJyEoRWSUiHQssXf7IzTI/C/T13iTrO+BfBRPNUpf7//2iLnnPYhV4RKQvEAe0tDpLfhKREOB14B6LoxQkO57NQ63wrPEtF5EGxphkS1Plr97ABGPM/4nIP4AvRKS+McZtdTB/EahrBIlA5SyvK3mHZTuNiNjxrFIeK5B0+SM3y4yItAWeBDobY9ILKFt+udQyRwD1gaUishvPttS5frzDODd/x/vx3BM80xizC9iKpxj8VW6WeSAwA8AY8wtQBM/F2QJZrv6/51agFsEaoKaIVBeRUDw7g+deMM1coL/3eXdgsfHuhfFTl1xmEYkFPsRTAv6+7RgusczGmJPGmDLGmGrGmGp49ot0Nsb4631Oc/PvejaetQFEpAyeTUU7CzKkj+VmmfcCbQBEpC6eIkgq0JQFby7Qz3v0UHPgpDHm4JW+WUBuGjLGOEVkCDAfz1EHnxpjNonI80C8MWYu8AmeVcjteHbK9LIucd7lcpnHAsWBL737xfcaYzpbFjqPcrnMASOXyzsfaC8imwEXMMIY47drurlc5seAj0RkGJ4dx/f4+S91iMhUPIVexrvv4xnAAWCMGYdnX8jNwHYgBRiQp8/z8z8vpZRSeRSom4aUUkrlkhaBUkoFOS0CpZQKcloESikV5LQIlFIqyGkRKKVUkNMiUEqpIPf/Sl7aEd0mLTEAAAAASUVORK5CYII=\n", 371 | "text/plain": [ 372 | "
" 373 | ] 374 | }, 375 | "metadata": { 376 | "tags": [], 377 | "needs_background": "light" 378 | } 379 | } 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": { 385 | "id": "gtnNE48UQSvu" 386 | }, 387 | "source": [ 388 | "We can build up the \"piecewise linear\" version of $T(x)$ from the following formula:\n", 389 | "\n", 390 | "$$ T_{piecewise}(x) = \\sum_i T_i N_i(x) $$ where $T_i$ are the discrete values of $T$ at each \"node\" and $N_i$ are functions that are \"local\" to the node. The functions $N_i(x)$ are called shape functions, and in this case, are just linear between neighbouring nodes: a triangular or tent-like shape. $N_i$ has value 1 at node $i$ and value 0 at all other nodes. The interactive plot below lets you build up $T$ by adding together the contributions from shape functions from 1 to 11" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "metadata": { 396 | "trusted": true, 397 | "id": "ea96GzElQSvv", 398 | "outputId": "dde18530-560d-4378-b690-63bf2a9ad93a", 399 | "colab": { 400 | "base_uri": "https://localhost:8080/", 401 | "height": 301, 402 | "referenced_widgets": [ 403 | "dfdaf8ee303f43bbbda0a76f09396220", 404 | "cc3c2018633a4007b36230fe78352f4b", 405 | "3ccbd2cf9c634c31936efc3c95c0e8cd", 406 | "a50d77c2c715494a90dfc9a6e491ddfc", 407 | "663fdb6fa5b8424a9e5a8ce25a41da23", 408 | "de608d5fc21a4866b725cd74a04a6bd6", 409 | "ff37659491e4482384481242650a91cf" 410 | ] 411 | } 412 | }, 413 | "source": [ 414 | "def plot(n):\n", 415 | " x = linspace(0, 1.0, 11)\n", 416 | " T = sin(5*x)\n", 417 | " # Plot T as given by the first 'n' shape functions\n", 418 | " T[n:] = 0.0\n", 419 | " plt.plot(x, T, marker='o', color='k')\n", 420 | " \n", 421 | " # Plot shape function n \n", 422 | " for i in range(1, n+1):\n", 423 | " Ni = zeros(11)\n", 424 | " Ni[i-1] = 1.0\n", 425 | " plt.plot(x, Ni)\n", 426 | " plt.text(0.1*(i-1), 1, '$N_{%d}$'%(i-1), size=16)\n", 427 | " plt.ylim(-1.5, 1.5)\n", 428 | "\n", 429 | "interact(plot, n=(1,11,1));" 430 | ], 431 | "execution_count": 3, 432 | "outputs": [ 433 | { 434 | "output_type": "display_data", 435 | "data": { 436 | "application/vnd.jupyter.widget-view+json": { 437 | "model_id": "dfdaf8ee303f43bbbda0a76f09396220", 438 | "version_minor": 0, 439 | "version_major": 2 440 | }, 441 | "text/plain": [ 442 | "interactive(children=(IntSlider(value=6, description='n', max=11, min=1), Output()), _dom_classes=('widget-int…" 443 | ] 444 | }, 445 | "metadata": { 446 | "tags": [] 447 | } 448 | } 449 | ] 450 | }, 451 | { 452 | "cell_type": "markdown", 453 | "metadata": { 454 | "id": "4YtnOTG-QSv0" 455 | }, 456 | "source": [ 457 | "We can also calculate the derivatives, $$ {d\\over dx}T_{piecewise}(x) = \\sum_i T_i {dN_i\\over dx} $$ \n", 458 | "\n", 459 | "For these simple shape functions $N_i$, the derivatives are piecewise constant (and hence the second derivative is zero)." 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "metadata": { 465 | "trusted": true, 466 | "id": "a3a3pAUCQSv1", 467 | "outputId": "e4ad3f0b-d869-49c9-cc41-6b1296d91215" 468 | }, 469 | "source": [ 470 | "# For example, dN5/dx looks like this:\n", 471 | "x = [0.0, 0.4, 0.4, 0.5, 0.5, 0.6, 0.6, 1.0]\n", 472 | "y = [0.0, 0.0, 0.1, 0.1, -0.1, -0.1, 0.0, 0.0]\n", 473 | "plt.plot(x, y)\n", 474 | "plt.title('$dN_5/dx$')\n", 475 | "plt.show()" 476 | ], 477 | "execution_count": null, 478 | "outputs": [ 479 | { 480 | "output_type": "display_data", 481 | "data": { 482 | "image/png": "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\n", 483 | "text/plain": [ 484 | "" 485 | ] 486 | }, 487 | "metadata": { 488 | "tags": [] 489 | } 490 | } 491 | ] 492 | }, 493 | { 494 | "cell_type": "markdown", 495 | "metadata": { 496 | "id": "q40eE2n6QSv5" 497 | }, 498 | "source": [ 499 | "### Variational method\n", 500 | "\n", 501 | "Returning to the mathematical description of the heat equation,\n", 502 | "assuming $k$ and $A$ to be constant, it can also be written as follows,\n", 503 | "\n", 504 | "$$ \\int_0^1 w(x) {d\\over dx}\\left({dT\\over dx}\\right) dx = 0 $$\n", 505 | "\n", 506 | "simply by multiplying by an arbitrary function $w(x)$ and integrating. At first, there may seem to be little gained by writing it in this way, but by manipulating this equation, we can first simplify, then localise the equation, and make it possible to solve numerically.\n", 507 | "\n", 508 | "Integration by parts gives:\n", 509 | "\n", 510 | "$$ - \\int_0^1 {dw\\over dx} {dT\\over dx} dx + \\left[w {dT\\over dx}\\right]_0^1= 0$$ \n", 511 | "\n", 512 | "For now, let's consider the integral, and worry about the boundary terms later." 513 | ] 514 | }, 515 | { 516 | "cell_type": "markdown", 517 | "metadata": { 518 | "collapsed": true, 519 | "id": "HsGejC14QSv6" 520 | }, 521 | "source": [ 522 | "### Finite Element method\n", 523 | "\n", 524 | "For a numerical approximation, we will replace both $w(x)$ and $T(x)$ with piecewise approximations $w_{piecewise}(x), T_{piecewise}(x)$ to get:\n", 525 | "\n", 526 | "$$ \\int_0^1 \\sum_{i,j} w_i {dN_i\\over dx} T_j {dN_j\\over dx} dx = 0 $$\n", 527 | "\n", 528 | "Note that we need separate iterators $i$ and $j$ for $w_i$ and $T_j$. If we can solve this equation to get all the $T_j$ values, we will have a numerical solution to the problem.\n", 529 | "\n", 530 | "The derivatives $dN_i\\over dx$ are just $+1/\\delta x$, $-1/\\delta x$, or 0, depending on which segment, or \"element\" you are in, where $\\delta x$ is the spacing between points (0.1 in this case). \n", 531 | "\n", 532 | "For example, if we take the \"element\" between 0.4 and 0.5, we see that ${dN_4\\over dx} = -{1\\over \\delta x}$ and ${dN_5\\over dx} = {1\\over\\delta x}$. \n", 533 | "\n", 534 | "All other derivatives ${dN_i\\over dx} = 0$. \n", 535 | "\n", 536 | "As a result, we can break down the integral into small pieces, over each \"element\", which are easy to evaluate:\n", 537 | "\n", 538 | "$$ \\int_{0.4}^{0.5} \\sum_{i,j} w_i {dN_i\\over dx} T_j {dN_j\\over dx} dx = {w_4 T_4 - w_4 T_5 - w_5 T_4 + w_5 T_5 \\over \\delta x^2} \\delta x$$ \n", 539 | "\n", 540 | "$$ \\int_{0.5}^{0.6} \\sum_{i,j} w_i {dN_i\\over dx} T_j {dN_j\\over dx} dx = {w_5 T_5 - w_5 T_6 - w_6 T_5 + w_6 T_6 \\over \\delta x^2} \\delta x$$ \n", 541 | "\n", 542 | "etc., and we could continue for the whole range from $x=0$ to $x=1$, integrating each element individually, and summing the contributions from each of them. \n", 543 | "\n", 544 | "\n" 545 | ] 546 | }, 547 | { 548 | "cell_type": "markdown", 549 | "metadata": { 550 | "collapsed": true, 551 | "id": "vtYhocYwQSv7" 552 | }, 553 | "source": [ 554 | "We can also write out each integral in a matrix form, like this:\n", 555 | "\n", 556 | "$$ \\int_{0.4}^{0.5} \\sum_{i,j} w_i {dN_i\\over dx} T_j {dN_j\\over dx} dx = {1\\over\\delta x}(\n", 557 | "\\begin{array}{cc}\n", 558 | "w_4 & w_5 \\\\\n", 559 | "\\end{array})\n", 560 | "\\left(\n", 561 | "\\begin{array}{cc} \n", 562 | "1 & -1 \\\\\n", 563 | "-1 & 1 \\\\\n", 564 | "\\end{array}\n", 565 | "\\right)\n", 566 | "\\left(\n", 567 | "\\begin{array}{c}\n", 568 | "T_4 \\\\\n", 569 | "T_5 \\\\\n", 570 | "\\end{array}\n", 571 | "\\right) $$\n", 572 | "\n", 573 | "Summing all the integrals gives the following:\n", 574 | "\n", 575 | "$$ \\int_0^1 \\sum_{i,j} w_i {dN_i\\over dx} T_j {dN_j\\over dx} dx = {1\\over\\delta x} \n", 576 | "[\\begin{array}{cccc} \n", 577 | "w_0 & w_1 & \\cdots & w_n \n", 578 | "\\end{array}]\n", 579 | "\\left(\n", 580 | "\\begin{array}{cccccc}\n", 581 | "1 & -1 & 0 & 0 & 0 & \\cdots & 0 \\\\\n", 582 | "-1 & 2 & -1 & 0 & 0 & \\cdots & 0 \\\\\n", 583 | "0 & -1 & 2 & -1 & 0 & \\cdots & 0 \\\\\n", 584 | "\\cdots & & & & \\\\\n", 585 | "\\end{array}\\right)\n", 586 | "\\left(\n", 587 | "\\begin{array}{c}\n", 588 | "T_0 \\\\ T_1 \\\\ \\cdots \\\\ \\cdots \\\\ T_n\n", 589 | "\\end{array}\n", 590 | "\\right) = \\left(\\begin{array}{c}0\\\\0\\\\ \\cdot\\\\ \\cdot\\\\ 0 \\end{array}\\right)\n", 591 | "$$\n", 592 | "\n", 593 | "Because of the arbitrariness of $w(x)$ and hence $w_i$, we can consider each $w_i$ in turn, and assume all the other $w_{j\\neq i} = 0$. Each row of the matrix, multiplied by the vector $T$ must be zero in turn, and we can discard $w$ altogether. We have thus reduced the problem to a matrix equation $A.T = 0$ where $A$ in this case is tridiagonal. Note that we could have got the same matrix equation from a finite-difference approximation." 594 | ] 595 | }, 596 | { 597 | "cell_type": "markdown", 598 | "metadata": { 599 | "collapsed": true, 600 | "id": "uGs5gmrvQSv9" 601 | }, 602 | "source": [ 603 | "#### Boundary Conditions\n", 604 | "Above, we ignored the $[w dT/dx]_0^1$ term, and so far, we haven't specified any boundary condition. Since $w$ is arbitrary, we can just let it be zero, and ignore this term. This is equivalent to a \"natural\" or \"Neumann\" boundary condition. If we wish to impose a hard or \"Dirichlet\" boundary condition, we will have to do something to our matrix above. For example, if we wish $T_0$ to be zero, we can clear the first row of the matrix, and set the diagonal to 1. Let's fully illustrate the example by solving the problem with the Dirichlet BCs described above." 605 | ] 606 | }, 607 | { 608 | "cell_type": "code", 609 | "metadata": { 610 | "trusted": true, 611 | "id": "e-trvaHNQSv-", 612 | "outputId": "c5672609-dab3-4048-d4c9-e36406064c3c" 613 | }, 614 | "source": [ 615 | "# Set up matrix problem A.T = b (where T is the unknown temperature)\n", 616 | "\n", 617 | "# Full matrix, as shown above\n", 618 | "A = zeros((11, 11))\n", 619 | "# Element matrix, representing laplace operator on each element\n", 620 | "Ke = array([[1.0, -1.0], \n", 621 | " [-1.0, 1.0]])\n", 622 | "# Insert element matrix into full matrix 10 times\n", 623 | "for i in range(10):\n", 624 | " A[i:i+2, i:i+2] += Ke\n", 625 | "print('A = \\n', A, '\\n')\n", 626 | "\n", 627 | "# Right hand side vector (zero)\n", 628 | "b = zeros(11)\n", 629 | "\n", 630 | "# Introduce a source term on the RHS(!)\n", 631 | "b += 0\n", 632 | "\n", 633 | "# Clear a row and set diagonal to one, for Dirichlet BC\n", 634 | "# Set BC T=100 at x=0\n", 635 | "A[0, :] = 0.0\n", 636 | "A[0, 0] = 1.0\n", 637 | "b[0] = 100.0\n", 638 | "\n", 639 | "# Set BC T=0 at x=1\n", 640 | "A[10, :] = 0.0\n", 641 | "A[10, 10] = 1.0\n", 642 | "b[10] = 0.0\n", 643 | "\n", 644 | "print('Modified A = \\n', A, '\\n')\n", 645 | "print('RHS b = ', b)\n", 646 | "\n", 647 | "from numpy.linalg import solve\n", 648 | "T = solve(A, b)\n", 649 | "\n", 650 | "print('Solution T =', T)" 651 | ], 652 | "execution_count": null, 653 | "outputs": [ 654 | { 655 | "output_type": "stream", 656 | "text": [ 657 | "A = \n", 658 | " [[ 1.00 -1.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 659 | " [-1.00 2.00 -1.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 660 | " [ 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 661 | " [ 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 662 | " [ 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00 0.00 0.00]\n", 663 | " [ 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00 0.00]\n", 664 | " [ 0.00 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00]\n", 665 | " [ 0.00 0.00 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00]\n", 666 | " [ 0.00 0.00 0.00 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00]\n", 667 | " [ 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00]\n", 668 | " [ 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 -1.00 1.00]] \n", 669 | "\n", 670 | "Modified A = \n", 671 | " [[ 1.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 672 | " [-1.00 2.00 -1.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 673 | " [ 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 674 | " [ 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 675 | " [ 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00 0.00 0.00]\n", 676 | " [ 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00 0.00]\n", 677 | " [ 0.00 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00 0.00]\n", 678 | " [ 0.00 0.00 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00 0.00]\n", 679 | " [ 0.00 0.00 0.00 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00 0.00]\n", 680 | " [ 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 -1.00 2.00 -1.00]\n", 681 | " [ 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 1.00]] \n", 682 | "\n", 683 | "RHS b = [100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00]\n", 684 | "Solution T = [100.00 90.00 80.00 70.00 60.00 50.00 40.00 30.00 20.00 10.00 0.00]\n" 685 | ], 686 | "name": "stdout" 687 | } 688 | ] 689 | }, 690 | { 691 | "cell_type": "markdown", 692 | "metadata": { 693 | "id": "_CILghlYQSwD" 694 | }, 695 | "source": [ 696 | "As predicted, the solution is linear." 697 | ] 698 | }, 699 | { 700 | "cell_type": "code", 701 | "metadata": { 702 | "trusted": true, 703 | "id": "G9xk42hxQSwE", 704 | "outputId": "18bc0919-fd9c-4fd2-a70b-ed43b50882da" 705 | }, 706 | "source": [ 707 | "x = linspace(0.0, 1.0, 11)\n", 708 | "plt.plot(x,T)\n", 709 | "plt.show()" 710 | ], 711 | "execution_count": null, 712 | "outputs": [ 713 | { 714 | "output_type": "display_data", 715 | "data": { 716 | "image/png": "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\n", 717 | "text/plain": [ 718 | "" 719 | ] 720 | }, 721 | "metadata": { 722 | "tags": [] 723 | } 724 | } 725 | ] 726 | }, 727 | { 728 | "cell_type": "code", 729 | "metadata": { 730 | "trusted": true, 731 | "id": "5RHkYEr-QSwJ" 732 | }, 733 | "source": [ 734 | "" 735 | ], 736 | "execution_count": null, 737 | "outputs": [] 738 | } 739 | ] 740 | } -------------------------------------------------------------------------------- /3D7 - Basic FEM - heat equation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "kernelspec": { 6 | "display_name": "Python 3", 7 | "language": "python", 8 | "name": "python3" 9 | }, 10 | "language_info": { 11 | "codemirror_mode": { 12 | "name": "ipython", 13 | "version": 3 14 | }, 15 | "file_extension": ".py", 16 | "mimetype": "text/x-python", 17 | "name": "python", 18 | "nbconvert_exporter": "python", 19 | "pygments_lexer": "ipython3", 20 | "version": "3.5.4" 21 | }, 22 | "colab": { 23 | "name": "3D7 - Basic FEM - heat equation.ipynb", 24 | "provenance": [], 25 | "include_colab_link": true 26 | } 27 | }, 28 | "cells": [ 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "id": "view-in-github", 33 | "colab_type": "text" 34 | }, 35 | "source": [ 36 | "\"Open" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": { 42 | "id": "q-9rHoChR-ej" 43 | }, 44 | "source": [ 45 | "# Basic FEM Example: Heat Equation\n", 46 | "\n", 47 | "First import the required libraries for basic algebra and plotting" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "metadata": { 53 | "id": "6k4fzuB4R-el" 54 | }, 55 | "source": [ 56 | "from numpy import *\n", 57 | "set_printoptions(2, suppress=True)\n", 58 | "import matplotlib.pyplot as plt\n", 59 | "%matplotlib inline" 60 | ], 61 | "execution_count": 1, 62 | "outputs": [] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": { 67 | "id": "YhPPo81AR-eu" 68 | }, 69 | "source": [ 70 | "### Mesh\n", 71 | "We define the \"mesh\" in terms of its geometry and topology. The geometry is just a set of $(x, y)$ points - a $(p \\times 2)$ floating point array, where $p$ is the number of points. The topology is the list of points which make up each triangle, hence it is a $(n\\times 3)$ integer array, where $n$ is the number of triangles. Note that numbering starts from zero. Also, the order of the points (i.e. triangle orientation) is important." 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "metadata": { 77 | "id": "DShH-MaxR-ew" 78 | }, 79 | "source": [ 80 | "geometry = array([[0.0, 0.0],\n", 81 | " [2.0, 0.0],\n", 82 | " [0.0, 1.0],\n", 83 | " [2.0, 1.0]])\n", 84 | "\n", 85 | "topology = array([[0, 1, 2],\n", 86 | " [1, 3, 2]])\n", 87 | "\n", 88 | "mesh = (geometry, topology)" 89 | ], 90 | "execution_count": 2, 91 | "outputs": [] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": { 96 | "id": "Bz2hm-wYR-e5" 97 | }, 98 | "source": [ 99 | "Here is a simple plotting routine to display the mesh" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "metadata": { 105 | "id": "kVmeEPjER-e7" 106 | }, 107 | "source": [ 108 | "def plot(mesh):\n", 109 | " geom, topo = mesh\n", 110 | " x = geom[:,0]\n", 111 | " y = geom[:,1]\n", 112 | " plt.triplot(x, y, topo)\n", 113 | " xmax = x.max()\n", 114 | " xmin = x.min()\n", 115 | " ymax = y.max()\n", 116 | " ymin = y.min()\n", 117 | " dx = 0.1*(xmax - xmin)\n", 118 | " dy = 0.1*(ymax - ymin)\n", 119 | " plt.xlim(xmin-dx, xmax+dx)\n", 120 | " plt.ylim(ymin-dy, ymax+dy)\n", 121 | " return\n" 122 | ], 123 | "execution_count": 3, 124 | "outputs": [] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "metadata": { 129 | "id": "lxFZG4pCR-fB", 130 | "outputId": "bee25de5-2686-4e06-fb1b-2bb73625f40c", 131 | "colab": { 132 | "base_uri": "https://localhost:8080/", 133 | "height": 265 134 | } 135 | }, 136 | "source": [ 137 | "plot(mesh)" 138 | ], 139 | "execution_count": 4, 140 | "outputs": [ 141 | { 142 | "output_type": "display_data", 143 | "data": { 144 | "image/png": "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\n", 145 | "text/plain": [ 146 | "
" 147 | ] 148 | }, 149 | "metadata": { 150 | "tags": [], 151 | "needs_background": "light" 152 | } 153 | } 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": { 159 | "id": "PevXNxprR-fH" 160 | }, 161 | "source": [ 162 | "### Element matrix\n", 163 | "The \"element stiffness matrix\", $K$ depends on the conductivity tensor $D$ and the triangle geometry.\n", 164 | "\n", 165 | "$$ K_e = \\int_{\\Omega_e} B_e^T D B_e d\\Omega $$\n" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "metadata": { 171 | "id": "4l1Tuw6IR-fI" 172 | }, 173 | "source": [ 174 | "def Kmat(D, p, geometry):\n", 175 | " ''' Calculate K from the D matrix and the points '''\n", 176 | " # D contains the conductivity tensor\n", 177 | " # p contains the indices of the three points\n", 178 | " # making up the triangle\n", 179 | " assert(len(p) == 3)\n", 180 | " x0, y0 = geometry[p[0]]\n", 181 | " x1, y1 = geometry[p[1]]\n", 182 | " x2, y2 = geometry[p[2]]\n", 183 | "\n", 184 | " # Element area Ae\n", 185 | " Ae = 0.5*abs((x0 - x1)*(y2 - y1) - (y0 - y1)*(x2 - x1))\n", 186 | "\n", 187 | " # 'B' Matrix - representing the 'gradient' operator\n", 188 | " B = array([[y1 - y2, y2 - y0, y0 - y1],\n", 189 | " [x2 - x1, x0 - x2, x1 - x0]])/(2*Ae)\n", 190 | "\n", 191 | " K = Ae*matmul(B.transpose(), matmul(D, B))\n", 192 | " return K" 193 | ], 194 | "execution_count": 5, 195 | "outputs": [] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": { 200 | "id": "jDC4biQcR-fN" 201 | }, 202 | "source": [ 203 | "### Matrix assembly (LHS)\n", 204 | "The process of combining the local element matrices into a larger global matrix is called \"assembly\". It is really just a case of relabelling the indices with their global values, and adding the local matrices together." 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "metadata": { 210 | "id": "W_NetHAJR-fO" 211 | }, 212 | "source": [ 213 | "def assemble_matrix(mesh, Dmat):\n", 214 | " geom, topo = mesh\n", 215 | " np = len(geom)\n", 216 | " Kglobal = zeros((np, np))\n", 217 | " \n", 218 | " for tri in topo:\n", 219 | " K = Kmat(Dmat, tri, geom)\n", 220 | " for i, idx in enumerate(tri):\n", 221 | " for j, jdx in enumerate(tri):\n", 222 | " Kglobal[idx, jdx] += K[i, j]\n", 223 | " return Kglobal" 224 | ], 225 | "execution_count": 6, 226 | "outputs": [] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "metadata": { 231 | "id": "2U5aWd3IR-fV" 232 | }, 233 | "source": [ 234 | "Dmat = array([[5.0, 0.0],\n", 235 | " [0.0, 5.0]])" 236 | ], 237 | "execution_count": 7, 238 | "outputs": [] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "metadata": { 243 | "id": "vzdr63-2R-fa" 244 | }, 245 | "source": [ 246 | "Kglobal = assemble_matrix(mesh, Dmat)" 247 | ], 248 | "execution_count": 8, 249 | "outputs": [] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "metadata": { 254 | "id": "vr2RuCqGR-fe" 255 | }, 256 | "source": [ 257 | "The fully assembled left-hand side (LHS) looks like this:" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "metadata": { 263 | "id": "IfraziGQR-ff", 264 | "outputId": "5174d09f-12e0-40c3-9f2c-3593a4c22ce0", 265 | "colab": { 266 | "base_uri": "https://localhost:8080/", 267 | "height": 85 268 | } 269 | }, 270 | "source": [ 271 | "print(Kglobal)" 272 | ], 273 | "execution_count": 9, 274 | "outputs": [ 275 | { 276 | "output_type": "stream", 277 | "text": [ 278 | "[[ 6.25 -1.25 -5. 0. ]\n", 279 | " [-1.25 6.25 0. -5. ]\n", 280 | " [-5. 0. 6.25 -1.25]\n", 281 | " [ 0. -5. -1.25 6.25]]\n" 282 | ], 283 | "name": "stdout" 284 | } 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": { 290 | "id": "RAMBIlFcR-fk" 291 | }, 292 | "source": [ 293 | "### Vector assembly (RHS)\n", 294 | "Now we need to perform RHS assembly on the vector $f$. This is similar to the matrix assembly above.\n", 295 | "\n", 296 | "The individual vector for each element is defined in `fvec()` and they are combined together in `assemble_vector()`." 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "metadata": { 302 | "id": "NezaFbmTR-fk" 303 | }, 304 | "source": [ 305 | "def fvec(s, p, geom):\n", 306 | " assert(len(p) == 3)\n", 307 | " x0, y0 = geometry[p[0]]\n", 308 | " x1, y1 = geometry[p[1]]\n", 309 | " x2, y2 = geometry[p[2]]\n", 310 | "\n", 311 | " # Element area Ae\n", 312 | " Ae = 0.5*abs((x0 - x1)*(y2 - y1) - (y0 - y1)*(x2 - x1))\n", 313 | " return s*Ae/3.0*ones(3)\n", 314 | "\n", 315 | "def assemble_vector(mesh, s):\n", 316 | " geom, topo = mesh\n", 317 | " fg = zeros(len(geom))\n", 318 | " for tri in topo:\n", 319 | " f = fvec(s, tri, geom)\n", 320 | " for i, idx in enumerate(tri):\n", 321 | " fg[idx] += f[i]\n", 322 | " return fg\n", 323 | "\n", 324 | "fglobal = assemble_vector(mesh, 3.0)" 325 | ], 326 | "execution_count": 10, 327 | "outputs": [] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": { 332 | "id": "lkXPd6oPR-fp" 333 | }, 334 | "source": [ 335 | "### Boundary flux terms\n", 336 | "There are also some boundary flux terms in the example. Here we will add them directly to $f$." 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "metadata": { 342 | "id": "kgpuqc2WR-fp" 343 | }, 344 | "source": [ 345 | "fglobal += array([0.0, -10.0, 0.0, -10.0])" 346 | ], 347 | "execution_count": 11, 348 | "outputs": [] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": { 353 | "id": "7C6J0d1sR-fu" 354 | }, 355 | "source": [ 356 | "### Boundary conditions \n", 357 | "\n", 358 | "Dirichlet boundary conditions can be enforced by zeroing a row of the $K$ matrix, setting the diagonal entry to 1, and putting the desired value in the RHS $f$ vector" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "metadata": { 364 | "id": "y9qEEUw2R-fv" 365 | }, 366 | "source": [ 367 | "def set_bc(K, f, row, val):\n", 368 | " # Set BC\n", 369 | " K[row] = 0.0\n", 370 | " K[row, row] = 1.0\n", 371 | " f[row] = val\n", 372 | "\n", 373 | "set_bc(Kglobal, fglobal, 0, 0.0) \n", 374 | "set_bc(Kglobal, fglobal, 2, 0.0)" 375 | ], 376 | "execution_count": 12, 377 | "outputs": [] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": { 382 | "id": "I8fN0CkcR-f1" 383 | }, 384 | "source": [ 385 | "Finally, we can refer the $K.u = f$ problem to a linear algebra solver which will use an LU method to get the answer" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "metadata": { 391 | "id": "jflTNLivR-f2", 392 | "outputId": "81f83765-0a33-407d-e7ff-b794b1f518a0", 393 | "colab": { 394 | "base_uri": "https://localhost:8080/", 395 | "height": 34 396 | } 397 | }, 398 | "source": [ 399 | "u = linalg.solve(Kglobal, fglobal)\n", 400 | "print(u)\n" 401 | ], 402 | "execution_count": 13, 403 | "outputs": [ 404 | { 405 | "output_type": "stream", 406 | "text": [ 407 | "[-0. -6.76 0. -6.84]\n" 408 | ], 409 | "name": "stdout" 410 | } 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "metadata": { 416 | "id": "xYzZ76vcR-f5" 417 | }, 418 | "source": [ 419 | "" 420 | ], 421 | "execution_count": 13, 422 | "outputs": [] 423 | } 424 | ] 425 | } -------------------------------------------------------------------------------- /3D7 - Heat Equation - very large mesh.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "kernelspec": { 6 | "name": "python3", 7 | "display_name": "Python 3", 8 | "language": "python" 9 | }, 10 | "language_info": { 11 | "mimetype": "text/x-python", 12 | "nbconvert_exporter": "python", 13 | "name": "python", 14 | "file_extension": ".py", 15 | "version": "3.5.4", 16 | "pygments_lexer": "ipython3", 17 | "codemirror_mode": { 18 | "version": 3, 19 | "name": "ipython" 20 | } 21 | }, 22 | "colab": { 23 | "name": "3D7 - Heat Equation - very large mesh.ipynb", 24 | "provenance": [], 25 | "include_colab_link": true 26 | } 27 | }, 28 | "cells": [ 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "id": "view-in-github", 33 | "colab_type": "text" 34 | }, 35 | "source": [ 36 | "\"Open" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": { 42 | "id": "HfbyMIaJSuFx" 43 | }, 44 | "source": [ 45 | "# Heat Equation - with a larger mesh\n", 46 | "### Sparse linear algebra\n", 47 | "\n", 48 | "First import the required libraries for basic algebra and plotting. For larger problems, we use the scipy\n", 49 | "sparse package, only storing non-zero entries in the LHS matrix." 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "metadata": { 55 | "trusted": true, 56 | "id": "Rm4W6yXFSuFz" 57 | }, 58 | "source": [ 59 | "from numpy import *\n", 60 | "from scipy.sparse import csr_matrix, lil_matrix\n", 61 | "from scipy.sparse.linalg import spsolve\n", 62 | "set_printoptions(suppress=True)\n", 63 | "import matplotlib.pyplot as plt\n", 64 | "%matplotlib inline" 65 | ], 66 | "execution_count": 1, 67 | "outputs": [] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": { 72 | "id": "imL6xqS2SuF8" 73 | }, 74 | "source": [ 75 | "### Mesh\n", 76 | "\n", 77 | "The mesh is made the same way as before." 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "metadata": { 83 | "trusted": true, 84 | "id": "rAzLybLHSuF9" 85 | }, 86 | "source": [ 87 | "\n", 88 | "# Make a rectangular mesh of triangles, nx by ny \n", 89 | "nx = 250\n", 90 | "ny = 250\n", 91 | "\n", 92 | "c = 0\n", 93 | "geometry = zeros((nx*ny, 2), dtype='float')\n", 94 | "for i in range(nx):\n", 95 | " for j in range(ny):\n", 96 | " geometry[c] = [float(i)/(nx-1), float(j)/(ny-1)]\n", 97 | " c += 1\n", 98 | "\n", 99 | "ntri = (nx - 1)*(ny - 1)*2\n", 100 | "topology = zeros((ntri, 3), dtype='int')\n", 101 | "\n", 102 | "c = 0\n", 103 | "for i in range(nx - 1):\n", 104 | " for j in range(ny - 1):\n", 105 | " ij = j + i*ny\n", 106 | " topology[c] = [ij, ij+ny, ij+ny+1]\n", 107 | " topology[c + 1] = [ij+1, ij, ij+ny+1]\n", 108 | " c += 2\n", 109 | " \n", 110 | "mesh = (geometry, topology)" 111 | ], 112 | "execution_count": 2, 113 | "outputs": [] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": { 118 | "id": "ZqZhkoQRSuGC" 119 | }, 120 | "source": [ 121 | "Here is a simple plotting routine to display the data values" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "metadata": { 127 | "trusted": true, 128 | "id": "CsJZ5TAZSuGD" 129 | }, 130 | "source": [ 131 | "def plot(mesh, data):\n", 132 | " geom, topo = mesh\n", 133 | " x = geom[:,0]\n", 134 | " y = geom[:,1]\n", 135 | "\n", 136 | " plt.gca(aspect='equal')\n", 137 | " plt.tricontourf(x, y, topo, data, 40)\n", 138 | " \n", 139 | " xmax = x.max()\n", 140 | " xmin = x.min()\n", 141 | " ymax = y.max()\n", 142 | " ymin = y.min()\n", 143 | " dx = 0.1*(xmax - xmin)\n", 144 | " dy = 0.1*(ymax - ymin)\n", 145 | " plt.xlim(xmin-dx, xmax+dx)\n", 146 | " plt.ylim(ymin-dy, ymax+dy)\n", 147 | " return\n" 148 | ], 149 | "execution_count": 3, 150 | "outputs": [] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": { 155 | "id": "WiEMgjZ5SuGH" 156 | }, 157 | "source": [ 158 | "### Element matrix\n", 159 | "The \"element stiffness matrix\", $K$ depends on the conductivity tensor $D$ and the triangle geometry.\n", 160 | "\n", 161 | "$$ K_e = \\int_{\\Omega_e} B_e^T D B_e d\\Omega $$\n" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "metadata": { 167 | "trusted": true, 168 | "id": "Dn_luRBKSuGI" 169 | }, 170 | "source": [ 171 | "def Kmat(D, p, geometry):\n", 172 | " ''' Calculate K from the D matrix and the points '''\n", 173 | " # D contains the conductivity tensor\n", 174 | " # p contains the indices of the three points\n", 175 | " # making up the triangle\n", 176 | " assert(len(p) == 3)\n", 177 | " x0, y0 = geometry[p[0]]\n", 178 | " x1, y1 = geometry[p[1]]\n", 179 | " x2, y2 = geometry[p[2]]\n", 180 | "\n", 181 | " # Element area Ae\n", 182 | " Ae = 0.5*abs((x0 - x1)*(y2 - y1) - (y0 - y1)*(x2 - x1))\n", 183 | "\n", 184 | " # 'B' Matrix - representing the 'gradient' operator\n", 185 | " B = array([[y1 - y2, y2 - y0, y0 - y1],\n", 186 | " [x2 - x1, x0 - x2, x1 - x0]])/(2*Ae)\n", 187 | "\n", 188 | " K = Ae*matmul(B.transpose(), matmul(D, B))\n", 189 | " return K" 190 | ], 191 | "execution_count": 4, 192 | "outputs": [] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": { 197 | "id": "-8TMJiHQSuGN" 198 | }, 199 | "source": [ 200 | "### Matrix assembly (LHS)\n", 201 | "The process of combining the local element matrices into a larger global matrix is called \"assembly\". It is really just a case of relabelling the indices with their global values, and adding the local matrices together. Now, we use the `scipy.sparse.lil_matrix` which only stores the non-zero entries." 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "metadata": { 207 | "trusted": true, 208 | "id": "UutfeMhPSuGN" 209 | }, 210 | "source": [ 211 | "def assemble_matrix(mesh, Dmat):\n", 212 | " geom, topo = mesh\n", 213 | " np = len(geom)\n", 214 | " Kglobal = lil_matrix((np, np))\n", 215 | " \n", 216 | " for tri in topo:\n", 217 | " K = Kmat(Dmat, tri, geom)\n", 218 | " for i, idx in enumerate(tri):\n", 219 | " for j, jdx in enumerate(tri):\n", 220 | " Kglobal[idx, jdx] += K[i, j]\n", 221 | " return Kglobal" 222 | ], 223 | "execution_count": 5, 224 | "outputs": [] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "metadata": { 229 | "trusted": true, 230 | "id": "i064Egt_SuGS" 231 | }, 232 | "source": [ 233 | "Dmat = array([[15.0, 0.0],\n", 234 | " [0.0, 5.0]])" 235 | ], 236 | "execution_count": 6, 237 | "outputs": [] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "metadata": { 242 | "trusted": true, 243 | "id": "ASagR8n1SuGY" 244 | }, 245 | "source": [ 246 | "Kglobal = assemble_matrix(mesh, Dmat)" 247 | ], 248 | "execution_count": 7, 249 | "outputs": [] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "metadata": { 254 | "id": "1SrxV4IKSuGe" 255 | }, 256 | "source": [ 257 | "### RHS assembly\n", 258 | "\n", 259 | "As before, we perform RHS assembly on the vector $f$, combining the local vector entries from each element. Each element has a constant source term $s$.\n" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "metadata": { 265 | "trusted": true, 266 | "id": "fmUyrUI3SuGe" 267 | }, 268 | "source": [ 269 | "def fvec(s, p, geom):\n", 270 | " assert(len(p) == 3)\n", 271 | " x0, y0 = geometry[p[0]]\n", 272 | " x1, y1 = geometry[p[1]]\n", 273 | " x2, y2 = geometry[p[2]]\n", 274 | "\n", 275 | " # Element area Ae\n", 276 | " Ae = 0.5*abs((x0 - x1)*(y2 - y1) - (y0 - y1)*(x2 - x1))\n", 277 | " return s*Ae/3.0*ones(3)\n", 278 | "\n", 279 | "def assemble_vector(mesh, s):\n", 280 | " geom, topo = mesh\n", 281 | " fg = zeros(len(geom))\n", 282 | " for tri in topo:\n", 283 | " f = fvec(s, tri, geom)\n", 284 | " for i, idx in enumerate(tri):\n", 285 | " fg[idx] += f[i]\n", 286 | " return fg\n", 287 | "\n", 288 | "fglobal = assemble_vector(mesh, 3.0)" 289 | ], 290 | "execution_count": 8, 291 | "outputs": [] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "metadata": { 296 | "trusted": true, 297 | "id": "ABII0pL7SuGj" 298 | }, 299 | "source": [ 300 | "def set_bc(K, f, row, val):\n", 301 | " # Set BC\n", 302 | " K[row,:] = 0.0\n", 303 | " K[row, row] = 1.0\n", 304 | " f[row] = val\n", 305 | "\n", 306 | "# Set zero BC on bottom edge\n", 307 | "for i in range(nx):\n", 308 | " set_bc(Kglobal, fglobal, i*ny, 0.0)\n", 309 | "\n", 310 | "# Set u=10 BC on left edge\n", 311 | "for j in range(ny):\n", 312 | " y = (j/(ny+1))\n", 313 | " set_bc(Kglobal, fglobal, j, 0.04*sin(2*pi*y))\n" 314 | ], 315 | "execution_count": 9, 316 | "outputs": [] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": { 321 | "id": "i9WkRimpSuGo" 322 | }, 323 | "source": [ 324 | "Finally, we can refer the $K.u = f$ problem to a sparse linear algebra solver which will use an LU method to get the answer. The solver requires the matrix in CSR format. " 325 | ] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "metadata": { 330 | "trusted": true, 331 | "id": "T2ooql38SuGo", 332 | "outputId": "60151362-b8e1-4913-ca66-8485e6b2d75f", 333 | "colab": { 334 | "base_uri": "https://localhost:8080/", 335 | "height": 51 336 | } 337 | }, 338 | "source": [ 339 | "u = spsolve(Kglobal.tocsr(), fglobal)\n", 340 | "print(u.max(), u.min())\n", 341 | "print('Number of DOFS=', len(u))" 342 | ], 343 | "execution_count": 10, 344 | "outputs": [ 345 | { 346 | "output_type": "stream", 347 | "text": [ 348 | "0.07354042572686259 -0.039999216713052996\n", 349 | "Number of DOFS= 62500\n" 350 | ], 351 | "name": "stdout" 352 | } 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "metadata": { 358 | "trusted": true, 359 | "id": "lSjzfqo8SuGt", 360 | "outputId": "9783aae3-c9a6-46bf-e196-2760fe416ae5", 361 | "colab": { 362 | "base_uri": "https://localhost:8080/", 363 | "height": 700 364 | } 365 | }, 366 | "source": [ 367 | "plt.figure(figsize=(12,12))\n", 368 | "plot(mesh, u)" 369 | ], 370 | "execution_count": 11, 371 | "outputs": [ 372 | { 373 | "output_type": "display_data", 374 | "data": { 375 | "image/png": "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\n", 376 | "text/plain": [ 377 | "
" 378 | ] 379 | }, 380 | "metadata": { 381 | "tags": [], 382 | "needs_background": "light" 383 | } 384 | } 385 | ] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "metadata": { 390 | "trusted": false, 391 | "id": "Kw6PDrRjSuGy" 392 | }, 393 | "source": [ 394 | "" 395 | ], 396 | "execution_count": null, 397 | "outputs": [] 398 | } 399 | ] 400 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FEMexamples 2 | Some simple finite element examples 3 | 4 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/chrisrichardson/FEMexamples) 5 | -------------------------------------------------------------------------------- /micromesh.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Chris Richardson (chris@bpi.cam.ac.uk) 2 | """ A super simple library for low volume two-dimensional unstructured meshes. 3 | Meshes are stored as python dicts containing 'geometry', 'topology', 4 | 'data', and can be read from a URL pointing to a text-based XDMF file """ 5 | 6 | import matplotlib.pyplot as plt 7 | import matplotlib.tri as tri 8 | import numpy 9 | 10 | def get(url): 11 | """ Read a mesh in XDMF XML format (not HDF5) from a URL""" 12 | try: 13 | import requests 14 | import xml.etree.ElementTree as ET 15 | except ImportError: 16 | raise("Missing required library") 17 | 18 | r = requests.get(url) 19 | if r.status_code != 200: 20 | raise IOError("Cannot read from URL") 21 | 22 | et = ET.fromstring(r.text) 23 | assert(et.tag == 'Xdmf') 24 | assert(et[0].tag == 'Domain') 25 | assert(et[0][0].tag == 'Grid') 26 | grid = et[0][0] 27 | 28 | # Get topology array 29 | topology = grid.find('Topology') 30 | assert(topology.attrib['TopologyType'] == 'Triangle') 31 | tdims = numpy.fromstring(topology[0].attrib['Dimensions'], sep=' ', dtype='int') 32 | nptopo = numpy.fromstring(topology[0].text, sep=' ', dtype='int').reshape(tdims) 33 | 34 | # Get geometry array 35 | geometry = grid.find('Geometry') 36 | assert(geometry.attrib['GeometryType'] == 'XY') 37 | gdims = numpy.fromstring(geometry[0].attrib['Dimensions'], sep=' ', dtype='int') 38 | npgeom = numpy.fromstring(geometry[0].text, sep=' ', dtype='float').reshape(gdims) 39 | 40 | # Find all attributes and put them in a list 41 | attrlist = grid.findall('Attribute') 42 | data_all = [] 43 | for attr in attrlist: 44 | adims = numpy.fromstring(attr[0].attrib['Dimensions'], sep=' ', dtype='int') 45 | npattr = attr.attrib 46 | npattr['value'] = numpy.fromstring(attr[0].text, sep=' ', dtype='int') 47 | data_all.append(npattr) 48 | 49 | mesh = {'geometry':npgeom, 'topology':nptopo, 'data':data_all} 50 | 51 | return mesh 52 | 53 | def rectangle_mesh(nx, ny): 54 | """Make a rectangular mesh of triangles, nx by ny.""" 55 | assert(isinstance(nx, int)) 56 | assert(isinstance(ny, int)) 57 | c = 0 58 | geometry = zeros((nx*ny, 2), dtype='float') 59 | for i in range(nx): 60 | for j in range(ny): 61 | geometry[c] = [float(i/(nx-1)), float(j/(ny-1))] 62 | c += 1 63 | 64 | ntri = (nx - 1)*(ny - 1)*2 65 | topology = zeros((ntri, 3), dtype='int') 66 | 67 | c = 0 68 | for i in range(nx - 1): 69 | for j in range(ny - 1): 70 | ij = j + i*ny 71 | topology[c] = [ij, ij+ny, ij+ny+1] 72 | topology[c + 1] = [ij+1, ij, ij+ny+1] 73 | c += 2 74 | 75 | mesh = {'geometry':geometry, 'topology':topology} 76 | return mesh 77 | 78 | def plot(mesh, *args, **kwargs): 79 | """ Plot a mesh with matplotlib, possibly with associated data, 80 | which may be associated with points or triangles """ 81 | 82 | # FIXME: check keys of mesh contain geometry and topology 83 | geom, topo = mesh['geometry'], mesh['topology'] 84 | x = geom[:,0] 85 | y = geom[:,1] 86 | 87 | plt.gca(aspect='equal') 88 | 89 | if args: 90 | data = args[0] 91 | if len(data)==len(geom): 92 | plt.tricontourf(x, y, topo, data, 40, **kwargs) 93 | elif len(data)==len(topo): 94 | tr = tri.Triangulation(x, y, topo) 95 | plt.tripcolor(tr, data, **kwargs) 96 | else: 97 | raise RuntimeError("Data is wrong length") 98 | 99 | plt.triplot(x, y, topo, color='k', alpha=0.5) 100 | 101 | xmax = x.max() 102 | xmin = x.min() 103 | ymax = y.max() 104 | ymin = y.min() 105 | dx = 0.1*(xmax - xmin) 106 | dy = 0.1*(ymax - ymin) 107 | plt.xlim(xmin-dx, xmax+dx) 108 | plt.ylim(ymin-dy, ymax+dy) 109 | 110 | return 111 | --------------------------------------------------------------------------------