{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2.3 *H(curl)* and *H(div)* function spaces\n",
    "\n",
    "Scalar and vectorial finite elements in NGSolve:\n",
    "\n",
    "*Standard* continuous $H^1$ elements: \n",
    "<!--- <center>  needed for html rendering ??? --->\n",
    "![title](resources/nodalelement.png)\n",
    "<!--- </center> --->\n",
    "\n",
    "Nedelec's tangentially-continuous $H(curl)$-conforming edge elements:\n",
    "\n",
    "![](resources/edgeelement.png)\n",
    "\n",
    "Raviart-Thomas normally-continuous $H(div)$-conforming face elements:\n",
    "\n",
    "![](resources/faceelement.png)\n",
    "\n",
    "Discontinuous $L_2$ elements:\n",
    "\n",
    "![](resources/l2element.png)\n",
    "\n",
    "These vector-valued spaces allow to represent physical quantities which are either normally or tangentially continuous.\n",
    "\n",
    "The finite element spaces are related by the de Rham complex:\n",
    "\n",
    "$$\n",
    "\\DeclareMathOperator{\\Grad}{grad}\n",
    "\\DeclareMathOperator{\\Curl}{curl}\n",
    "\\DeclareMathOperator{\\Div}{div}\n",
    "\\begin{array}{ccccccc}\n",
    "H^1      &  \\stackrel{\\Grad}{\\longrightarrow}          &\n",
    "H(\\Curl) &  \\stackrel{\\Curl}{\\longrightarrow}   &\n",
    "H(\\Div)  &  \\stackrel{\\Div}{\\longrightarrow}    & \n",
    "L^2                                                                                    \\\\[8pt]\n",
    "\\bigcup  &                  &\n",
    "\\bigcup  &                  &\n",
    "\\bigcup  &                  &\n",
    "\\bigcup                              \\\\[8pt]\n",
    " W_{h}                   &      \n",
    "\\stackrel{\\Grad}{\\longrightarrow}          &\n",
    " V_{h }       &     \n",
    " \\stackrel{\\Curl}{\\longrightarrow}   &\n",
    " Q_{h}          &      \n",
    "\\stackrel{\\Div}{\\longrightarrow}    & \n",
    "S_{h}  \\:                                                               \n",
    " \\\\[3ex]\n",
    "\\end{array}\n",
    "$$\n",
    "\n",
    "NGSolve supports these elements of arbitrary order, on all common element shapes (trigs, quads, tets, prisms, pyramids, hexes). Elements may be curved."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ngsolve import *\n",
    "from ngsolve.webgui import Draw\n",
    "\n",
    "mesh = Mesh(unit_square.GenerateMesh(maxh=0.3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate a higher order $H^1$-space. We first explore its different types of basis functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "order=3\n",
    "fes = H1(mesh, order=order)\n",
    "gfu = GridFunction(fes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first basis functions are hat-functions, one per vertex. By setting the solution vector to a unit-vector, we may look at the individual basis functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gfu.vec[:] = 0\n",
    "# vertex nr 17:\n",
    "gfu.vec[17] = 1\n",
    "Draw(gfu, min=0, max=1, deformation=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next are edge-bubbles, where we have $(p-1)$ basis functions per edge. A `NodeId` object refers to a particular vertex, edge, face or cell node in the mesh. We can ask for the degrees of freedom on a node:  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# basis functions on edge nr:\n",
    "edge_dofs = fes.GetDofNrs(NodeId(EDGE,10))\n",
    "print(\"edge_dofs =\", edge_dofs)\n",
    "gfu.vec[:] = 0\n",
    "gfu.vec[edge_dofs[0]] = -1\n",
    "Draw(gfu, order=3, min=-0.05, max=0.05, deformation=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we have $(p-1)(p-2)/2$ inner basis functions on every triangle:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trig_dofs = fes.GetDofNrs(NodeId(FACE,0))\n",
    "print(\"trig_dofs = \", trig_dofs)\n",
    "gfu.vec[:] = 0\n",
    "gfu.vec[trig_dofs[0]] = 10\n",
    "Draw(gfu, order=3, min=0, max=0.3, deformation=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `FESpace` also maintains information about local dofs, interface dofs and wire-basket dofs for the BDDC preconditioner:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(fes.ndof):\n",
    "    print (i,\":\", fes.CouplingType(i))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## $H(curl)$ finite element space"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In NGSolve we use hierarchical high order finite element basis functions with node-wise exact sequences. The lowest order space $W_{l.o}$ is the edge-element space:\n",
    "\n",
    "$$ \n",
    "\\begin{array}{rcll}\n",
    "W_{hp} & = & W_{p=1} + \\sum_E W_E + \\sum_F W_F + \\sum_C W_C & \\subset H^1 \\\\[0.5em]\n",
    "V_{hp} & = & W_{l.o} + \\sum_E V_E + \\sum_F V_F + \\sum_C V_C & \\subset H(curl) \n",
    "\\end{array}\n",
    "$$\n",
    "\n",
    "where the edge, face and cell blocks are compatible in the sense that\n",
    "\n",
    "$$\n",
    "\\nabla W_E = V_E, \\quad \\nabla W_F \\subset V_F, \\quad \\nabla W_C \\subset V_C\n",
    "$$\n",
    "\n",
    "We obtain this by using gradients of $H^1$ basis functions as $H(curl)$ basis functions, and some more (see thesis Sabine Zaglmayr):\n",
    "\n",
    "$$ \n",
    "\\begin{array}{rcl}\n",
    "V_E & = & \\text{span} \\{ \\nabla \\varphi_{E,i}^{H^1} \\} \\\\\n",
    "V_F & = & \\text{span} \\{ \\nabla \\varphi_{F,i}^{H^1} \\cup \\widetilde \\varphi_{F,i}^{H(curl)} \\} \\\\\n",
    "V_C & = & \\text{span} \\{ \\nabla \\varphi_{C,i}^{H^1} \\cup \\widetilde \\varphi_{C,i}^{H(curl)} \\} \n",
    "\\end{array}\n",
    "$$\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fes = HCurl(mesh, order=2)\n",
    "uc = GridFunction(fes, name=\"uc\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "edge_dofs = fes.GetDofNrs(NodeId(EDGE,10))\n",
    "print (\"edgedofs: \", edge_dofs)\n",
    "uc.vec[:] = 0\n",
    "uc.vec[edge_dofs[0]] = 1\n",
    "Draw (uc, min=0, max=3, vectors = { \"grid_size\":30})\n",
    "Draw (curl(uc), mesh, \"curl\", min=-25, max=25);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "face_dofs = fes.GetDofNrs(NodeId(FACE,10))\n",
    "print (\"facedofs: \", face_dofs)\n",
    "uc.vec[:] = 0\n",
    "uc.vec[face_dofs[0]] = 1\n",
    "Draw (uc, min=0, max=1, vectors = { \"grid_size\":30})\n",
    "Draw (curl(uc), mesh, \"curl\", min=-1, max=1, order=3); # it's a gradient"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## $H(div)$ finite element space"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NGSolve provides Raviart-Thomas (RT) as well as Brezzi-Douglas-Marini (BDM) finite element spaces for H(div). We obtain the RT-version by setting `RT=True`, otherwise we get BDM."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fes = HDiv(mesh, order=2, RT=True)\n",
    "ud = GridFunction(fes)\n",
    "func = x*y*(x,y)\n",
    "ud.Set (func)\n",
    "Draw (ud, vectors = { \"grid_size\":30})\n",
    "print (\"interpolation error:\", Integrate ((func-ud)**2, mesh))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function spaces know their canonical derivatives. These operations are efficiently implemented by transformation from the reference element."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gfu.derivname, ud.derivname, uc.derivname"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But there are additional options, like forming the element-wise gradient of H(div) finite element functions. We can query the available operators via"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print (\"H(div) operators: \", ud.Operators())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and access them via the Operator() method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "raises-exception"
    ]
   },
   "outputs": [],
   "source": [
    "Draw (grad(ud)[0,1], mesh, \"gradud\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  },
  "nbsphinx": {
   "allow_errors": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
