{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Step-49"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a replica of [step-49](https://www.dealii.org/current/doxygen/deal.II/step_49.html) C++ tutorial program. However, here we will use the deal.II Python interface to achieve the same. \n",
    "\n",
    "Not all of the material is replicated since some parts of the original C++ tutorial are only relevant when using C++. Furthermore, in contrast to the C++ program, here we will take advantage of Jupyter notebook format and do coding and visualization in place.\n",
    "\n",
    "*This notebook is contributed by Alexander Grayver, 2020*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Python initialization"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Provided the deal.II was compiled with the python wrappers, we can import the module **PyDealII**. Note that it is only a shell and importing it will only allow you to call\n",
    "```python \n",
    "help(PyDealII)\n",
    "``` \n",
    "**PyDealII** is composed of two libraries: \n",
    " - **PyDealII.Debug** which uses the debug version of **deal.II**\n",
    " - **PyDealII.Release** which uses the release version of **deal.II**\n",
    " \n",
    "Let's us import the release version of the library and give it the shortname **dealii**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import PyDealII.Release as dealii"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition, we will import a few widely used python packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import matplotlib\n",
    "import numpy as np\n",
    "import re\n",
    "import math\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, here are a couple of functions, which we will use to plot a triangulation or print some information about it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.patches import Polygon\n",
    "from matplotlib.collections import PatchCollection\n",
    "\n",
    "def plot_triangulation(triangulation, color_scheme = None):\n",
    "    fig, ax = plt.subplots()\n",
    "    patches = []\n",
    "    colors = []\n",
    "    cell_id = 0 \n",
    "    for cell in triangulation.active_cells():\n",
    "        quad_vertices = np.zeros((4,2))\n",
    "        # The shift variable is used to reorder the vertices because\n",
    "        # deal.II and matplotlib require different ordering\n",
    "        shift = [0,1,3,2]\n",
    "        for i in range(4):\n",
    "            vertex = cell.get_vertex(i)\n",
    "            quad_vertices[shift[i]][0] = vertex.x\n",
    "            quad_vertices[shift[i]][1] = vertex.y\n",
    "        quad = Polygon(quad_vertices, closed=True)\n",
    "        patches.append(quad)\n",
    "        \n",
    "        if color_scheme:\n",
    "            colors.append(color_scheme(cell))\n",
    "\n",
    "        cell_id += 1\n",
    "    p = PatchCollection(patches, edgecolors='k', facecolors=None)  \n",
    "    p.set_array(np.array(colors))\n",
    "    ax.add_collection(p, autolim=True)\n",
    "    ax.autoscale_view()\n",
    "    ax.set_aspect(1)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You don't need to understand internals of these functions, they are used as mere helpers here."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tutorial is an extension to [step-1](https://www.dealii.org/current/doxygen/deal.II/step_1.html) and demonstrates several ways to obtain more involved meshes than the ones shown there.\n",
    "\n",
    "Generating complex geometries is a challenging task, especially in three space dimensions. We will discuss several ways to do this, but this list is not exhaustive. Additionally, there is not one approach that fits all problems.\n",
    "\n",
    "This example program shows some of ways to create and modify meshes for computations and outputs them as .vtu files in much the same way as we do in [step-1](https://www.dealii.org/current/doxygen/deal.II/step_1.html). No other computations or adaptive refinements are done; the idea is that you can use the techniques used here as building blocks in other, more involved simulators. Please note that the example program does not show all the ways to generate meshes that are discussed in this introduction."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### General concerns about meshes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When you use adaptive mesh refinement, you definitely want the initial mesh to be as coarse as possible. The reason is that you can make it as fine as you want using adaptive refinement as long as you have memory and CPU time available. However, this requires that you don't waste mesh cells in parts of the domain where they don't pay off. As a consequence, you don't want to start with a mesh that is too fine to start with, because that takes up a good part of your cell budget already, and because you can't coarsen away cells that are in the initial mesh.\n",
    "\n",
    "That said, your mesh needs to capture the given geometry adequately."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How to create meshes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are several ways to create an initial mesh. Meshes can be modified or combined in many ways as discussed later on."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Using Grid Generators"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The easiest way to generate meshes is to use the functions from the namespace [GridGenerator](https://www.dealii.org/current/doxygen/deal.II/namespaceGridGenerator.html). Python interface currently supports the following functions "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['generate_cheese',\n",
       " 'generate_enclosed_hyper_cube',\n",
       " 'generate_fixed_subdivided_parallelepiped',\n",
       " 'generate_general_cell',\n",
       " 'generate_half_hyper_ball',\n",
       " 'generate_hyper_ball',\n",
       " 'generate_hyper_cube',\n",
       " 'generate_hyper_cube_with_cylindrical_hole',\n",
       " 'generate_hyper_rectangle',\n",
       " 'generate_hyper_shell',\n",
       " 'generate_hyper_sphere',\n",
       " 'generate_parallelepiped',\n",
       " 'generate_parallelogram',\n",
       " 'generate_quarter_hyper_ball',\n",
       " 'generate_simplex',\n",
       " 'generate_subdivided_hyper_cube',\n",
       " 'generate_subdivided_hyper_rectangle',\n",
       " 'generate_subdivided_material_hyper_rectangle',\n",
       " 'generate_subdivided_steps_hyper_rectangle',\n",
       " 'generate_varying_subdivided_parallelepiped']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(filter(re.compile('generate_*').match, dir(dealii.Triangulation)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Importing from external programs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The original C++ class [GridIn](https://www.dealii.org/current/doxygen/deal.II/classGridIn.html) can read many different mesh formats from a file from disk. In PyDealII, GridIn functions are called via the *Triangulation.read* method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function read:\n",
      "\n",
      "read(...)\n",
      "    read( (Triangulation)self, (str)filename, (str)format) -> None :\n",
      "        Read a mesh from the file according to the given data format.          \n",
      "        The possible formats are:                                              \n",
      "          - msh                                                                \n",
      "          - vtk\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(dealii.Triangulation.read)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Meshes can be generated from different tools like [gmsh](http://gmsh.info/), [lagrit](https://lagrit.lanl.gov/) and [cubit](https://cubit.sandia.gov/). See the documentation of [GridIn](https://www.dealii.org/current/doxygen/deal.II/classGridIn.html) for more information. The problem is that deal.II needs meshes that only consist of quadrilaterals and hexahedra – tetrahedral meshes won't work (this means tools like tetgen can not be used directly).\n",
    "\n",
    "We will describe a possible workflow using Gmsh. Gmsh is the smallest and most quickly set up open source tool we are aware of. It can generate unstructured 2d quad meshes. In 3d, it can extrude 2d meshes to get hexahedral meshes; 3D meshing of unstructured geometry into hexahedra is possible, though there are some issues with the quality of these meshes that imply that these meshes only sometimes work in deal.II.\n",
    "\n",
    "In Gmsh, a mesh is fundamentally described in a text-based .geo file whose format can contain computations, loops, variables, etc. This format is quite flexible in allowing the description of complex geometries. The mesh is then generated from a surface representation, which is built from a list of line loops, which is built from a list of lines, which are in turn built from points. The .geo script can be written and edited by hand or it can be generated automatically by creating objects graphically inside Gmsh. In many cases it is best to combine both approaches. The file can be easily reloaded by pressing \"reload\" under the \"Geometry\" tab if you want to write it by hand and see the effects in the graphical user interface of gmsh.\n",
    "\n",
    "This tutorial contains an example .geo file that describes a box with two objects cut out in the interior. This is how *example.geo* looks like in Gmsh (displaying the boundary indicators as well as the mesh discussed further down below):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://www.dealii.org/images/steps/developer/step-49.gmsh_picture.png\"/>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<img src=\"https://www.dealii.org/images/steps/developer/step-49.gmsh_picture.png\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You might want to open the example.geo file in a text editor (it is located in the same directory as the *step-49.cc* source file) to see how it is structured. You can see how the boundary of the domain is composed of a number of lines and how later on we combine several lines into \"physical lines\" (or \"physical surfaces\") that list the logical lines' numbers. \"Physical\" object are the ones that carry information about the boundary indicator (see [this glossary entry](https://www.dealii.org/developer/doxygen/deal.II/DEALGlossary.html#GlossBoundaryIndicator))."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note**\n",
    ">It is important that this file contain \"physical lines\" and \"physical surfaces\". These give the boundary indicators and material ids for use in deal.II. Without these physical entities, nothing will be imported into deal.II."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "deal.II's GridIn class can read the .msh format written by Gmsh and that contains a mesh created for the geometry described by the .geo file. You generate the .msh from the .geo by running the commands\n",
    "\n",
    "```javascript\n",
    "gmsh -2 example.geo\n",
    "```\n",
    "\n",
    "on the command line, or by clicking \"Mesh\" and then \"2D\" inside Gmsh after loading the file. Now, we can read in the mesh from the .msh file and visualize using the python bindings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 80972 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "triangulation = dealii.Triangulation(dim = '2D')\n",
    "triangulation.read(filename = 'example.msh', format = 'msh')\n",
    "plot_triangulation(triangulation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note**\n",
    "> gmsh has a number of other interfaces by which one can describe geometries to it. In particular, it has the ability to interface with scripting languages like Python and Julia, but it can also be scripted from C++. These interfaces are useful if one doesn't just want to generate a mesh for a single geometry (in which case the graphical interface or, in simple cases, a hand-written .geo file is probably the simplest approach), but instead wants to do parametric studies over the geometry for which it is necessary to generate many meshes for geometries that differ in certain parameters. Another case where this is useful is if there is already a CAD geometry for which one only needs a mesh; indeed, this can be done from within deal.II using the [Gmsh::create_triangulation_from_boundary_curve()](https://www.dealii.org/current/doxygen/deal.II/namespaceGmsh.html#ab65eb217368d006b66bcfcc6fa0f2894) function."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Modifying a Mesh\n",
    "\n",
    "After acquiring one (or several) meshes in the ways described above, there are many ways to manipulate them before using them in a finite element computation.\n",
    "\n",
    "#### Transformations\n",
    "\n",
    "The function *Triangulation.transform* allows you to transform the vertices of a given mesh using a smooth function. An example of its use is also given in the results section of [step-38](https://www.dealii.org/current/doxygen/deal.II/step_38.html) but let us show a simpler example here. Using the code below, we perturb the *y* coordinate of a mesh with a sine curve.\n",
    "\n",
    "First, let us create a triangulation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "triangulation = dealii.Triangulation('2D')\n",
    "triangulation.generate_subdivided_hyper_rectangle(repetitions = [14, 2],\\\n",
    "                                                  p1 = dealii.Point([0, 0]),\\\n",
    "                                                  p2 = dealii.Point([10, 1]))\n",
    "plot_triangulation(triangulation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we define a lambda function that takes a point (a list of size *dim* with coordinates), transforms it according to the formula $(x,y) \\rightarrow (x, y + \\sin(\\pi x / 5))$ and returns the result:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "sine_y_transform = lambda p: [p[0], p[1] + math.sin(p[0] / 5.0 * math.pi)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, apply this transformation and plot the result:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 16692 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "triangulation.transform(transformation = sine_y_transform)\n",
    "plot_triangulation(triangulation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarly, we can transform a regularly refined unit square to a wall-adapted mesh in y direction using the formula $(x,y) \\rightarrow (x, \\tanh(2y) / \\tanh(2))$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 5188 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 5208 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "triangulation = dealii.Triangulation('2D')\n",
    "triangulation.generate_subdivided_hyper_rectangle(repetitions = [20, 20],\\\n",
    "                                                  p1 = dealii.Point([0, 0]),\\\n",
    "                                                  p2 = dealii.Point([1, 1]))\n",
    "plot_triangulation(triangulation)\n",
    "\n",
    "tanh_y_transform = lambda p: [p[0], math.tanh(2. * p[1]) / math.tanh(2.)]\n",
    "triangulation.transform(transformation = tanh_y_transform)\n",
    "\n",
    "plot_triangulation(triangulation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, the function *distort_random* allows you to move vertices in the mesh (optionally ignoring boundary nodes) by a random amount. This is demonstrated in below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 5036 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 62992 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "triangulation = dealii.Triangulation('2D')\n",
    "triangulation.generate_subdivided_hyper_rectangle(repetitions = [16, 16],\\\n",
    "                                                  p1 = dealii.Point([0, 0]),\\\n",
    "                                                  p2 = dealii.Point([1, 1]))\n",
    "plot_triangulation(triangulation)\n",
    "\n",
    "triangulation.distort_random(factor = 0.3, keep_boundary = True)\n",
    "\n",
    "plot_triangulation(triangulation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This function is primarily intended to negate some of the superconvergence effects one gets when studying convergence on regular meshes, as well as to suppress some optimizations in deal.II that can exploit the fact that cells are similar in shape. (Superconvergence refers to the fact that if a mesh has certain symmetries – for example, if the edges running into a vertex are symmetric to this vertex, and if this is so for all vertices of a cell – that the solution is then often convergent with a higher order than one would have expected from the usual error analysis. In the end, this is a result of the fact that if one were to make a Taylor expansion of the error, the symmetry leads to the fact that the expected next term of the expansion happens to be zero, and the error order is determined by the second next term. A distorted mesh does not have these symmetries and consequently the error reflects what one will see when solving the equation on any kind of mesh, rather than showing something that is only reflective of a particular situation.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Merging Meshes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The method *Triangulation.merge_triangulations* allows you to merge two given *Triangulation* objects into a single one. For this to work, the vertices of the shared edge or face have to match exactly. Lining up the two meshes can be achieved using *Triangulation.shift* and *Triangulation.scale*. In the code section below, we merge a square with a round hole. The method *Triangulation.subdivided_hyper_rectangle* allows you to specify the number of repetitions and the positions of the corners, so there is no need to shift the triangulation manually here. You should inspect the mesh graphically to make sure that cells line up correctly and no unpaired nodes exist in the merged Triangulation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create and plot the input meshes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 9504 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 6628 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "tria_1 = dealii.Triangulation('2D')\n",
    "tria_1.generate_hyper_cube_with_cylindrical_hole(inner_radius = .25, outer_radius = 1.)\n",
    "\n",
    "plot_triangulation(tria_1)\n",
    "\n",
    "tria_2 = dealii.Triangulation('2D')\n",
    "tria_2.generate_subdivided_hyper_rectangle(repetitions = [3, 2],\\\n",
    "                                           p1 = dealii.Point([1., -1.]),\\\n",
    "                                           p2 = dealii.Point([4., 1.]))\n",
    "plot_triangulation(tria_2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now merge triangulations and plot the resulting mesh"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 6616 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "tria_merged = dealii.Triangulation('2D')\n",
    "tria_merged.merge_triangulations([tria_1, tria_2])\n",
    "plot_triangulation(tria_merged)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Moving Vertices\n",
    "\n",
    "Here we show the ability to pick individual vertices and move them around in an existing mesh. Note that this has the potential to produce degenerate or inverted cells and you shouldn't expect anything useful to come of using such meshes. Here, we create a box with a cylindrical hole that is not exactly centered by moving the top vertices upwards:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 38028 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "triangulation = dealii.Triangulation('2D')\n",
    "triangulation.generate_hyper_cube_with_cylindrical_hole(inner_radius = .25, outer_radius = 1.)\n",
    "\n",
    "for cell in triangulation.active_cells():\n",
    "    for i in range(4):\n",
    "        v = cell.get_vertex(i)\n",
    "        \n",
    "        if(abs(v.y - 1.0) < 1e-5):\n",
    "            v.y += 0.5\n",
    "            \n",
    "        cell.set_vertex(i, v)\n",
    "        \n",
    "triangulation.refine_global(2)\n",
    "\n",
    "plot_triangulation(triangulation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Extruding Meshes\n",
    "\n",
    "If you need a 3d mesh that can be created by extruding a given 2d mesh (that can be created in any of the ways given above), you can use the function *Triangulation.extrude_triangulation*. See code below for an example. Note that for this particular case, the given result could also be achieved using the 3d version of *Triangulation.generate_hyper_cube_with_cylindrical_hole*. The main usage is a 2d mesh, generated for example with Gmsh, that is read in from a .msh file as described above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "tria = dealii.Triangulation('2D')\n",
    "tria.generate_hyper_cube_with_cylindrical_hole(inner_radius = .25, outer_radius = 1.)\n",
    "\n",
    "tria_extruded = dealii.Triangulation('3D')\n",
    "tria.extrude_triangulation(n_slices = 3, depth = 2.0, tria_out = tria_extruded)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since we cannot easily visualize 3d mesh, let's save it in the EPS format and then plot the image (this requires *pillow* package to be installed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f8435432a58>"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 18900 characters]\n",
      "text/plain": [
       "<Figure size 864x864 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "tria_extruded.write('extruded_mesh.eps', format = 'eps')\n",
    "img = matplotlib.image.imread('extruded_mesh.eps')\n",
    "\n",
    "fig = plt.figure(figsize = (12,12))\n",
    "ax = fig.add_subplot(111)\n",
    "ax.imshow(img, interpolation='none')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### After you have a coarse mesh\n",
    "\n",
    "Creating a coarse mesh using the methods discussed above is only the first step. When you have it, it will typically serve as the basis for further mesh refinement. This is not difficult — in fact, there is nothing else to do — if your geometry consists of only straight faces. However, this is often not the case if you have a more complex geometry and more steps than just creating the mesh are necessary. We will go over some of these steps in the section below."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Next steps: Curved Cells\n",
    "\n",
    "As mentioned in the introduction, creating a coarse mesh using the methods discussed here is only the first step. In order to refine a mesh, the *Triangulation* needs to know where to put new vertices on the mid-points of edges, faces, and cells. By default, these new points will be placed at the arithmetic mean of the surrounding points, but this isn't what you want if you need curved boundaries that aren't already adequately resolved by the coarse mesh. \n",
    "\n",
    "To illustrate this process in more detail, let us consider an example created by Yuhan Zhou as part of a 2013 semester project at Texas A&M University. The goal was to generate (and use) a geometry that describes a microstructured electric device. In a CAD program, the geometry looks like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://www.dealii.org/images/steps/developer/step-49.yuhan.1.png\">\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<img src=\"https://www.dealii.org/images/steps/developer/step-49.yuhan.1.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the following, we will walk you through the entire process of creating a mesh for this geometry, including a number of common pitfalls by showing the things that can go wrong.\n",
    "\n",
    "The first step in getting there was to create a coarse mesh, which was done by creating a 2d coarse mesh for each of cross sections, extruding them into the third direction, and gluing them together. The following code does this, using the techniques previously described:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_3d_grid():\n",
    "    # Generate first cross section\n",
    "    vertices_1 = [[-1.5, 0.], [-0.5, 0.], [0.5, 0.], [1.5, 0.],\\\n",
    "              [-1.5, 1.5], [-0.5, 1.5], [0.5, 1.5], [1.5, 1.5],\\\n",
    "              [-1.5, 3.], [-0.5, 3.], [0.5, 3.], [1.5, 3.],\\\n",
    "              [-0.5, 3 + 0.5 * math.sqrt(3)],\\\n",
    "              [0.5, 3 + 0.5 * math.sqrt(3)],\\\n",
    "              [-0.75, 3 + 0.75 * math.sqrt(3)],\\\n",
    "              [0.75, 3 + 0.75 * math.sqrt(3)]]\n",
    "\n",
    "    cell_vertices_1 = [[0, 1, 4, 5],\n",
    "                   [1, 2, 5, 6],\n",
    "                   [3, 7, 2, 6],\n",
    "                   [4, 5, 8, 9],\n",
    "                   [5, 6, 9, 10],\n",
    "                   [7, 11, 6, 10],\n",
    "                   [8, 9, 14, 12],\n",
    "                   [9, 10, 12, 13],\n",
    "                   [11, 15, 10, 13],\n",
    "                   [14, 12, 15, 13]]\n",
    "\n",
    "    # Given a list of points and how vertices connect to cells, create a mesh.\n",
    "    tria_2d_1 = dealii.Triangulation('2D')\n",
    "    tria_2d_1.create_triangulation(vertices_1, cell_vertices_1)\n",
    "\n",
    "    # Then extrude it into a 3d piece\n",
    "    tria_3d_1 = dealii.Triangulation('3D')\n",
    "    tria_2d_1.extrude_triangulation(n_slices = 5, depth = 2.5, tria_out = tria_3d_1)\n",
    "\n",
    "    # Now do the same with the second volume\n",
    "    vertices_2 = [[-2.5, 0.],\n",
    "              [-1.5, 0.],\n",
    "              [-0.5, 0.],\n",
    "              [0.5, 0.],\n",
    "              [1.5, 0.],\n",
    "              [2.5, 0.],\n",
    "              [-2.5, 1.5],\n",
    "              [-1.5, 1.5],\n",
    "              [-0.5, 1.5],\n",
    "              [0.5, 1.5],\n",
    "              [1.5, 1.5],\n",
    "              [2.5, 1.5],\n",
    "              [-2.5, 3.],\n",
    "              [-1.5, 3.],\n",
    "              [-0.5, 3.],\n",
    "              [0.5, 3.],\n",
    "              [1.5, 3.],\n",
    "              [2.5, 3.],\n",
    "              [-0.5, 3. + 0.5 * math.sqrt(3)],\n",
    "              [0.5, 3. + 0.5 * math.sqrt(3)],\n",
    "              [-0.75, 3. + 0.75 * math.sqrt(3)],\n",
    "              [0.75, 3. + 0.75 * math.sqrt(3)],\n",
    "              [-1.25, 3. + 1.25 * math.sqrt(3)],\n",
    "              [1.25, 3. + 1.25 * math.sqrt(3)]]\n",
    "\n",
    "    cell_vertices_2 = [[0, 1, 6, 7],\n",
    "                   [1, 2, 7, 8],\n",
    "                   [2, 3, 8, 9],\n",
    "                   [4, 10, 3, 9],\n",
    "                   [5, 11, 4, 10],\n",
    "                   [6, 7, 12, 13],\n",
    "                   [7, 8, 13, 14],\n",
    "                   [8, 9, 14, 15],\n",
    "                   [10, 16, 9, 15],\n",
    "                   [11, 17, 10, 16],\n",
    "                   [12, 13, 22, 20],\n",
    "                   [13, 14, 20, 18],\n",
    "                   [14, 15, 18, 19],\n",
    "                   [16, 21, 15, 19],\n",
    "                   [17, 23, 16, 21],\n",
    "                   [20, 18, 21, 19],\n",
    "                   [22, 20, 23, 21]]\n",
    "\n",
    "    tria_2d_2 = dealii.Triangulation('2D')\n",
    "    tria_2d_2.create_triangulation(vertices_2, cell_vertices_2)\n",
    "\n",
    "    tria_3d_2 = dealii.Triangulation('3D')\n",
    "    tria_2d_2.extrude_triangulation(n_slices = 5, depth = 2.5, tria_out = tria_3d_2)\n",
    "\n",
    "    # Also shift this triangulation in the z-direction so that it matches the\n",
    "    # end face of the first part\n",
    "    tria_3d_2.shift([0, 0, 2.5])\n",
    "\n",
    "    # Now first merge these two pieces, then shift the first piece in\n",
    "    # z-direction beyond the second, and merge the shifted piece with the two\n",
    "    # previously merged one into the final one:\n",
    "    tria_3d_tmp = dealii.Triangulation('3D')\n",
    "    tria_3d_tmp.merge_triangulations(tria_3d_1, tria_3d_2)\n",
    "\n",
    "    tria_3d_1.shift([0, 0, 5.])\n",
    "\n",
    "    triangulation = dealii.Triangulation('3D')\n",
    "    triangulation.merge_triangulations(tria_3d_tmp, tria_3d_1)\n",
    "\n",
    "    return triangulation\n",
    "\n",
    "triangulation = create_3d_grid()\n",
    "# triangulation.write('coarse.vtk', 'vtk')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This creates the following mesh:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://www.dealii.org/images/steps/developer/step-49.yuhan.8.png\">\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<img src=\"https://www.dealii.org/images/steps/developer/step-49.yuhan.8.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This mesh has the right general shape, but the top cells are now polygonal: their edges are no longer along circles and we do not have a very accurate representation of the original geometry. The next step is to teach the top part of the domain that it should be curved. Put another way, all calculations done on the top boundary cells should be done in cylindrical coordinates rather than Cartesian coordinates. We can do this by creating a *CylindricalManifold* object and associating it with the cells above $y=3$. This way, when we refine the cells on top, we will place new points along concentric circles instead of straight lines.\n",
    "\n",
    "In this case, with the CylindricalManifold constructed below, the geodesics are arcs along circles orthogonal to the $z$-axis centered along the line $(0,3,z)$.\n",
    "\n",
    "Since all three top parts of the domain use the same geodesics, we will mark all cells with centers above the $y=3$ line as being cylindrical in nature:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "triangulation = create_3d_grid()\n",
    "\n",
    "cylinder = dealii.Manifold(dim = 3, spacedim = 3)\n",
    "cylinder.create_cylindrical(direction = [0., 0., 1.], axial_point = [0, 3., 0.])\n",
    "\n",
    "cylinder_id = 8\n",
    "\n",
    "triangulation.set_manifold(number = cylinder_id, manifold = cylinder)\n",
    "\n",
    "for cell in triangulation.active_cells():\n",
    "    if cell.center().y >= 3.:\n",
    "        cell.set_all_manifold_ids(cylinder_id)\n",
    "        \n",
    "triangulation.refine_global(1)\n",
    "\n",
    "triangulation.write('coarse.vtk', 'vtk')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With this code, we get a mesh that looks like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://www.dealii.org/images/steps/developer/step-49.yuhan.9.png\">\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<img src=\"https://www.dealii.org/images/steps/developer/step-49.yuhan.9.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This change fixes the boundary but creates a new problem: the cells adjacent to the cylinder's axis are badly distorted. We should use Cartesian coordinates for calculations on these central cells to avoid this issue. The cells along the center line all have a face that touches the line $(0,3,z)$ so, to implement this, we go back and overwrite the manifold_ids on these cells to be zero (which is the default):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "triangulation = create_3d_grid()\n",
    "\n",
    "triangulation.set_manifold(number = cylinder_id, manifold = cylinder)\n",
    "\n",
    "for cell in triangulation.active_cells():\n",
    "    if cell.center().y >= 3.:\n",
    "        cell.set_all_manifold_ids(cylinder_id)\n",
    "        \n",
    "flat_manifold_id = -1\n",
    "for cell in triangulation.active_cells():\n",
    "    for face in cell.faces():\n",
    "        face_center = face.center()\n",
    "        if abs(face_center.x) < 1.0e-5 and\\\n",
    "           abs(face_center.y - 3.0) < 1.0e-5:\n",
    "            cell.set_all_manifold_ids(flat_manifold_id)\n",
    "        \n",
    "triangulation.refine_global(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can finally save the mesh now:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "triangulation.write('final.vtk', 'vtk')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This gives us a good mesh, where cells at the center of each circle are still Cartesian and cells around the boundary lie along a circle. We can really see the nice detail of the boundary fitted mesh if we refine two more times:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://www.dealii.org/images/steps/developer/step-49.yuhan.11.png\">\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<img src=\"https://www.dealii.org/images/steps/developer/step-49.yuhan.11.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
