├── .gitignore ├── Copper2019-AMG-Tutorial.pdf ├── Copper2019-MG-intro-Tutorial.pdf ├── Copper2019-ParMG-Tutorial.pdf ├── README.md ├── TutorialGoogleDocLink.ipynb ├── algebraic-multigrid ├── 0-multigrid-in-2d.ipynb ├── 1-AMG-coarse-mesh-example.ipynb ├── 2-AMG-complexity.ipynb ├── 3-SA-AMG-in-1D.ipynb ├── 4-components-of-smoothed-aggregation.ipynb ├── 5-AMG-advanced-options-anisotropy.ipynb ├── 6-AMG-advanced-options-nonsymmetric-flow.ipynb ├── 7-AMG-advanced-options-systems-elasticity.ipynb ├── diffusion.py ├── helper.py ├── square.mat └── stencil.py ├── clearoutput.sh ├── list-notebooks.sh └── multigrid ├── 1-relaxation-and-modes.ipynb ├── 2-MGtwolevel.ipynb ├── 3-Multigrid-V-cycle.ipynb ├── Coarse-Modes.ipynb ├── Error-vs-Solution.ipynb ├── Interpolation-1D.ipynb ├── Multigrid-Two-Grid-Cycle.ipynb ├── Multigrid-in-2D.ipynb ├── Multigrid-with-Variable-Spacing.ipynb ├── Nested-Iteration.ipynb ├── Relaxation-and-omega.ipynb ├── Relaxation-on-smooth-and-non-smooth-error.ipynb ├── Relaxation.ipynb ├── diffusion.py ├── helper.py ├── onedprojection.py └── stencil.py /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | .*.swp 3 | __pycache__ 4 | -------------------------------------------------------------------------------- /Copper2019-AMG-Tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copper-multigrid-conference/2019-tutorials/36a2cd6c25db8db11aa00f95a7e774f10d185e6b/Copper2019-AMG-Tutorial.pdf -------------------------------------------------------------------------------- /Copper2019-MG-intro-Tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copper-multigrid-conference/2019-tutorials/36a2cd6c25db8db11aa00f95a7e774f10d185e6b/Copper2019-MG-intro-Tutorial.pdf -------------------------------------------------------------------------------- /Copper2019-ParMG-Tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copper-multigrid-conference/2019-tutorials/36a2cd6c25db8db11aa00f95a7e774f10d185e6b/Copper2019-ParMG-Tutorial.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Slides 2 | 3 | - 4 | - [Copper2019-AMG-Tutorial.pdf](Copper2019-AMG-Tutorial.pdf) 5 | - [Copper2019-ParMG-Tutorial.pdf](Copper2019-ParMG-Tutorial.pdf) 6 | 7 | ## Notebooks 8 | 9 | The notebooks can be viewed at [https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/](https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/) 10 | 11 | ### Multigrid: 12 | 13 | - [1-relaxation-and-modes.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/1-relaxation-and-modes.ipynb) 14 | - [2-MGtwolevel.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/2-MGtwolevel.ipynb) 15 | - [3-Multigrid-V-cycle.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/3-Multigrid-V-cycle.ipynb) 16 | - [Coarse-Modes.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Coarse-Modes.ipynb) 17 | - [Error-vs-Solution.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Error-vs-Solution.ipynb) 18 | - [Interpolation-1D.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Interpolation-1D.ipynb) 19 | - [Multigrid-Two-Grid-Cycle.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Multigrid-Two-Grid-Cycle.ipynb) 20 | - [Multigrid-in-2D.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Multigrid-in-2D.ipynb) 21 | - [Multigrid-with-Variable-Spacing.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Multigrid-with-Variable-Spacing.ipynb) 22 | - [Nested-Iteration.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Nested-Iteration.ipynb) 23 | - [Relaxation-and-omega.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Relaxation-and-omega.ipynb) 24 | - [Relaxation-on-smooth-and-non-smooth-error.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Relaxation-on-smooth-and-non-smooth-error.ipynb) 25 | - [Relaxation.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/multigrid/Relaxation.ipynb) 26 | 27 | ### Algebraic Multigrid: 28 | 29 | - [0-multigrid-in-2d.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/algebraic-multigrid/0-multigrid-in-2d.ipynb) 30 | - [1-AMG-coarse-mesh-example.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/algebraic-multigrid/1-AMG-coarse-mesh-example.ipynb) 31 | - [2-AMG-complexity.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/algebraic-multigrid/2-AMG-complexity.ipynb) 32 | - [3-SA-AMG-in-1D.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/algebraic-multigrid/3-SA-AMG-in-1D.ipynb) 33 | - [4-components-of-smoothed-aggregation.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/algebraic-multigrid/4-components-of-smoothed-aggregation.ipynb) 34 | - [5-AMG-advanced-options-anisotropy.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/algebraic-multigrid/5-AMG-advanced-options-anisotropy.ipynb) 35 | - [6-AMG-advanced-options-nonsymmetric-flow.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/algebraic-multigrid/6-AMG-advanced-options-nonsymmetric-flow.ipynb) 36 | - [7-AMG-advanced-options-systems-elasticity.ipynb]( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/algebraic-multigrid/7-AMG-advanced-options-systems-elasticity.ipynb) 37 | -------------------------------------------------------------------------------- /TutorialGoogleDocLink.ipynb: -------------------------------------------------------------------------------- 1 | {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"Copy of Untitled0.ipynb","version":"0.3.2","provenance":[{"file_id":"1yggrf3ro1qUeXRm3wbKWEn8nxmUEKFWy","timestamp":1553433862082}]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"n86NyzMy00n1","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]},{"metadata":{"id":"YpmUBwT603C1","colab_type":"text"},"cell_type":"markdown","source":["Welcome to the Multigrid Tutorials at the 19th Copper Mountain Conference on Multigrid Methods.\n","\n","We have a Google Doc for to help with communication,\n","\n","[Click here for the Google Doc](https://docs.google.com/document/d/1p1LRHYYIhQBVzkvCOMjYNgZRHE4jDO8GiidykDW9GwI/edit?usp=sharing)\n","\n","Please add your name and contact information. Questions and suggestions are also appreciated.\n","\n","Run the simple print command below to ensure your collaborative notebook setup is working."]},{"metadata":{"id":"fwnzQZ823LaR","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":34},"outputId":"4b300caf-f6f2-4f7f-d526-ca126233e20e","executionInfo":{"status":"ok","timestamp":1553433701721,"user_tz":360,"elapsed":435,"user":{"displayName":"David Moulton","photoUrl":"https://lh6.googleusercontent.com/-JMEvYuOKDZg/AAAAAAAAAAI/AAAAAAAAAF4/ckY8ff1n8Fs/s64/photo.jpg","userId":"03847248841726013675"}}},"cell_type":"code","source":["print(\"I love to ski at Copper Mountain\")"],"execution_count":1,"outputs":[{"output_type":"stream","text":["I love to ski at Copper Mountain\n"],"name":"stdout"}]}]} -------------------------------------------------------------------------------- /algebraic-multigrid/0-multigrid-in-2d.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Fetch the local files" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "!wget -Nq https://raw.githubusercontent.com/copper-multigrid-conference/2019-tutorials/master/algebraic-multigrid/stencil.py\n", 17 | "!wget -Nq https://raw.githubusercontent.com/copper-multigrid-conference/2019-tutorials/master/algebraic-multigrid/diffusion.py" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "## Import numpy, scipy, matplotlib" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "import scipy as sp\n", 34 | "import numpy as np\n", 35 | "import scipy.sparse as sparse\n", 36 | "import scipy.sparse.linalg as sla\n", 37 | "import matplotlib.pyplot as plt\n", 38 | "%matplotlib inline\n", 39 | "\n", 40 | "import stencil\n", 41 | "import diffusion\n", 42 | "\n", 43 | "import warnings\n", 44 | "warnings.simplefilter(action='ignore', category=FutureWarning)" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "## Define the h-norm\n", 52 | "\n", 53 | "The analog of\n", 54 | "$$\n", 55 | "\\int_0^1 r(x) dx\n", 56 | "$$\n", 57 | "for discrete vector $r$ is\n", 58 | "$$\n", 59 | "\\|r\\|_h = h \\|r\\|_{\\ell_2}\n", 60 | "$$" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "def hnorm(r):\n", 70 | " \"\"\"define ||r||_h = h ||r||_2\"\"\"\n", 71 | " n = len(r)\n", 72 | " h = 1.0 / (n+1)\n", 73 | " hrnorm = h * np.linalg.norm(r)\n", 74 | " return hrnorm" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "## Define Jacobi relaxation, interpolation" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "def relaxJ(A, u, f, nu):\n", 91 | " \"\"\"relax nu times on A u = f\n", 92 | " \"\"\"\n", 93 | " n = A.shape[0]\n", 94 | " unew = u.copy()\n", 95 | " Dinv = sparse.spdiags(1./A.diagonal(), [0], n, n)\n", 96 | " omega = 2.0 / 3.0\n", 97 | " \n", 98 | " for i in range(nu):\n", 99 | " unew += omega * Dinv * (f - A * unew)\n", 100 | "\n", 101 | " return unew\n", 102 | "\n", 103 | "\n", 104 | "def interpolation1d(nc, nf):\n", 105 | " \"\"\"linear interpolation\n", 106 | " \"\"\"\n", 107 | " d = np.repeat([[1, 2, 1]], nc, axis=0).T\n", 108 | " I = np.zeros((3,nc), dtype=int)\n", 109 | " for i in range(nc):\n", 110 | " I[:,i] = [2*i, 2*i+1, 2*i+2]\n", 111 | " J = np.repeat([np.arange(nc)], 3, axis=0)\n", 112 | " P = sparse.coo_matrix(\n", 113 | " (d.ravel(), (I.ravel(), J.ravel()))\n", 114 | " ).tocsr()\n", 115 | " return 0.5 * P\n", 116 | "\n", 117 | "def create_operator(n, sten):\n", 118 | " \"\"\"\n", 119 | " Create a 2D operator from a stencil.\n", 120 | " \"\"\"\n", 121 | " A = stencil.stencil_grid(sten, (n, n), format='csr')\n", 122 | " return A" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "## A two grid cycle" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "def twogrid(A, P, A1, u0, f0, nu):\n", 139 | " \n", 140 | " # relax and restrict the residual\n", 141 | " u0 = relaxJ(A, u0, f0, nu)\n", 142 | " f1 = P.T * (f0 - A * u0)\n", 143 | "\n", 144 | " # coarse grid solve\n", 145 | " u1 = sla.spsolve(A1, f1)\n", 146 | "\n", 147 | " # interpolate, correct, and relax\n", 148 | " u0 = u0 + P * u1\n", 149 | " u0 = relaxJ(A, u0, f0, nu)\n", 150 | " return u0" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "### Run a two grid cycle\n", 158 | "$$\n", 159 | "-u_{xx} - \\epsilon u_{yy} = f\n", 160 | "$$" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "k = 6\n", 170 | "n = 2**k - 1\n", 171 | "nc = 2**(k-1) - 1\n", 172 | "\n", 173 | "# matrix\n", 174 | "# eps = 1\n", 175 | "sten = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]])\n", 176 | "# eps = 0.001 (and rotate)\n", 177 | "# sten = diffusion.diffusion_stencil_2d(epsilon=0.001, theta=np.pi/40, type='FD')\n", 178 | "A = (n+1)**2 * create_operator(n, sten)\n", 179 | "\n", 180 | "# interpolation\n", 181 | "P1d = interpolation1d(nc, n)\n", 182 | "P = sparse.kron(P1d, P1d).tocsr()\n", 183 | "\n", 184 | "# coarse level matrix\n", 185 | "A1 = P.T * A * P\n", 186 | "\n", 187 | "# the problem\n", 188 | "x1d = np.linspace(0, 1, n+2)[1:-1]\n", 189 | "X, Y = np.meshgrid(x1d, x1d)\n", 190 | "ustar = (X**2 - X**4) * (Y**4 - Y**2)\n", 191 | "f = 2 * ((1-6*X**2) * Y**2 * (1 - Y**2) + (1-6*Y**2) * X**2 * (1-X**2))\n", 192 | "f = f.ravel()\n", 193 | "u = np.random.rand(n*n)\n", 194 | "\n", 195 | "res = [hnorm(f - A * u)]\n", 196 | "for i in range(10):\n", 197 | " u = twogrid(A, P, A1, u, f, 2)\n", 198 | " res.append(hnorm(f - A * u))\n", 199 | " \n", 200 | "res = np.array(res)\n", 201 | "print(res[1:] / res[:-1])" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": null, 207 | "metadata": {}, 208 | "outputs": [], 209 | "source": [ 210 | "u = np.random.rand(n*n)\n", 211 | "f = np.zeros((n*n,))\n", 212 | "plt.pcolormesh(u.reshape(n,n))\n", 213 | "plt.colorbar()\n", 214 | "u = relaxJ(A, u, f, 4)\n", 215 | "plt.figure()\n", 216 | "plt.pcolormesh(u.reshape(n,n))\n", 217 | "plt.colorbar()" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": null, 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [] 226 | } 227 | ], 228 | "metadata": { 229 | "kernelspec": { 230 | "display_name": "Python 3", 231 | "language": "python", 232 | "name": "python3" 233 | }, 234 | "language_info": { 235 | "codemirror_mode": { 236 | "name": "ipython", 237 | "version": 3 238 | }, 239 | "file_extension": ".py", 240 | "mimetype": "text/x-python", 241 | "name": "python", 242 | "nbconvert_exporter": "python", 243 | "pygments_lexer": "ipython3", 244 | "version": "3.7.2" 245 | } 246 | }, 247 | "nbformat": 4, 248 | "nbformat_minor": 1 249 | } 250 | -------------------------------------------------------------------------------- /algebraic-multigrid/1-AMG-coarse-mesh-example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### You may need to install pyamg" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "!pip install pyamg" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import pyamg\n", 26 | "import numpy\n", 27 | "from scipy.io import loadmat\n", 28 | "import matplotlib.pyplot as plt\n", 29 | "%matplotlib inline" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "Load some data" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "#data = loadmat('square.mat')\n", 46 | "data = pyamg.gallery.load_example('airfoil')" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "Create vertices and edges (for plotting)" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "A = data['A'].tocsr() # matrix\n", 63 | "V = data['vertices'][:A.shape[0]] # vertices of each variable\n", 64 | "E = numpy.vstack((A.tocoo().row,A.tocoo().col)).T # edges of the matrix graph" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "create an AMG hierarchy" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "mls = pyamg.ruge_stuben_solver(A, max_levels=2, max_coarse=1,\n", 81 | " CF='RS',keep=True)\n", 82 | "print(mls)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "# The CF splitting, 1 == C-node and 0 == F-node\n", 92 | "splitting = mls.levels[0].splitting\n", 93 | "C_nodes = splitting == 1\n", 94 | "F_nodes = splitting == 0\n", 95 | "\n", 96 | "plt.figure(figsize=(6,6))\n", 97 | "plt.axis('equal')\n", 98 | "\n", 99 | "for e in E:\n", 100 | " plt.plot(V[e,0], V[e,1], 'k-', zorder=1)\n", 101 | " \n", 102 | "plt.scatter(V[:,0][C_nodes], V[:,1][C_nodes], c='r', s=100.0, zorder=2) #plot C-nodes in red\n", 103 | "plt.scatter(V[:,0][F_nodes], V[:,1][F_nodes], c='b', s=100.0, zorder=2) #plot F-nodes in blue" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [] 112 | } 113 | ], 114 | "metadata": { 115 | "kernelspec": { 116 | "display_name": "Python 3", 117 | "language": "python", 118 | "name": "python3" 119 | }, 120 | "language_info": { 121 | "codemirror_mode": { 122 | "name": "ipython", 123 | "version": 3 124 | }, 125 | "file_extension": ".py", 126 | "mimetype": "text/x-python", 127 | "name": "python", 128 | "nbconvert_exporter": "python", 129 | "pygments_lexer": "ipython3", 130 | "version": "3.7.2" 131 | } 132 | }, 133 | "nbformat": 4, 134 | "nbformat_minor": 1 135 | } 136 | -------------------------------------------------------------------------------- /algebraic-multigrid/2-AMG-complexity.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "#### You may need to install pyamg" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "!pip install pyamg" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## import pyamg, numpy, scipy, and matplotlib" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "import pyamg\n", 33 | "import numpy as np\n", 34 | "from scipy.io import loadmat\n", 35 | "import matplotlib.pyplot as plt\n", 36 | "%matplotlib inline\n", 37 | "\n", 38 | "import warnings\n", 39 | "warnings.simplefilter(action='ignore', category=FutureWarning)" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "### Construct a rotated anisotropic diffusion problem" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "n=60\n", 56 | "sten = pyamg.gallery.diffusion.diffusion_stencil_2d(epsilon=0.0001, theta=np.pi/4, type='FE')\n", 57 | "A = pyamg.gallery.stencil_grid(sten, (n,n), format='csr')\n", 58 | "x1d = np.linspace(0, 1, n)\n", 59 | "X, Y = np.meshgrid(x1d, x1d)" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "### create an AMG hierarchy" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "ml = pyamg.ruge_stuben_solver(A, max_coarse=200, keep=True)" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "ml" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "splitting0 = ml.levels[0].splitting\n", 94 | "plt.figure(figsize=(8,8))\n", 95 | "\n", 96 | "X = X.ravel()\n", 97 | "Y = Y.ravel()\n", 98 | "\n", 99 | "I = np.where(splitting0==0)[0]\n", 100 | "plt.scatter(X[I], Y[I], color='b', s=8, marker='s', lw=0)\n", 101 | "\n", 102 | "I = np.where(splitting0==1)[0]\n", 103 | "plt.scatter(X[I], Y[I], color='b', s=16, marker='s', lw=0)\n", 104 | "\n", 105 | "splitting1 = ml.levels[1].splitting\n", 106 | "I1 = np.where(splitting1==1)[0]\n", 107 | "plt.scatter(X[I[I1]], Y[I[I1]], color='r', s=22, marker='s', lw=0)" 108 | ] 109 | } 110 | ], 111 | "metadata": { 112 | "kernelspec": { 113 | "display_name": "Python 3", 114 | "language": "python", 115 | "name": "python3" 116 | }, 117 | "language_info": { 118 | "codemirror_mode": { 119 | "name": "ipython", 120 | "version": 3 121 | }, 122 | "file_extension": ".py", 123 | "mimetype": "text/x-python", 124 | "name": "python", 125 | "nbconvert_exporter": "python", 126 | "pygments_lexer": "ipython3", 127 | "version": "3.7.2" 128 | } 129 | }, 130 | "nbformat": 4, 131 | "nbformat_minor": 1 132 | } 133 | -------------------------------------------------------------------------------- /algebraic-multigrid/3-SA-AMG-in-1D.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# install pyamg if needed\n", 10 | "!pip install pyamg" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "import numpy as np\n", 20 | "import scipy.io as sio\n", 21 | "import pyamg\n", 22 | "import scipy.sparse.linalg as sla\n", 23 | "\n", 24 | "import matplotlib.pyplot as plt\n", 25 | "from matplotlib import collections\n", 26 | "from matplotlib import tri\n", 27 | "%matplotlib inline\n", 28 | "\n", 29 | "import warnings\n", 30 | "warnings.simplefilter(action='ignore', category=FutureWarning)" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "# Make a 1D Problem" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "A = pyamg.gallery.poisson((8,), format='csr')\n", 47 | "x = np.linspace(0, 1, 10)[1:-1]" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "metadata": {}, 54 | "outputs": [], 55 | "source": [ 56 | "ml = pyamg.smoothed_aggregation_solver(\n", 57 | " A, max_coarse=1, keep=True, improve_candidates=None,\n", 58 | " presmoother=('block_gauss_seidel', {'sweep': 'forward'}),\n", 59 | " postsmoother=('block_gauss_seidel', {'sweep': 'forward'}),\n", 60 | " smooth=('jacobi', {'degree': 2}))" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "ml" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "# Aggregates" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "AggOp = ml.levels[0].AggOp.tocoo()" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "AggOp.todense()" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "plt.scatter(x, 0*x, c=AggOp.col, s=100)" 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": {}, 109 | "source": [ 110 | "# Columns of $T$" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "T = ml.levels[0].T.todense()" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [ 128 | "T" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "metadata": {}, 135 | "outputs": [], 136 | "source": [ 137 | "for i in range(3):\n", 138 | " plt.plot(x, T[:,i], '-o', ms=15, clip_on=False)" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "# PLot $P$" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "P = ml.levels[0].P.todense()" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "P" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "T" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "for i in range(3):\n", 182 | " plt.plot(x, P[:,i], '-o', ms=15, clip_on=False)" 183 | ] 184 | } 185 | ], 186 | "metadata": { 187 | "kernelspec": { 188 | "display_name": "Python 3", 189 | "language": "python", 190 | "name": "python3" 191 | }, 192 | "language_info": { 193 | "codemirror_mode": { 194 | "name": "ipython", 195 | "version": 3 196 | }, 197 | "file_extension": ".py", 198 | "mimetype": "text/x-python", 199 | "name": "python", 200 | "nbconvert_exporter": "python", 201 | "pygments_lexer": "ipython3", 202 | "version": "3.7.2" 203 | } 204 | }, 205 | "nbformat": 4, 206 | "nbformat_minor": 2 207 | } 208 | -------------------------------------------------------------------------------- /algebraic-multigrid/4-components-of-smoothed-aggregation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# install pyamg if needed\n", 10 | "!pip install pyamg" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "!wget -Nq https://raw.githubusercontent.com/copper-multigrid-conference/2019-tutorials/master/algebraic-multigrid/square.mat" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "import numpy as np\n", 29 | "import scipy.io as sio\n", 30 | "import pyamg\n", 31 | "import scipy.sparse.linalg as sla\n", 32 | "\n", 33 | "import matplotlib.pyplot as plt\n", 34 | "from matplotlib import collections\n", 35 | "from matplotlib import tri\n", 36 | "%matplotlib inline\n", 37 | "\n", 38 | "import warnings\n", 39 | "warnings.simplefilter(action='ignore', category=FutureWarning)" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "# Read in a problem\n", 47 | "\n", 48 | "This is a poisson problem on an unstructured mesh\n", 49 | "\n", 50 | "`A` is the matrix\n", 51 | "\n", 52 | "`vertices` are the vertices in the mesh\n", 53 | "\n", 54 | "`elements` are the triangles in the mesh" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "data = sio.loadmat('square.mat')\n", 64 | "\n", 65 | "A = data['A'].tocsr() # matrix\n", 66 | "V = data['vertices'][:A.shape[0]] # vertices of each variable\n", 67 | "Elmts = data['elements']\n", 68 | "n = A.shape[0]\n", 69 | "\n", 70 | "x = V[:,0]\n", 71 | "y = V[:,1]" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "# Plot the mesh" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "f, ax = plt.subplots(1, figsize=(8,8))\n", 88 | "t = tri.Triangulation(x, y, Elmts)\n", 89 | "plt.triplot(t)\n", 90 | "plt.axis('off')" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "# Plot the \"graph of $A$\"" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "thismatrix = A\n", 107 | "E = np.vstack((thismatrix.tocoo().row,thismatrix.tocoo().col)).T # edges of the matrix graph\n", 108 | "\n", 109 | "f, ax = plt.subplots(1, figsize=(8,8))\n", 110 | "\n", 111 | "lines = np.empty((E.shape[0], 2, 2))\n", 112 | "lines[:,0,0] = x[E[:,0]] # xstart\n", 113 | "lines[:,1,0] = x[E[:,1]] # xend\n", 114 | "lines[:,0,1] = y[E[:,0]] # ystart\n", 115 | "lines[:,1,1] = y[E[:,1]] # yend\n", 116 | "\n", 117 | "ls = collections.LineCollection(lines)\n", 118 | "ax.add_collection(ls, autolim=True)\n", 119 | "ax.autoscale_view()\n", 120 | "ax.axis('off')" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "# Set the near null space or candidate vectors" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "B = np.ones((n,1))" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": {}, 142 | "source": [ 143 | "# Find the strength of connection in the graph of $A$" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "S = pyamg.strength.symmetric_strength_of_connection(A, theta = 0.1)\n", 153 | "S" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "def plotmatrix(thismatrix, ax, lw=1):\n", 163 | " E = np.vstack((thismatrix.tocoo().row,thismatrix.tocoo().col)).T # edges of the matrix graph\n", 164 | "\n", 165 | " lines = np.empty((E.shape[0], 2, 2))\n", 166 | " lines[:,0,0] = x[E[:,0]] # xstart\n", 167 | " lines[:,1,0] = x[E[:,1]] # xend\n", 168 | " lines[:,0,1] = y[E[:,0]] # ystart\n", 169 | " lines[:,1,1] = y[E[:,1]] # yend\n", 170 | "\n", 171 | " ls = collections.LineCollection(lines)\n", 172 | " ax.add_collection(ls, autolim=True)\n", 173 | " ls.set_linewidth(lw)\n", 174 | " ax.autoscale_view()\n", 175 | " ax.axis('off')" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "f, ax = plt.subplots(1, figsize=(6,6))\n", 185 | "\n", 186 | "# FIRST A\n", 187 | "plotmatrix(A, ax)\n", 188 | "\n", 189 | "# THEN S\n", 190 | "plotmatrix(S, ax, lw=6)\n", 191 | "\n", 192 | "# What about point 44?\n", 193 | "i = 44\n", 194 | "J = A.getrow(i).indices\n", 195 | "for j in J:\n", 196 | " plt.plot(x[j], y[j], 'ro')\n", 197 | " plt.text(x[j], y[j], '%d'%j, fontsize=20, color='r')\n", 198 | " \n", 199 | "print(A.getrow(i))" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "# Aggregate based on Strength" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "AggOp, Cpts = pyamg.aggregation.aggregate.standard_aggregation(S)" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "AggOp" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": null, 230 | "metadata": {}, 231 | "outputs": [], 232 | "source": [ 233 | "f, ax = plt.subplots(1, figsize=(6,6))\n", 234 | "\n", 235 | "plotmatrix(A, ax)\n", 236 | "\n", 237 | "plotmatrix(S, ax, lw=2)\n", 238 | "\n", 239 | "# now plot each aggregate\n", 240 | "for i in range(AggOp.shape[1]):\n", 241 | " J = AggOp.getcol(i).tocoo().row\n", 242 | " for j1 in J:\n", 243 | " for j2 in J: \n", 244 | " if j1 != j2:\n", 245 | " if A[j1, j2]:\n", 246 | " plt.plot([x[j1], x[j2]], [y[j1], y[j2]], 'r', lw=4)\n", 247 | " \n", 248 | "#for i, v in enumerate(V):\n", 249 | "# plt.text(v[0], v[1], '%d'%i)" 250 | ] 251 | }, 252 | { 253 | "cell_type": "markdown", 254 | "metadata": {}, 255 | "source": [ 256 | "# Create an interpolation operator" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [ 265 | "T, R = pyamg.aggregation.tentative.fit_candidates(AggOp, B)" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": null, 271 | "metadata": {}, 272 | "outputs": [], 273 | "source": [ 274 | "T" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": {}, 280 | "source": [ 281 | "# Improve the interpolation operator" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": null, 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "P = pyamg.aggregation.smooth.jacobi_prolongation_smoother(A, T, S, B, degree=1).tocsr()" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "T = T.toarray()\n", 300 | "P = P.toarray()" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": null, 306 | "metadata": {}, 307 | "outputs": [], 308 | "source": [ 309 | "f, ax = plt.subplots(1, figsize=(4,4))\n", 310 | "t = tri.Triangulation(x, y, Elmts)\n", 311 | "plt.tripcolor(t, T[:,15])\n", 312 | "plt.axis('off')\n", 313 | "\n", 314 | "f, ax = plt.subplots(1, figsize=(4,4))\n", 315 | "t = tri.Triangulation(x, y, Elmts)\n", 316 | "plt.tripcolor(t, P[:,15])\n", 317 | "plt.axis('off')" 318 | ] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "metadata": {}, 323 | "source": [ 324 | "## A full multilevel hierarchy" 325 | ] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "execution_count": null, 330 | "metadata": {}, 331 | "outputs": [], 332 | "source": [ 333 | "ml = pyamg.smoothed_aggregation_solver(A, max_levels=2, keep=True, improve_candidates=None)\n", 334 | "print(ml)" 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "execution_count": null, 340 | "metadata": {}, 341 | "outputs": [], 342 | "source": [ 343 | "res = []\n", 344 | "b = np.zeros((n,))\n", 345 | "x = np.random.rand(n)\n", 346 | "x = ml.solve(b, x0=x, residuals=res)\n", 347 | "print(res)" 348 | ] 349 | } 350 | ], 351 | "metadata": { 352 | "kernelspec": { 353 | "display_name": "Python 3", 354 | "language": "python", 355 | "name": "python3" 356 | }, 357 | "language_info": { 358 | "codemirror_mode": { 359 | "name": "ipython", 360 | "version": 3 361 | }, 362 | "file_extension": ".py", 363 | "mimetype": "text/x-python", 364 | "name": "python", 365 | "nbconvert_exporter": "python", 366 | "pygments_lexer": "ipython3", 367 | "version": "3.7.2" 368 | } 369 | }, 370 | "nbformat": 4, 371 | "nbformat_minor": 2 372 | } 373 | -------------------------------------------------------------------------------- /algebraic-multigrid/5-AMG-advanced-options-anisotropy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# You may need to install pyamg\n", 10 | "!pip3 install pyamg" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "import numpy as np\n", 20 | "import scipy.io as sio\n", 21 | "import pyamg\n", 22 | "import scipy.sparse.linalg as sla\n", 23 | "\n", 24 | "import matplotlib.pyplot as plt\n", 25 | "from matplotlib import collections\n", 26 | "from matplotlib import tri\n", 27 | "%matplotlib inline\n", 28 | "\n", 29 | "import warnings\n", 30 | "warnings.simplefilter(action='ignore', category=FutureWarning)" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "# Anisotropic diffusion problem" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "stencil = pyamg.gallery.diffusion.diffusion_stencil_2d(type='FE', epsilon=0.001, theta=2 * np.pi / 16.0)\n", 47 | "A = pyamg.gallery.stencil_grid(stencil, (50, 50), format='csr')" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "# Candidate vectors" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "Start with a single vector" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "B = np.ones((A.shape[0], 1))" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "ml = pyamg.smoothed_aggregation_solver(A, B, max_coarse=1)\n", 80 | "print(ml)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "res = []\n", 90 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 91 | "res = np.array(res)\n", 92 | "res[1:] / res[:-1]" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "Convergence is poor, what does the solution (error) look like?" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "len(res)" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "plt.pcolormesh(x.reshape(50,50))\n", 118 | "plt.colorbar()" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": {}, 124 | "source": [ 125 | "Can we augment the candidate vector? Take a vector of ones and the old solution (error) vector." 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "B = np.vstack((np.ones(A.shape[0],), x)).T" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": null, 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "B.shape" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "ml = pyamg.smoothed_aggregation_solver(A, B, max_coarse=1, keep=True)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "print(ml)" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "res = []\n", 171 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 172 | "res = np.array(res)\n", 173 | "res[1:] / res[:-1]" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [ 182 | "plt.pcolormesh(x.reshape(50,50))" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "metadata": {}, 189 | "outputs": [], 190 | "source": [ 191 | "ml" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": null, 197 | "metadata": {}, 198 | "outputs": [], 199 | "source": [ 200 | "ml.levels[0].P.shape" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "metadata": {}, 207 | "outputs": [], 208 | "source": [ 209 | "ml.levels[0].AggOp.shape" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "# Try Adaptive AMG\n", 217 | "\n", 218 | "In short, adaptive AMG will run a cycle on $Ax=0$ to determine improved $B$ vectors" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": {}, 225 | "outputs": [], 226 | "source": [ 227 | "mladapt, work = pyamg.aggregation.adaptive_sa_solver(A, num_candidates=3)\n", 228 | "print(mladapt)" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "Let's look at the candidate vectors" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": {}, 242 | "outputs": [], 243 | "source": [ 244 | "plt.pcolormesh(mladapt.levels[0].B[:,0].reshape(50,50))" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "plt.pcolormesh(mladapt.levels[0].B[:,1].reshape(50,50))" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": null, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [ 262 | "plt.pcolormesh(mladapt.levels[0].B[:,2].reshape(50,50))" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": null, 268 | "metadata": {}, 269 | "outputs": [], 270 | "source": [ 271 | "ml = pyamg.smoothed_aggregation_solver(A, mladapt.levels[0].B, max_coarse=1)\n", 272 | "print(ml)" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "# Check the convergence\n", 280 | "\n", 281 | "Let's see how well improved candidate vectors work for both a SA and adaptive SA method." 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": null, 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "res = []\n", 291 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 292 | "res = np.array(res)\n", 293 | "res[1:] / res[:-1]" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "metadata": {}, 300 | "outputs": [], 301 | "source": [ 302 | "res = []\n", 303 | "x = mladapt.solve(np.random.rand(A.shape[0]), residuals=res)\n", 304 | "res = np.array(res)\n", 305 | "res[1:] / res[:-1]" 306 | ] 307 | }, 308 | { 309 | "cell_type": "markdown", 310 | "metadata": {}, 311 | "source": [ 312 | "# Improving SOC" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [ 321 | "B = np.ones((A.shape[0],1))\n", 322 | "ml = pyamg.smoothed_aggregation_solver(A, B, strength='evolution', max_coarse=5)\n", 323 | "print(ml)" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": null, 329 | "metadata": {}, 330 | "outputs": [], 331 | "source": [ 332 | "res = []\n", 333 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 334 | "res = np.array(res)\n", 335 | "res[1:] / res[:-1]" 336 | ] 337 | }, 338 | { 339 | "cell_type": "markdown", 340 | "metadata": {}, 341 | "source": [ 342 | "# Improving Interpolation" 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": null, 348 | "metadata": {}, 349 | "outputs": [], 350 | "source": [ 351 | "B = np.ones((A.shape[0],1))\n", 352 | "smooth=('energy', {'krylov': 'cg', 'maxiter': 4, 'degree': 3, 'weighting': 'local'})\n", 353 | "ml = pyamg.smoothed_aggregation_solver(A, B, strength='evolution', max_coarse=5,\n", 354 | " smooth=smooth)\n", 355 | "print(ml)" 356 | ] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "execution_count": null, 361 | "metadata": {}, 362 | "outputs": [], 363 | "source": [ 364 | "res = []\n", 365 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 366 | "res = np.array(res)\n", 367 | "res[1:] / res[:-1]" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": null, 373 | "metadata": {}, 374 | "outputs": [], 375 | "source": [ 376 | "res" 377 | ] 378 | }, 379 | { 380 | "cell_type": "code", 381 | "execution_count": null, 382 | "metadata": {}, 383 | "outputs": [], 384 | "source": [] 385 | } 386 | ], 387 | "metadata": { 388 | "kernelspec": { 389 | "display_name": "Python 3", 390 | "language": "python", 391 | "name": "python3" 392 | }, 393 | "language_info": { 394 | "codemirror_mode": { 395 | "name": "ipython", 396 | "version": 3 397 | }, 398 | "file_extension": ".py", 399 | "mimetype": "text/x-python", 400 | "name": "python", 401 | "nbconvert_exporter": "python", 402 | "pygments_lexer": "ipython3", 403 | "version": "3.7.2" 404 | } 405 | }, 406 | "nbformat": 4, 407 | "nbformat_minor": 2 408 | } 409 | -------------------------------------------------------------------------------- /algebraic-multigrid/6-AMG-advanced-options-nonsymmetric-flow.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# you may need to install pyamg\n", 10 | "!pip install pyamg" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "import numpy as np\n", 20 | "import scipy.io as sio\n", 21 | "import pyamg\n", 22 | "import scipy.sparse.linalg as sla\n", 23 | "\n", 24 | "import matplotlib.pyplot as plt\n", 25 | "from matplotlib import collections\n", 26 | "from matplotlib import tri\n", 27 | "%matplotlib inline\n", 28 | "\n", 29 | "import warnings\n", 30 | "warnings.simplefilter(action='ignore', category=FutureWarning)" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "# recirculating flow" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "A = pyamg.gallery.load_example('recirc_flow')['A'].tocsr()" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "ml = pyamg.smoothed_aggregation_solver(A)" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "res = []\n", 65 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 66 | "res = np.array(res)\n", 67 | "res" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "The error explodes. Let's see what relaxation does (or does not) do" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "x = np.random.rand(A.shape[0])\n", 84 | "pyamg.relaxation.relaxation.jacobi(A, x, 0*x, iterations=5, omega=4/3)\n", 85 | "plt.pcolormesh(x.reshape(15,15))\n", 86 | "plt.colorbar()" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "Since Jacobi requires (sufficient) a weakly diagonally dominant matrix, this may not be the right smoother. Let's try Gauss-Seidel on $AA^T$" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "x = np.random.rand(A.shape[0])\n", 103 | "pyamg.relaxation.relaxation.gauss_seidel_ne(A, x, 0*x, iterations=5, omega=4/3)\n", 104 | "plt.pcolormesh(x.reshape(15,15))\n", 105 | "plt.colorbar()" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "ml = pyamg.smoothed_aggregation_solver(A,\n", 115 | " presmoother=('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 2}),\n", 116 | " postsmoother=('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 2}),)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "res = []\n", 126 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 127 | "res = np.array(res)\n", 128 | "res[1:]/res[:-1]" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "At least the problem doesn't blow up.\n", 136 | "\n", 137 | "What does the solution look like?" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "plt.pcolormesh(x.reshape(15,15))\n", 147 | "plt.colorbar()" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "# Adding better strength" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "strength=('evolution', {'k': 2, 'proj_type': 'l2', 'epsilon': 4.0})\n", 164 | "ml = pyamg.smoothed_aggregation_solver(A,\n", 165 | " presmoother=('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 2}),\n", 166 | " postsmoother=('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 2}),\n", 167 | " strength=strength)" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "res = []\n", 177 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 178 | "res = np.array(res)\n", 179 | "res[1:]/res[:-1]" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "plt.pcolormesh(x.reshape(15,15))" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "# Improving the candidate vectors" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "improve_candidates=[('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 4}), None, None, None, None, None, None, None, None, None, None, None, None, None, None]\n", 205 | "ml = pyamg.smoothed_aggregation_solver(A,\n", 206 | " presmoother=('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 2}),\n", 207 | " postsmoother=('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 2}),\n", 208 | " strength=strength,\n", 209 | " improve_candidates=improve_candidates)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "res = []\n", 219 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 220 | "res = np.array(res)\n", 221 | "res[1:]/res[:-1]" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "plt.pcolormesh(x.reshape(15,15))" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "# Adding energy minimization" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "smooth=('energy', {'krylov': 'gmres', 'maxiter': 2, 'degree': 1, 'weighting': 'local'})\n", 247 | "ml = pyamg.smoothed_aggregation_solver(A,\n", 248 | " presmoother=('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 2}),\n", 249 | " postsmoother=('gauss_seidel_nr', {'sweep': 'symmetric', 'iterations': 2}),\n", 250 | " strength=strength,\n", 251 | " improve_candidates=improve_candidates,\n", 252 | " smooth=smooth)" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": null, 258 | "metadata": {}, 259 | "outputs": [], 260 | "source": [ 261 | "ml" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": null, 267 | "metadata": {}, 268 | "outputs": [], 269 | "source": [ 270 | "res = []\n", 271 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 272 | "res = np.array(res)\n", 273 | "res[1:]/res[:-1]" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": null, 279 | "metadata": {}, 280 | "outputs": [], 281 | "source": [ 282 | "plt.pcolormesh(x.reshape(15,15))" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "# Oh, right! A Krylov accelerator" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": null, 295 | "metadata": {}, 296 | "outputs": [], 297 | "source": [ 298 | "res = []\n", 299 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res, accel='gmres')\n", 300 | "res = np.array(res)\n", 301 | "res[1:]/res[:-1]" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "metadata": {}, 308 | "outputs": [], 309 | "source": [ 310 | "res" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": null, 316 | "metadata": {}, 317 | "outputs": [], 318 | "source": [] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": null, 323 | "metadata": {}, 324 | "outputs": [], 325 | "source": [] 326 | } 327 | ], 328 | "metadata": { 329 | "kernelspec": { 330 | "display_name": "Python 3", 331 | "language": "python", 332 | "name": "python3" 333 | }, 334 | "language_info": { 335 | "codemirror_mode": { 336 | "name": "ipython", 337 | "version": 3 338 | }, 339 | "file_extension": ".py", 340 | "mimetype": "text/x-python", 341 | "name": "python", 342 | "nbconvert_exporter": "python", 343 | "pygments_lexer": "ipython3", 344 | "version": "3.7.2" 345 | } 346 | }, 347 | "nbformat": 4, 348 | "nbformat_minor": 2 349 | } 350 | -------------------------------------------------------------------------------- /algebraic-multigrid/7-AMG-advanced-options-systems-elasticity.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy.io as sio\n", 11 | "import pyamg\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "from matplotlib import collections\n", 16 | "from matplotlib import tri\n", 17 | "%matplotlib inline\n", 18 | "\n", 19 | "import warnings\n", 20 | "warnings.simplefilter(action='ignore', category=FutureWarning)" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "# Elasticity" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "A, B = pyamg.gallery.linear_elasticity((10,10))" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "B" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "A" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "plt.figure(figsize=(8,8))\n", 64 | "x, y = np.meshgrid(np.linspace(0,1,10), np.linspace(0,1,10))\n", 65 | "plt.quiver(x, y, B[0::2,0], B[1::2,0], color='b')\n", 66 | "plt.quiver(x, y, B[0::2,1], B[1::2,1], color='r')\n", 67 | "plt.quiver(x, y, B[0::2,2], B[1::2,2], color='g')" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "ml = pyamg.smoothed_aggregation_solver(A, B, max_coarse=5, keep=True)\n", 77 | "print(ml)" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "ml.levels[0].P.shape" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "ml.levels[0].AggOp.shape" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "res = []\n", 105 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 106 | "res = np.array(res)\n", 107 | "res[1:]/res[:-1]" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "A = A.tocsr()\n", 117 | "ml = pyamg.smoothed_aggregation_solver(A, max_coarse=5)\n", 118 | "print(ml)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "res = []\n", 128 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res)\n", 129 | "res = np.array(res)\n", 130 | "res[1:]/res[:-1]" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "res = []\n", 140 | "x = ml.solve(np.random.rand(A.shape[0]), residuals=res, accel='cg')\n", 141 | "res = np.array(res)\n", 142 | "res[1:]/res[:-1]" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [] 158 | } 159 | ], 160 | "metadata": { 161 | "kernelspec": { 162 | "display_name": "Python 3", 163 | "language": "python", 164 | "name": "python3" 165 | }, 166 | "language_info": { 167 | "codemirror_mode": { 168 | "name": "ipython", 169 | "version": 3 170 | }, 171 | "file_extension": ".py", 172 | "mimetype": "text/x-python", 173 | "name": "python", 174 | "nbconvert_exporter": "python", 175 | "pygments_lexer": "ipython3", 176 | "version": "3.7.2" 177 | } 178 | }, 179 | "nbformat": 4, 180 | "nbformat_minor": 2 181 | } 182 | -------------------------------------------------------------------------------- /algebraic-multigrid/diffusion.py: -------------------------------------------------------------------------------- 1 | """Generate a diffusion stencil 2 | 3 | Supports isotropic diffusion (FE,FD), anisotropic diffusion (FE, FD), and 4 | rotated anisotropic diffusion (FD). 5 | 6 | The stencils include redundancy to maintain readability for simple cases (e.g. 7 | isotropic diffusion). 8 | 9 | """ 10 | from __future__ import print_function 11 | 12 | import numpy as np 13 | 14 | __docformat__ = "restructuredtext en" 15 | 16 | __all__ = ['diffusion_stencil_2d'] 17 | 18 | 19 | def diffusion_stencil_2d(epsilon=1.0, theta=0.0, type='FE'): 20 | """ 21 | Rotated Anisotropic diffusion in 2d of the form: 22 | 23 | -div Q A Q^T grad u 24 | 25 | Q = [cos(theta) -sin(theta)] 26 | [sin(theta) cos(theta)] 27 | 28 | A = [1 0 ] 29 | [0 eps ] 30 | 31 | Parameters 32 | ---------- 33 | epsilon : float, optional 34 | Anisotropic diffusion coefficient: -div A grad u, 35 | where A = [1 0; 0 epsilon]. The default is isotropic, epsilon=1.0 36 | theta : float, optional 37 | Rotation angle `theta` in radians defines -div Q A Q^T grad, 38 | where Q = [cos(`theta`) -sin(`theta`); sin(`theta`) cos(`theta`)]. 39 | type : {'FE','FD'} 40 | Specifies the discretization as Q1 finite element (FE) or 2nd order 41 | finite difference (FD) 42 | The default is `theta` = 0.0 43 | 44 | Returns 45 | ------- 46 | stencil : numpy array 47 | A 3x3 diffusion stencil 48 | 49 | See Also 50 | -------- 51 | stencil_grid, poisson 52 | 53 | Notes 54 | ----- 55 | Not all combinations are supported. 56 | 57 | Examples 58 | -------- 59 | >>> import scipy as sp 60 | >>> from pyamg.gallery.diffusion import diffusion_stencil_2d 61 | >>> sten = diffusion_stencil_2d(epsilon=0.0001,theta=sp.pi/6,type='FD') 62 | >>> print sten 63 | [[-0.2164847 -0.750025 0.2164847] 64 | [-0.250075 2.0002 -0.250075 ] 65 | [ 0.2164847 -0.750025 -0.2164847]] 66 | 67 | """ 68 | eps = float(epsilon) # for brevity 69 | theta = float(theta) 70 | 71 | C = np.cos(theta) 72 | S = np.sin(theta) 73 | CS = C*S 74 | CC = C**2 75 | SS = S**2 76 | 77 | if(type == 'FE'): 78 | """FE approximation to:: 79 | 80 | - (eps c^2 + s^2) u_xx + 81 | -2(eps - 1) c s u_xy + 82 | - ( c^2 + eps s^2) u_yy 83 | 84 | [ -c^2*eps-s^2+3*c*s*(eps-1)-c^2-s^2*eps, 85 | 2*c^2*eps+2*s^2-4*c^2-4*s^2*eps, 86 | -c^2*eps-s^2-3*c*s*(eps-1)-c^2-s^2*eps] 87 | 88 | [-4*c^2*eps-4*s^2+2*c^2+2*s^2*eps, 89 | 8*c^2*eps+8*s^2+8*c^2+8*s^2*eps, 90 | -4*c^2*eps-4*s^2+2*c^2+2*s^2*eps] 91 | 92 | [-c^2*eps-s^2-3*c*s*(eps-1)-c^2-s^2*eps, 93 | 2*c^2*eps+2*s^2-4*c^2-4*s^2*eps, 94 | -c^2*eps-s^2+3*c*s*(eps-1)-c^2-s^2*eps] 95 | 96 | c = cos(theta) 97 | s = sin(theta) 98 | """ 99 | 100 | a = (-1*eps - 1)*CC + (-1*eps - 1)*SS + (3*eps - 3)*CS 101 | b = (2*eps - 4)*CC + (-4*eps + 2)*SS 102 | c = (-1*eps - 1)*CC + (-1*eps - 1)*SS + (-3*eps + 3)*CS 103 | d = (-4*eps + 2)*CC + (2*eps - 4)*SS 104 | e = (8*eps + 8)*CC + (8*eps + 8)*SS 105 | 106 | stencil = np.array([[a, b, c], 107 | [d, e, d], 108 | [c, b, a]]) / 6.0 109 | 110 | elif type == 'FD': 111 | """FD approximation to: 112 | 113 | - (eps c^2 + s^2) u_xx + 114 | -2(eps - 1) c s u_xy + 115 | - ( c^2 + eps s^2) u_yy 116 | 117 | c = cos(theta) 118 | s = sin(theta) 119 | 120 | A = [ 1/2(eps - 1) c s -(c^2 + eps s^2) -1/2(eps - 1) c s ] 121 | [ ] 122 | [ -(eps c^2 + s^2) 2 (eps + 1) -(eps c^2 + s^2) ] 123 | [ ] 124 | [ -1/2(eps - 1) c s -(c^2 + eps s^2) 1/2(eps - 1) c s ] 125 | """ 126 | 127 | a = 0.5*(eps - 1)*CS 128 | b = -(eps*SS + CC) 129 | c = -a 130 | d = -(eps*CC + SS) 131 | e = 2.0*(eps + 1) 132 | 133 | stencil = np.array([[a, b, c], 134 | [d, e, d], 135 | [c, b, a]]) 136 | 137 | return stencil 138 | 139 | 140 | def _symbolic_rotation_helper(): 141 | """ 142 | Simple SymPy script to generate the 3D rotation matrix and products for 143 | diffusion_stencil_3d. 144 | 145 | """ 146 | 147 | from sympy import symbols, Matrix 148 | 149 | cpsi, spsi = symbols('cpsi, spsi') 150 | cth, sth = symbols('cth, sth') 151 | cphi, sphi = symbols('cphi, sphi') 152 | Rpsi = Matrix([[cpsi, spsi, 0], [-spsi, cpsi, 0], [0, 0, 1]]) 153 | Rth = Matrix([[1, 0, 0], [0, cth, sth], [0, -sth, cth]]) 154 | Rphi = Matrix([[cphi, sphi, 0], [-sphi, cphi, 0], [0, 0, 1]]) 155 | 156 | Q = Rpsi * Rth * Rphi 157 | 158 | epsy, epsz = symbols('epsy, epsz') 159 | A = Matrix([[1, 0, 0], [0, epsy, 0], [0, 0, epsz]]) 160 | 161 | D = Q * A * Q.T 162 | 163 | for i in range(3): 164 | for j in range(3): 165 | print('D[%d, %d] = %s' % (i, j, D[i, j])) 166 | 167 | 168 | def _symbolic_product_helper(): 169 | """ 170 | Simple SymPy script to generate the 3D products for diffusion_stencil_3d. 171 | 172 | """ 173 | 174 | from sympy import symbols, Matrix 175 | 176 | D11, D12, D13, D21, D22, D23, D31, D32, D33 =\ 177 | symbols('D11, D12, D13, D21, D22, D23, D31, D32, D33') 178 | 179 | D = Matrix([[D11, D12, D13], [D21, D22, D23], [D31, D32, D33]]) 180 | grad = Matrix([['dx', 'dy', 'dz']]).T 181 | div = grad.T 182 | 183 | a = div * D * grad 184 | 185 | print(a[0]) 186 | 187 | 188 | def diffusion_stencil_3d(epsilony=1.0, epsilonz=1.0, theta=0.0, phi=0.0, 189 | psi=0.0, type='FD'): 190 | """ 191 | Rotated Anisotropic diffusion in 3d of the form: 192 | 193 | -div Q A Q^T grad u 194 | 195 | Q = Rpsi Rtheta Rphi 196 | 197 | Rpsi = [ c s 0 ] 198 | [-s c 0 ] 199 | [ 0 0 1 ] 200 | c = cos(psi) 201 | s = sin(psi) 202 | 203 | Rtheta = [ 1 0 0 ] 204 | [ 0 c s ] 205 | [ 0 -s c ] 206 | c = cos(theta) 207 | s = sin(theta) 208 | 209 | Rphi = [ c s 0 ] 210 | [-s c 0 ] 211 | [ 0 0 1 ] 212 | c = cos(phi) 213 | s = sin(phi) 214 | 215 | Here Euler Angles are used: 216 | http://en.wikipedia.org/wiki/Euler_angles 217 | 218 | This results in 219 | 220 | Q = [ cphi*cpsi - cth*sphi*spsi, cpsi*sphi + cphi*cth*spsi, spsi*sth] 221 | [ - cphi*spsi - cpsi*cth*sphi, cphi*cpsi*cth - sphi*spsi, cpsi*sth] 222 | [ sphi*sth, -cphi*sth, cth] 223 | 224 | A = [1 0 ] 225 | [0 epsy ] 226 | [0 0 epsz] 227 | 228 | D = Q A Q^T 229 | 230 | Parameters 231 | ---------- 232 | epsilony : float, optional 233 | Anisotropic diffusion coefficient in the y-direction 234 | where A = [1 0 0; 0 epsilon_y 0; 0 0 epsilon_z]. The default is 235 | isotropic, epsilon=1.0 236 | epsilonz : float, optional 237 | Anisotropic diffusion coefficient in the z-direction 238 | where A = [1 0 0; 0 epsilon_y 0; 0 0 epsilon_z]. The default is 239 | isotropic, epsilon=1.0 240 | theta : float, optional 241 | Euler rotation angle `theta` in radians. The default is 0.0. 242 | phi : float, optional 243 | Euler rotation angle `phi` in radians. The default is 0.0. 244 | psi : float, optional 245 | Euler rotation angle `psi` in radians. The default is 0.0. 246 | type : {'FE','FD'} 247 | Specifies the discretization as Q1 finite element (FE) or 2nd order 248 | finite difference (FD) 249 | 250 | Returns 251 | ------- 252 | stencil : numpy array 253 | A 3x3 diffusion stencil 254 | 255 | See Also 256 | -------- 257 | stencil_grid, poisson, _symbolic_rotation_helper, _symbolic_product_helper 258 | 259 | Notes 260 | ----- 261 | Not all combinations are supported. 262 | 263 | Examples 264 | -------- 265 | >>> import scipy as sp 266 | >>> from pyamg.gallery.diffusion import diffusion_stencil_2d 267 | >>> sten = diffusion_stencil_2d(epsilon=0.0001,theta=sp.pi/6,type='FD') 268 | >>> print sten 269 | [[-0.2164847 -0.750025 0.2164847] 270 | [-0.250075 2.0002 -0.250075 ] 271 | [ 0.2164847 -0.750025 -0.2164847]] 272 | 273 | """ 274 | 275 | epsy = float(epsilony) # for brevity 276 | epsz = float(epsilonz) # for brevity 277 | theta = float(theta) 278 | phi = float(phi) 279 | psi = float(psi) 280 | 281 | D = np.zeros((3, 3)) 282 | cphi = np.cos(phi) 283 | sphi = np.sin(phi) 284 | cth = np.cos(theta) 285 | sth = np.sin(theta) 286 | cpsi = np.cos(psi) 287 | spsi = np.sin(psi) 288 | 289 | # from _symbolic_rotation_helper 290 | D[0, 0] = epsy*(cphi*cth*spsi + cpsi*sphi)**2 + epsz*spsi**2*sth**2 +\ 291 | (cphi*cpsi - cth*sphi*spsi)**2 292 | D[0, 1] = cpsi*epsz*spsi*sth**2 +\ 293 | epsy*(cphi*cpsi*cth - sphi*spsi)*(cphi*cth*spsi + cpsi*sphi) +\ 294 | (cphi*cpsi - cth*sphi*spsi)*(-cphi*spsi - cpsi*cth*sphi) 295 | D[0, 2] = -cphi*epsy*sth*(cphi*cth*spsi + cpsi*sphi) +\ 296 | cth*epsz*spsi*sth + sphi*sth*(cphi*cpsi - cth*sphi*spsi) 297 | D[1, 0] = cpsi*epsz*spsi*sth**2 +\ 298 | epsy*(cphi*cpsi*cth - sphi*spsi)*(cphi*cth*spsi + cpsi*sphi) +\ 299 | (cphi*cpsi - cth*sphi*spsi)*(-cphi*spsi - cpsi*cth*sphi) 300 | D[1, 1] = cpsi**2*epsz*sth**2 + epsy*(cphi*cpsi*cth - sphi*spsi)**2 +\ 301 | (-cphi*spsi - cpsi*cth*sphi)**2 302 | D[1, 2] = -cphi*epsy*sth*(cphi*cpsi*cth - sphi*spsi) +\ 303 | cpsi*cth*epsz*sth + sphi*sth*(-cphi*spsi - cpsi*cth*sphi) 304 | D[2, 0] = -cphi*epsy*sth*(cphi*cth*spsi + cpsi*sphi) + cth*epsz*spsi*sth +\ 305 | sphi*sth*(cphi*cpsi - cth*sphi*spsi) 306 | D[2, 1] = -cphi*epsy*sth*(cphi*cpsi*cth - sphi*spsi) + cpsi*cth*epsz*sth +\ 307 | sphi*sth*(-cphi*spsi - cpsi*cth*sphi) 308 | D[2, 2] = cphi**2*epsy*sth**2 + cth**2*epsz + sphi**2*sth**2 309 | 310 | stencil = np.zeros((3, 3, 3)) 311 | 312 | if type == 'FD': 313 | # from _symbolic_product_helper 314 | # dx*(D11*dx + D21*dy + D31*dz) + 315 | # dy*(D12*dx + D22*dy + D32*dz) + 316 | # dz*(D13*dx + D23*dy + D33*dz) 317 | # 318 | # D00*dxx + 319 | # (D10+D01)*dxy + 320 | # (D20+D02)*dxz + 321 | # D11*dyy + 322 | # (D21+D12)*dyz + 323 | # D22*dzz 324 | 325 | i, j, k = (1, 1, 1) 326 | 327 | # dxx 328 | stencil[[i-1, i, i+1], j, k] += np.array([-1, 2, -1]) * D[0, 0] 329 | 330 | # dyy 331 | stencil[i, [j-1, j, j+1], k] += np.array([-1, 2, -1]) * D[1, 1] 332 | 333 | # dzz 334 | stencil[i, j, [k-1, k, k+1]] += np.array([-1, 2, -1]) * D[2, 2] 335 | 336 | L = np.array([-1, -1, 1, 1]) 337 | M = np.array([-1, 1, -1, 1]) 338 | # dxy 339 | stencil[i + L, j + M, k] \ 340 | += 0.25 * np.array([1, -1, -1, 1]) * (D[1, 0] + D[0, 1]) 341 | 342 | # dxz 343 | stencil[i + L, j, k + M] \ 344 | += 0.25 * np.array([1, -1, -1, 1]) * (D[2, 0] + D[0, 2]) 345 | 346 | # dyz 347 | stencil[i, j + L, k + M] \ 348 | += 0.25 * np.array([1, -1, -1, 1]) * (D[2, 1] + D[1, 2]) 349 | 350 | return stencil 351 | 352 | if type == 'FE': 353 | raise NotImplementedError('FE not implemented yet') 354 | -------------------------------------------------------------------------------- /algebraic-multigrid/helper.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as pl 2 | import numpy as np 3 | from matplotlib.collections import LineCollection 4 | 5 | def draw_graph(xy,edges,edgecolor='b'): 6 | lcol = xy[edges] 7 | lc = LineCollection(xy[edges]) 8 | lc.set_linewidth(0.1) 9 | lc.set_color(edgecolor) 10 | pl.gca().add_collection(lc) 11 | pl.xlim(xy[:,0].min(), xy[:,0].max()) 12 | pl.ylim(xy[:,1].min(), xy[:,1].max()) 13 | 14 | def draw_points(xy,parts=None, markersize=10): 15 | if parts is None: 16 | pl.plot(xy[:,0], xy[:,1], 'ro', markersize=markersize, clip_on=False) 17 | else: 18 | parttypes = np.unique(parts) 19 | colors = pl.cm.jet(parttypes/float(parttypes.max())) 20 | for p in parttypes: 21 | I = np.where(parts == p)[0] 22 | pl.plot(xy[I,0], xy[I,1], 'o', markersize=markersize, markerfacecolor=colors[p,:3],clip_on=False) 23 | -------------------------------------------------------------------------------- /algebraic-multigrid/square.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/copper-multigrid-conference/2019-tutorials/36a2cd6c25db8db11aa00f95a7e774f10d185e6b/algebraic-multigrid/square.mat -------------------------------------------------------------------------------- /algebraic-multigrid/stencil.py: -------------------------------------------------------------------------------- 1 | """Construct sparse matrix from a local stencil""" 2 | from __future__ import print_function 3 | 4 | __docformat__ = "restructuredtext en" 5 | 6 | import numpy as np 7 | import scipy.sparse as sparse 8 | 9 | __all__ = ['stencil_grid'] 10 | 11 | 12 | def stencil_grid(S, grid, dtype=None, format=None): 13 | """Construct a sparse matrix form a local matrix stencil 14 | 15 | Parameters 16 | ---------- 17 | S : ndarray 18 | matrix stencil stored in N-d array 19 | grid : tuple 20 | tuple containing the N grid dimensions 21 | dtype : 22 | data type of the result 23 | format : string 24 | sparse matrix format to return, e.g. "csr", "coo", etc. 25 | 26 | Returns 27 | ------- 28 | A : sparse matrix 29 | Sparse matrix which represents the operator given by applying 30 | stencil S at each vertex of a regular grid with given dimensions. 31 | 32 | Notes 33 | ----- 34 | The grid vertices are enumerated as arange(prod(grid)).reshape(grid). 35 | This implies that the last grid dimension cycles fastest, while the 36 | first dimension cycles slowest. For example, if grid=(2,3) then the 37 | grid vertices are ordered as (0,0), (0,1), (0,2), (1,0), (1,1), (1,2). 38 | 39 | This coincides with the ordering used by the NumPy functions 40 | ndenumerate() and mgrid(). 41 | 42 | Examples 43 | -------- 44 | >>> from pyamg.gallery import stencil_grid 45 | >>> stencil = [-1,2,-1] # 1D Poisson stencil 46 | >>> grid = (5,) # 1D grid with 5 vertices 47 | >>> A = stencil_grid(stencil, grid, dtype=float, format='csr') 48 | >>> A.todense() 49 | matrix([[ 2., -1., 0., 0., 0.], 50 | [-1., 2., -1., 0., 0.], 51 | [ 0., -1., 2., -1., 0.], 52 | [ 0., 0., -1., 2., -1.], 53 | [ 0., 0., 0., -1., 2.]]) 54 | 55 | >>> stencil = [[0,-1,0],[-1,4,-1],[0,-1,0]] # 2D Poisson stencil 56 | >>> grid = (3,3) # 2D grid with shape 3x3 57 | >>> A = stencil_grid(stencil, grid, dtype=float, format='csr') 58 | >>> A.todense() 59 | matrix([[ 4., -1., 0., -1., 0., 0., 0., 0., 0.], 60 | [-1., 4., -1., 0., -1., 0., 0., 0., 0.], 61 | [ 0., -1., 4., 0., 0., -1., 0., 0., 0.], 62 | [-1., 0., 0., 4., -1., 0., -1., 0., 0.], 63 | [ 0., -1., 0., -1., 4., -1., 0., -1., 0.], 64 | [ 0., 0., -1., 0., -1., 4., 0., 0., -1.], 65 | [ 0., 0., 0., -1., 0., 0., 4., -1., 0.], 66 | [ 0., 0., 0., 0., -1., 0., -1., 4., -1.], 67 | [ 0., 0., 0., 0., 0., -1., 0., -1., 4.]]) 68 | 69 | """ 70 | 71 | S = np.asarray(S, dtype=dtype) 72 | grid = tuple(grid) 73 | 74 | if not (np.asarray(S.shape) % 2 == 1).all(): 75 | raise ValueError('all stencil dimensions must be odd') 76 | 77 | if len(grid) != np.ndim(S): 78 | raise ValueError('stencil dimension must equal number of grid\ 79 | dimensions') 80 | 81 | if min(grid) < 1: 82 | raise ValueError('grid dimensions must be positive') 83 | 84 | N_v = np.prod(grid) # number of vertices in the mesh 85 | N_s = (S != 0).sum() # number of nonzero stencil entries 86 | 87 | # diagonal offsets 88 | diags = np.zeros(N_s, dtype=int) 89 | 90 | # compute index offset of each dof within the stencil 91 | strides = np.cumprod([1] + list(reversed(grid)))[:-1] 92 | indices = tuple(i.copy() for i in S.nonzero()) 93 | for i, s in zip(indices, S.shape): 94 | i -= s // 2 95 | # i = (i - s) // 2 96 | # i = i // 2 97 | # i = i - (s // 2) 98 | for stride, coords in zip(strides, reversed(indices)): 99 | diags += stride * coords 100 | 101 | data = S[S != 0].repeat(N_v).reshape(N_s, N_v) 102 | 103 | indices = np.vstack(indices).T 104 | 105 | # zero boundary connections 106 | for index, diag in zip(indices, data): 107 | diag = diag.reshape(grid) 108 | for n, i in enumerate(index): 109 | if i > 0: 110 | s = [slice(None)] * len(grid) 111 | s[n] = slice(0, i) 112 | diag[s] = 0 113 | elif i < 0: 114 | s = [slice(None)]*len(grid) 115 | s[n] = slice(i, None) 116 | diag[s] = 0 117 | 118 | # remove diagonals that lie outside matrix 119 | mask = abs(diags) < N_v 120 | if not mask.all(): 121 | diags = diags[mask] 122 | data = data[mask] 123 | 124 | # sum duplicate diagonals 125 | if len(np.unique(diags)) != len(diags): 126 | new_diags = np.unique(diags) 127 | new_data = np.zeros((len(new_diags), data.shape[1]), 128 | dtype=data.dtype) 129 | 130 | for dia, dat in zip(diags, data): 131 | n = np.searchsorted(new_diags, dia) 132 | new_data[n, :] += dat 133 | 134 | diags = new_diags 135 | data = new_data 136 | 137 | return sparse.dia_matrix((data, diags), 138 | shape=(N_v, N_v)).asformat(format) 139 | 140 | 141 | if __name__ == '__main__': 142 | D = 2 143 | 144 | if D == 1: 145 | # 1D Laplacian 146 | S = np.array([-1, 2, -1]) 147 | grid = (4,) 148 | 149 | if D == 2: 150 | # 2D Laplacian 151 | S = np.array([[0, -1, 0], 152 | [-1, 4, -1], 153 | [0, -1, 0]]) 154 | # S = array([[-1, -1, -1], 155 | # [-1, 8, -1], 156 | # [-1, -1, -1]]) 157 | grid = (2, 1) 158 | 159 | if D == 3: 160 | S = np.array([[[0, 0, 0], 161 | [0, -1, 0], 162 | [0, 0, 0]], 163 | [[0, -1, 0], 164 | [-1, 6, -1], 165 | [0, -1, 0]], 166 | [[0, 0, 0], 167 | [0, -1, 0], 168 | [0, 0, 0]]]) 169 | grid = (3, 4, 5) 170 | 171 | A = stencil_grid(S, grid) 172 | 173 | print(A.todense()) 174 | -------------------------------------------------------------------------------- /clearoutput.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/zsh 2 | 3 | for f in *.ipynb; 4 | jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace $f 5 | -------------------------------------------------------------------------------- /list-notebooks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/zsh 2 | 3 | for f in multigrid/*.ipynb; 4 | echo '- ['$f:t']''( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/'$f')' 5 | 6 | for f in algebraic-multigrid/*.ipynb; 7 | echo '- ['$f:t']''( https://colab.research.google.com/github/copper-multigrid-conference/2019-tutorials/blob/master/'$f')' 8 | -------------------------------------------------------------------------------- /multigrid/1-relaxation-and-modes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy as sp\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "%matplotlib inline" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "n = 64\n", 24 | "A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 25 | "b = np.zeros((n,))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "I = sparse.eye(n, format='csr')\n", 35 | "Dinv = 0.5 * I\n", 36 | "D = 2 * I\n", 37 | "E = -sparse.tril(A, -1)" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "rnorm = []\n", 47 | "\n", 48 | "test = 'random'\n", 49 | "if test == 'random':\n", 50 | " u = np.random.rand(n)\n", 51 | "elif test == 'smooth':\n", 52 | " n = A.shape[0]\n", 53 | " u = np.sin(np.pi * np.arange(1, n+1)/ (n+1))\n", 54 | " \n", 55 | "uinit = u.copy()\n", 56 | "\n", 57 | "for i in range(100):\n", 58 | " u[:] = u - Dinv * A * u\n", 59 | " #u[:] = u - sla.(D-E, A*u)\n", 60 | " rnorm.append(np.linalg.norm(A * u))" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "plt.figure(figsize=(8,8))\n", 70 | "plt.plot(uinit, 'b-', lw=3, clip_on=False)\n", 71 | "plt.plot(u, 'r-', lw=3, clip_on=False)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "# Try specific $\\omega$-Jacobi" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "omega = 2.0 / 3.0\n", 88 | "rnorm = []\n", 89 | "\n", 90 | "test = 'random'\n", 91 | "if test == 'random':\n", 92 | " u = np.random.rand(n)\n", 93 | "elif test == 'smooth':\n", 94 | " n = A.shape[0]\n", 95 | " u = np.sin(np.pi * np.arange(1, n+1)/ (n+1))\n", 96 | " \n", 97 | "uinit = u.copy()\n", 98 | "\n", 99 | "for i in range(100):\n", 100 | " u[:] = u - omega * Dinv * A * u\n", 101 | " #u[:] = u - sla.(D-E, A*u)\n", 102 | " rnorm.append(np.linalg.norm(A * u))" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "plt.figure(figsize=(10,10))\n", 112 | "plt.plot(uinit, 'b-', lw=3, clip_on=False)\n", 113 | "plt.plot(u, 'r-', lw=3, clip_on=False)" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "# Try different \"modes\"" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "omega = 2.0 / 3.0\n", 130 | "\n", 131 | "rnorm1 = []\n", 132 | "rnorm3 = []\n", 133 | "rnorm6 = []\n", 134 | "\n", 135 | "u1 = np.sin(np.pi * np.arange(1, n+1)/ (n+1))\n", 136 | "u3 = np.sin(3 * np.pi * np.arange(1, n+1)/ (n+1))\n", 137 | "u6 = np.sin(6 * np.pi * np.arange(1, n+1)/ (n+1))\n", 138 | " \n", 139 | "for i in range(100):\n", 140 | " u = u1\n", 141 | " u[:] = u - omega * Dinv * A * u\n", 142 | " rnorm1.append(np.linalg.norm(A * u))\n", 143 | " \n", 144 | " u = u3\n", 145 | " u[:] = u - omega * Dinv * A * u\n", 146 | " rnorm3.append(np.linalg.norm(A * u))\n", 147 | " \n", 148 | " u = u6\n", 149 | " u[:] = u - omega * Dinv * A * u\n", 150 | " rnorm6.append(np.linalg.norm(A * u))" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [ 159 | "u1 = np.sin(np.pi * np.arange(1, n+1)/ (n+1))\n", 160 | "u3 = np.sin(3 * np.pi * np.arange(1, n+1)/ (n+1))\n", 161 | "u56 = np.sin(56 * np.pi * np.arange(1, n+1)/ (n+1))\n", 162 | "plt.figure(figsize=(6,6))\n", 163 | "plt.plot(u1, '-o', lw=3)\n", 164 | "plt.plot(u3, '-o',lw=3)\n", 165 | "plt.plot(u56, '-o', lw=3)" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "plt.figure(figsize=(6,6))\n", 175 | "for rnorm, label in zip([rnorm1, rnorm3, rnorm6], ['k=1', 'k=3', 'k=6']):\n", 176 | " rnorm = np.array(rnorm) / rnorm[0]\n", 177 | " plt.plot(rnorm, lw=3, label=label)\n", 178 | "plt.xlabel('iterations')\n", 179 | "plt.ylabel('residual')\n", 180 | "plt.legend()" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "# Test many \"modes\"" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": null, 193 | "metadata": {}, 194 | "outputs": [], 195 | "source": [ 196 | "omega = 3.0 / 3.0\n", 197 | "\n", 198 | "rnorm = []\n", 199 | "u1 = np.sin(np.pi * np.arange(1, n+1)/ (n+1))\n", 200 | "u6 = np.sin(2 * np.pi * np.arange(1, n+1)/ (n+1))\n", 201 | "u32 = np.sin(3 * np.pi * np.arange(1, n+1)/ (n+1))\n", 202 | "u = (u1 + u6 + u32) / 3.0\n", 203 | "\n", 204 | "for i in range(100):\n", 205 | " u[:] = u - omega * Dinv * A * u\n", 206 | " rnorm.append(np.linalg.norm(A * u))" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "plt.plot((u1 + u6 + u32) / 3.0)" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "plt.figure(figsize=(6,6))\n", 225 | "rnorm = np.array(rnorm) / rnorm[0]\n", 226 | "plt.plot(rnorm, lw=3)" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "metadata": {}, 233 | "outputs": [], 234 | "source": [ 235 | "K = np.arange(1,101)\n", 236 | "lmbda = 1 - (4.0/3.0) * np.sin(np.pi * K/ (2*100))**2\n", 237 | "\n", 238 | "fig = plt.figure(figsize=(6,6))\n", 239 | "ax = fig.add_subplot(111)\n", 240 | "ax.plot(lmbda,'-k',label='residual',linewidth=4,clip_on=False)\n", 241 | "\n", 242 | "plt.axis('auto')\n", 243 | "plt.ylabel(r'$|\\lambda^{\\omega J}_k|$')\n", 244 | "plt.xlabel(r'wave number, $k$')\n", 245 | "ax.spines[\"right\"].set_visible(False)\n", 246 | "ax.spines[\"top\"].set_visible(False)\n", 247 | "ax.get_xaxis().tick_bottom() # remove unneeded ticks \n", 248 | "ax.get_yaxis().tick_left()" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": {}, 255 | "outputs": [], 256 | "source": [] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": null, 261 | "metadata": {}, 262 | "outputs": [], 263 | "source": [] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": null, 268 | "metadata": {}, 269 | "outputs": [], 270 | "source": [] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": null, 275 | "metadata": {}, 276 | "outputs": [], 277 | "source": [] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": null, 282 | "metadata": {}, 283 | "outputs": [], 284 | "source": [] 285 | } 286 | ], 287 | "metadata": { 288 | "kernelspec": { 289 | "display_name": "Python 3", 290 | "language": "python", 291 | "name": "python3" 292 | }, 293 | "language_info": { 294 | "codemirror_mode": { 295 | "name": "ipython", 296 | "version": 3 297 | }, 298 | "file_extension": ".py", 299 | "mimetype": "text/x-python", 300 | "name": "python", 301 | "nbconvert_exporter": "python", 302 | "pygments_lexer": "ipython3", 303 | "version": "3.7.2" 304 | } 305 | }, 306 | "nbformat": 4, 307 | "nbformat_minor": 1 308 | } 309 | -------------------------------------------------------------------------------- /multigrid/2-MGtwolevel.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import scipy as sp\n", 10 | "import numpy as np\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import seaborn as sns\n", 15 | "sns.set_context(\"talk\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n", 16 | "%matplotlib inline\n", 17 | "\n", 18 | "import stencil" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "# Define the $h$-norm\n", 26 | "\n", 27 | "$$\n", 28 | "\\|r\\|_{h} = h \\|r\\|_{2}\n", 29 | "$$\n", 30 | "for any vector $r$" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": null, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "def hnorm(r):\n", 40 | " \"\"\"define ||r||_h = h ||r||_2\"\"\"\n", 41 | " n = len(r)\n", 42 | " h = 1.0 / (n+1)\n", 43 | " hrnorm = h * np.linalg.norm(r)\n", 44 | " return hrnorm" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "# Multigrid Operators\n", 52 | "\n", 53 | "Form relaxation, interpolation (based on 1D), and the matrix operator $A$." 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "def relax(A, u, f, nu):\n", 63 | " n = A.shape[0]\n", 64 | " unew = u.copy()\n", 65 | " DE = sparse.tril(A, 0).tocsc()\n", 66 | " \n", 67 | " for i in range(nu):\n", 68 | " unew += sla.spsolve(DE, f - A * unew, permc_spec='NATURAL')\n", 69 | "\n", 70 | " return unew\n", 71 | "\n", 72 | "def interpolation1d(nc, nf):\n", 73 | " d = np.repeat([[1, 2, 1]], nc, axis=0).T\n", 74 | " I = np.zeros((3,nc),dtype=int)\n", 75 | " for i in range(nc):\n", 76 | " I[:,i] = [2*i, 2*i+1, 2*i+2]\n", 77 | " J = np.repeat([np.arange(nc)], 3, axis=0)\n", 78 | " P = sparse.coo_matrix(\n", 79 | " (d.ravel(), (I.ravel(), J.ravel()))\n", 80 | " ).tocsr()\n", 81 | " return 0.5 * P\n", 82 | "\n", 83 | "def create_operator(n, sten):\n", 84 | " \"\"\"\n", 85 | " Create a 2D operator from a stencil.\n", 86 | " \"\"\"\n", 87 | " A = stencil.stencil_grid(sten, (n, n), format='csr')\n", 88 | " return A" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "# Two-level Method\n", 96 | "\n", 97 | "An example two-level method:\n", 98 | "\n", 99 | "- pre-semooth\n", 100 | "- restrict\n", 101 | "- coarse solve\n", 102 | "- interpolate\n", 103 | "- post-smooth" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "def twolevel(A, P, A1, u0, f0, nu):\n", 113 | " u0 = relax(A, u0, f0, nu) # pre-smooth\n", 114 | " f1 = P.T * (f0 - A * u0) # restrict\n", 115 | "\n", 116 | " u1 = sla.spsolve(A1, f1) # coarse solve\n", 117 | "\n", 118 | " u0 = u0 + P * u1 # interpolate\n", 119 | " u0 = relax(A, u0, f0, nu) # post-smooth\n", 120 | " return u0" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "# Test Problem\n", 128 | "\n", 129 | "This runs a problem with $f=0$." 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "# Problem setup\n", 139 | "k = 9\n", 140 | "n = 2**k - 1\n", 141 | "nc = 2**(k-1) - 1\n", 142 | "sten = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]])\n", 143 | "A = (n+1)**2 * create_operator(n, sten)\n", 144 | "f = np.zeros(n*n)\n", 145 | "u = np.random.rand(n*n)\n", 146 | "\n", 147 | "# Multigrid Setup\n", 148 | "P1d = interpolation1d(nc, n)\n", 149 | "P = sparse.kron(P1d, P1d).tocsr()\n", 150 | "A1 = P.T * A * P\n", 151 | "\n", 152 | "# Multigrid cycling\n", 153 | "res = [hnorm(f - A * u)]\n", 154 | "for i in range(10):\n", 155 | " u = twolevel(A, P, A1, u, f, 2)\n", 156 | " res.append(hnorm(f - A * u))\n", 157 | "\n", 158 | "# Look at the residuals\n", 159 | "res = np.array(res)\n", 160 | "print(res[1:] / res[:-1])" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "res" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": {}, 176 | "outputs": [], 177 | "source": [ 178 | "plt.semilogy(res, label='residual')\n", 179 | "plt.legend()" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [] 188 | } 189 | ], 190 | "metadata": { 191 | "kernelspec": { 192 | "display_name": "Python 3", 193 | "language": "python", 194 | "name": "python3" 195 | }, 196 | "language_info": { 197 | "codemirror_mode": { 198 | "name": "ipython", 199 | "version": 3 200 | }, 201 | "file_extension": ".py", 202 | "mimetype": "text/x-python", 203 | "name": "python", 204 | "nbconvert_exporter": "python", 205 | "pygments_lexer": "ipython3", 206 | "version": "3.7.2" 207 | } 208 | }, 209 | "nbformat": 4, 210 | "nbformat_minor": 1 211 | } 212 | -------------------------------------------------------------------------------- /multigrid/3-Multigrid-V-cycle.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import scipy as sp\n", 10 | "import numpy as np\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import seaborn as sns\n", 15 | "sns.set_context(\"talk\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n", 16 | "%matplotlib inline" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "# Define Operators\n", 24 | "\n", 25 | "Here we define the Poisson operator, the residual calculation (without building the matrix), relaxation, the restriction operation, and interpolation." 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "def hnorm(r):\n", 35 | " \"\"\"define ||r||_h = h ||r||_2\"\"\"\n", 36 | " n = len(r)\n", 37 | " h = 1.0 / (n+1)\n", 38 | " hrnorm = h * np.linalg.norm(r)\n", 39 | " return hrnorm\n", 40 | "\n", 41 | "def poissonop(n):\n", 42 | " \"\"\"\n", 43 | " Poisson operator h^{-2} * [-1 2 1]\n", 44 | " \"\"\"\n", 45 | " A = (n+1)**2 * sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 46 | " return A\n", 47 | "\n", 48 | "def residual(u, f):\n", 49 | " \"\"\"\n", 50 | " f - A u\n", 51 | " \"\"\"\n", 52 | " n = len(f)\n", 53 | " r = np.zeros(len(u))\n", 54 | " r[1:-1] = f[1:-1] - ((n+1)**2) * (2 * u[1:-1] - u[2:] - u[:-2])\n", 55 | " r[0] = f[0] - ((n+1)**2) * (2 * u[0] - u[1])\n", 56 | " r[-1] = f[-1] - ((n+1)**2) * (2 * u[-1] - u[-2])\n", 57 | " return r\n", 58 | "\n", 59 | "def relax(u, f, nu):\n", 60 | " \"\"\"\n", 61 | " Weighted Jacobi\n", 62 | " \"\"\"\n", 63 | " n = len(u)\n", 64 | " Dinv = 1.0 / (2.0 * ((n+1)**2))\n", 65 | " omega = 2.0 / 3.0\n", 66 | " unew = u.copy()\n", 67 | " \n", 68 | " for steps in range(nu):\n", 69 | " unew = unew + omega * Dinv * residual(unew, f)\n", 70 | " \n", 71 | " return unew\n", 72 | "\n", 73 | "def interpolate(uc):\n", 74 | " \"\"\"interpolate u of size 2**(k-1)-1 to 2**(k)-1\"\"\"\n", 75 | " uf = np.zeros((2*len(uc) + 1,))\n", 76 | " uf[:-1:2] = 0.5 * uc\n", 77 | " uf[1::2] = uc\n", 78 | " uf[2::2] += 0.5 * uc\n", 79 | " return uf\n", 80 | "\n", 81 | "def restrict(uf):\n", 82 | " \"\"\"restrict u of size 2**(k)-1 to 2**(k-1)-1\"\"\"\n", 83 | " uc = 0.25 * uf[:-1:2] + 0.5 * uf[1::2] + 0.25 * uf[2::2]\n", 84 | " return uc" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "# Define Cycles\n", 92 | "\n", 93 | "Here we define a number of different cycles. A V-cycle down to $2^{kmin}-1$ (vcycle) and a two level cycle (vcycle2)." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "def vcycle(kmax, kmin, u, f, nu):\n", 103 | " ulist = [None for k in range(kmax+1)]\n", 104 | " flist = [None for k in range(kmax+1)]\n", 105 | "\n", 106 | " #print('grid: ', end=' ')\n", 107 | " \n", 108 | " # down cycle\n", 109 | " for k in range(kmax, kmin, -1):\n", 110 | " #print(k, end=' ')\n", 111 | " u = relax(u, f, nu)\n", 112 | " ulist[k] = u\n", 113 | " flist[k] = f\n", 114 | " \n", 115 | " f = restrict(residual(u, f))\n", 116 | " u = np.zeros(f.shape)\n", 117 | " ulist[k-1] = u\n", 118 | " flist[k-1] = f\n", 119 | "\n", 120 | " # coarsest grid\n", 121 | " #print(kmin, end=' ')\n", 122 | " Ac = poissonop(2**kmin - 1)\n", 123 | " flist[kmin] = f\n", 124 | " ulist[kmin] = sla.spsolve(Ac, f)\n", 125 | "\n", 126 | " # up cycle\n", 127 | " for k in range(kmin+1, kmax+1, 1):\n", 128 | " #print(k, end=' ')\n", 129 | " u = ulist[k]\n", 130 | " f = flist[k]\n", 131 | " uc = ulist[k-1]\n", 132 | " u += interpolate(uc)\n", 133 | " u = relax(u, f, nu)\n", 134 | " #print('.')\n", 135 | " return u\n", 136 | "\n", 137 | "def vcycle3(u, f, nu):\n", 138 | " f0 = f.copy()\n", 139 | " u0 = u.copy()\n", 140 | " \n", 141 | " u0 = relax(u0, f0, nu)\n", 142 | " f1 = restrict(residual(u0, f0))\n", 143 | " \n", 144 | " u1 = relax(np.zeros(len(f1)), f1, nu)\n", 145 | " f2 = restrict(residual(u1, f1))\n", 146 | " \n", 147 | " A2 = poissonop(len(f2))\n", 148 | " u2 = sla.spsolve(A2, f2)\n", 149 | " \n", 150 | " u1 += interpolate(u2)\n", 151 | " u1 = relax(u1, f1, 1)\n", 152 | " \n", 153 | " u0 += interpolate(u1)\n", 154 | " u0 = relax(u0, f0, 1)\n", 155 | " return u0\n", 156 | "\n", 157 | "def vcycle2(u0, f0, nu):\n", 158 | " u0 = relax(u0, f0, nu)\n", 159 | " f1 = restrict(residual(u0, f0))\n", 160 | " u1 = sla.spsolve(poissonop(len(f1)), f1) \n", 161 | " ui = interpolate(u1)\n", 162 | " u0 = u0 + ui\n", 163 | " u0 = relax(u0, f0, nu)\n", 164 | " return u0" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "kmax = 5\n", 174 | "kmin = 2\n", 175 | "\n", 176 | "# set up fine problem\n", 177 | "n = 2**kmax - 1\n", 178 | "\n", 179 | "xx = np.linspace(0, 1, n+2)[1:-1]\n", 180 | "\n", 181 | "f = 2 - 12 * xx**2\n", 182 | "\n", 183 | "ustar = xx**4 - xx**2\n", 184 | "A = poissonop(len(f))\n", 185 | "udstar = sla.spsolve(A, f)\n", 186 | "print(\"discretization error: \", hnorm(ustar - udstar))\n", 187 | "u = np.random.rand(len(f))\n", 188 | "\n", 189 | "# set up smoothing sweeps\n", 190 | "nu = 2\n", 191 | "\n", 192 | "res = []\n", 193 | "err = []\n", 194 | "\n", 195 | "res.append(hnorm(residual(u, f)))\n", 196 | "for i in range(15):\n", 197 | " u = vcycle(kmax, kmin, u, f, nu)\n", 198 | " res.append(hnorm(residual(u, f)))\n", 199 | " err.append(hnorm(u - ustar))" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "# Plot the solution and approximate solution" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "plt.plot(u, 'g-', lw=3)\n", 216 | "plt.plot(ustar, 'bo', lw=3)" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [ 225 | "plt.plot(u-ustar, 'g-', lw=3)" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "# Output the residual convergence factors" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "metadata": {}, 239 | "outputs": [], 240 | "source": [ 241 | "res = np.array(res)\n", 242 | "print(res[1:] / res[:-1])" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "# Output the residual and error" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [ 258 | "for r, e in zip(res, err):\n", 259 | " print('residual: %5.5e error: %5.5e' % (r, e))" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "kmax = 7\n", 269 | "kmin = 2\n", 270 | "\n", 271 | "# set up fine problem\n", 272 | "n = 2**kmax - 1\n", 273 | "\n", 274 | "xx = np.linspace(0, 1, n+2)[1:-1]\n", 275 | "\n", 276 | "f = 2 - 12 * xx**2\n", 277 | "\n", 278 | "ustar = xx**4 - xx**2\n", 279 | "A = poissonop(len(f))\n", 280 | "udstar = sla.spsolve(A, f)\n", 281 | "de = hnorm(ustar - udstar)\n", 282 | "print(\"discretization error: \", de)\n", 283 | "u = np.random.rand(len(f))\n", 284 | "\n", 285 | "# set up smoothing sweeps\n", 286 | "nu = 2\n", 287 | "\n", 288 | "res = []\n", 289 | "err = []\n", 290 | "aerr = []\n", 291 | "res.append(hnorm(residual(u, f)))\n", 292 | "for i in range(15):\n", 293 | " u = vcycle(kmax, kmin, u, f, nu)\n", 294 | " res.append(hnorm(residual(u, f)))\n", 295 | " err.append(hnorm(ustar - u))\n", 296 | " aerr.append(hnorm(udstar - u))\n", 297 | " print(\"res = %10.4e, total err = %10.4e alg err = %10.4e\" % (res[-1], err[-1], aerr[-1]))" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": null, 303 | "metadata": {}, 304 | "outputs": [], 305 | "source": [ 306 | "plt.semilogy(res, lw=3, label='residual')\n", 307 | "plt.semilogy(err, lw=3, label='error')\n", 308 | "plt.semilogy([de for i in range(len(err))],'--', lw=3, label='discretization error')\n", 309 | "plt.legend(fontsize=15)" 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": null, 315 | "metadata": {}, 316 | "outputs": [], 317 | "source": [ 318 | "err" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": null, 324 | "metadata": {}, 325 | "outputs": [], 326 | "source": [] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": null, 331 | "metadata": {}, 332 | "outputs": [], 333 | "source": [] 334 | } 335 | ], 336 | "metadata": { 337 | "kernelspec": { 338 | "display_name": "Python 3", 339 | "language": "python", 340 | "name": "python3" 341 | }, 342 | "language_info": { 343 | "codemirror_mode": { 344 | "name": "ipython", 345 | "version": 3 346 | }, 347 | "file_extension": ".py", 348 | "mimetype": "text/x-python", 349 | "name": "python", 350 | "nbconvert_exporter": "python", 351 | "pygments_lexer": "ipython3", 352 | "version": "3.7.2" 353 | } 354 | }, 355 | "nbformat": 4, 356 | "nbformat_minor": 1 357 | } 358 | -------------------------------------------------------------------------------- /multigrid/Coarse-Modes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy as sp\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import seaborn as sns\n", 15 | "sns.set_context(\"talk\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n", 16 | "%matplotlib inline" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "m = 4\n", 26 | "k = 4\n", 27 | "\n", 28 | "nf = 2**m + 1\n", 29 | "xf = np.linspace(0,1,nf)\n", 30 | "yf = sp.sin(k * np.pi * xf)\n", 31 | "\n", 32 | "nc = 2**(m-1) + 1\n", 33 | "xc = np.linspace(0,1,nc)\n", 34 | "yc = sp.sin(k * np.pi * xc)" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "plt.plot(xc, 0*xc, 'gs', ms=15, label='coarse')\n", 44 | "plt.plot(xf, 0*xf, 'bo', label='fine')\n", 45 | "plt.legend()" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "print(nc)\n", 55 | "print(nf)\n", 56 | "plt.plot(xc, yc, 'g-s', lw=3, clip_on=False, label='coarse')\n", 57 | "plt.plot(xf, yf, 'b-o', lw=3, clip_on=False, label='fine')\n", 58 | "plt.legend()" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [] 67 | } 68 | ], 69 | "metadata": { 70 | "kernelspec": { 71 | "display_name": "Python 3", 72 | "language": "python", 73 | "name": "python3" 74 | }, 75 | "language_info": { 76 | "codemirror_mode": { 77 | "name": "ipython", 78 | "version": 3 79 | }, 80 | "file_extension": ".py", 81 | "mimetype": "text/x-python", 82 | "name": "python", 83 | "nbconvert_exporter": "python", 84 | "pygments_lexer": "ipython3", 85 | "version": "3.7.2" 86 | } 87 | }, 88 | "nbformat": 4, 89 | "nbformat_minor": 1 90 | } 91 | -------------------------------------------------------------------------------- /multigrid/Error-vs-Solution.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy as sp\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "\n", 15 | "import seaborn as sns\n", 16 | "sns.set_context(\"talk\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n", 17 | "%matplotlib inline" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "n = 2**7 - 1\n", 27 | "A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 28 | "b = np.zeros((n,))" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "I = sparse.eye(n, format='csr')\n", 38 | "Dinv = 0.5 * I\n", 39 | "D = 2 * I\n", 40 | "E = -sparse.tril(A, -1)" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "xstar = np.random.rand(n)\n", 50 | "b = A * xstar\n", 51 | "\n", 52 | "plt.plot(xstar, label=r'$x^*$')\n", 53 | "plt.legend()" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "omega = 2.0/3.0\n", 63 | "rnorm = []\n", 64 | "monitor = True\n", 65 | "x = np.random.rand(n)\n", 66 | "for i in range(200):\n", 67 | " x[:] = x + omega * Dinv * (b - A * x)\n", 68 | " #x[:] = x - sla.spsolve(D-E, A*x)\n", 69 | " rnorm.append(np.linalg.norm(b - A * x))" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "rnorm[-1]" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "plt.plot(x, label=r'$U$')\n", 88 | "plt.legend()" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "plt.plot(x-xstar, label=r'$u - u^*$')\n", 98 | "plt.legend()" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "e = x - xstar\n", 108 | "plt.plot(e[1::4], label=r'$u - u^*$')\n", 109 | "plt.legend()" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [] 118 | } 119 | ], 120 | "metadata": { 121 | "kernelspec": { 122 | "display_name": "Python 3", 123 | "language": "python", 124 | "name": "python3" 125 | }, 126 | "language_info": { 127 | "codemirror_mode": { 128 | "name": "ipython", 129 | "version": 3 130 | }, 131 | "file_extension": ".py", 132 | "mimetype": "text/x-python", 133 | "name": "python", 134 | "nbconvert_exporter": "python", 135 | "pygments_lexer": "ipython3", 136 | "version": "3.7.2" 137 | } 138 | }, 139 | "nbformat": 4, 140 | "nbformat_minor": 2 141 | } 142 | -------------------------------------------------------------------------------- /multigrid/Interpolation-1D.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy as sp\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import seaborn as sns\n", 15 | "sns.set_context(\"talk\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n", 16 | "%matplotlib inline" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "n = 64\n", 26 | "h = 1.0 / n\n", 27 | "A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 28 | "b = np.zeros((n,))" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "def interpolate(vc):\n", 38 | " \"\"\"interpolate v of size 2**(m-1)-1 to 2**(m)-1\"\"\"\n", 39 | " nc = len(vc)\n", 40 | " nf = 2**(int(np.log2(nc+1))+1)-1\n", 41 | " \n", 42 | " vf = np.zeros((nf,))\n", 43 | " \n", 44 | " # 1\n", 45 | " vf[2:-1:2] = 0.5 * vc[:-1] + 0.5 * vc[1:]\n", 46 | " \n", 47 | " # 2\n", 48 | " vf[1::2] = vc\n", 49 | " \n", 50 | " # 3\n", 51 | " vf[0] = 0.5 * vc[0]\n", 52 | " \n", 53 | " # 4\n", 54 | " vf[-1] = 0.5 * vc[-1]\n", 55 | " \n", 56 | " print(nc)\n", 57 | " print(nf)\n", 58 | " return vf" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "m = 4\n", 68 | "nf = 2**m - 1\n", 69 | "nc = 2**(m-1) - 1\n", 70 | "\n", 71 | "xc = np.linspace(0,1,nc+2)[1:-1]\n", 72 | "vc = np.sin(np.pi * xc)\n", 73 | "#vc = np.random.rand(nc)\n", 74 | "plt.plot(xc, vc, 'bo-', clip_on=False, ms=15)\n", 75 | "\n", 76 | "xf = np.linspace(0,1,nf+2)[1:-1]\n", 77 | "vf = interpolate(vc)\n", 78 | "plt.plot(xf, vf, 'go-', clip_on=False)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [] 87 | } 88 | ], 89 | "metadata": { 90 | "kernelspec": { 91 | "display_name": "Python 3", 92 | "language": "python", 93 | "name": "python3" 94 | }, 95 | "language_info": { 96 | "codemirror_mode": { 97 | "name": "ipython", 98 | "version": 3 99 | }, 100 | "file_extension": ".py", 101 | "mimetype": "text/x-python", 102 | "name": "python", 103 | "nbconvert_exporter": "python", 104 | "pygments_lexer": "ipython3", 105 | "version": "3.7.2" 106 | } 107 | }, 108 | "nbformat": 4, 109 | "nbformat_minor": 1 110 | } 111 | -------------------------------------------------------------------------------- /multigrid/Multigrid-Two-Grid-Cycle.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import scipy as sp\n", 10 | "import numpy as np\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "\n", 15 | "import seaborn as sns\n", 16 | "sns.set_context(\"talk\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n", 17 | "%matplotlib inline" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "def hnorm(r):\n", 27 | " \"\"\"define ||r||_h = h ||r||_2\"\"\"\n", 28 | " n = len(r)\n", 29 | " h = 1.0 / (n+1)\n", 30 | " hrnorm = h * np.linalg.norm(r)\n", 31 | " return hrnorm" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "def poissonop(n):\n", 41 | " A = (n+1)**2 * sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 42 | " return A" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "def relax(A, u, f, nu):\n", 52 | " n = A.shape[0]\n", 53 | " Dinv = 1.0 / (2.0 * (n+1)**2)\n", 54 | " omega = 2.0 / 3.0\n", 55 | " for steps in range(nu):\n", 56 | " u += omega * Dinv * (f - A * u)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "def interpolate(uc):\n", 66 | " \"\"\"interpolate u of size 2**(k-1)-1 to 2**(k)-1\"\"\"\n", 67 | " nc = len(uc)\n", 68 | " nf = 2**(int(np.log2(nc+1))+1)-1\n", 69 | " uf = np.zeros((nf,))\n", 70 | " I = np.arange(1,nf,2)\n", 71 | " uf[I-1] = 0.5 * uc\n", 72 | " uf[I] = uc\n", 73 | " uf[I+1] += 0.5 * uc\n", 74 | " return uf" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "def restrict(uf):\n", 84 | " \"\"\"restrict u of size 2**(k)-1 to 2**(k-1)-1\"\"\"\n", 85 | " nf = len(uf)\n", 86 | " nc = 2**(int(np.log2(nf+1))-1)-1\n", 87 | " uc = np.zeros((nc,))\n", 88 | " I = np.arange(1,nf,2)\n", 89 | " uc = 0.25 * uf[I-1] + 0.5 * uf[I] + 0.25 * uf[I+1]\n", 90 | " return uc" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "k=4\n", 100 | "nc = 2**(k-1)-1\n", 101 | "nf = 2**(k)-1\n", 102 | "xc = np.linspace(0,1,nc+2)[1:-1]\n", 103 | "xf = np.linspace(0,1,nf+2)[1:-1]\n", 104 | "\n", 105 | "uc = np.sin(xc*np.pi)\n", 106 | "uf = interpolate(uc)\n", 107 | "\n", 108 | "plt.plot(xc, uc, 'b-o', clip_on=False, ms=15)\n", 109 | "plt.plot(xf, uf, 'g--s')\n", 110 | "plt.axis([0,1,0,1])" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "uf = np.sin(xf*np.pi)\n", 120 | "uc = restrict(uf)\n", 121 | "\n", 122 | "plt.plot(xc, uc, 'b-o', ms=15)\n", 123 | "plt.plot(xf, uf, 'g--s')" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "# Set up the problem" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "k = 12\n", 140 | "n = 2**k - 1\n", 141 | "print(\"size = %d\" % n)\n", 142 | "u = np.random.rand(n)\n", 143 | "xx = np.linspace(0,1,n+2)[1:-1]\n", 144 | "\n", 145 | "#f = np.random.rand(n)#np.pi**2 * np.sin(np.pi*xx)\n", 146 | "A = poissonop(n)\n", 147 | "f = A * np.random.rand(n)\n", 148 | "ustar = sla.spsolve(A, f) \n", 149 | "\n", 150 | "plt.plot(xx, ustar)\n", 151 | "plt.xlabel(r'$x$')\n", 152 | "plt.ylabel(r'$u(x)$')" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "# Try one cycle:\n", 160 | "\n", 161 | "* smooth\n", 162 | "* restrict\n", 163 | "* solve `Ac`\n", 164 | "* interpolate\n", 165 | "* correct" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "print('starting residual: ', hnorm(f - A * u))\n", 175 | "relax(A, u, f, 1)\n", 176 | "rc = restrict(f - A * u)\n", 177 | "Ac = poissonop(len(rc))\n", 178 | "ec = sparse.linalg.spsolve(Ac, rc)\n", 179 | "ef = interpolate(ec)\n", 180 | "u = u + ef\n", 181 | "relax(A, u, f, 1)\n", 182 | "print(' ending residual: ', hnorm(f - A * u))\n", 183 | "plt.plot(xx, u-ustar)\n", 184 | "plt.xlabel(r'$x$')\n", 185 | "plt.ylabel(r'$u(x)$')" 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "metadata": {}, 191 | "source": [ 192 | "# Multiple Cycles" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [ 201 | "u = np.random.rand(n)\n", 202 | "\n", 203 | "res = [hnorm(f - A * u)]\n", 204 | "\n", 205 | "print(\"res[0] = %g\"%res[-1])\n", 206 | "for cycle in range(10):\n", 207 | " relax(A, u, f, 10)\n", 208 | " rc = restrict(f - A * u)\n", 209 | " ec = sparse.linalg.spsolve(poissonop(len(rc)), rc)\n", 210 | " ef = interpolate(ec)\n", 211 | " u = u + ef\n", 212 | " relax(A, u, f, 10)\n", 213 | " res.append(hnorm(f - A * u))\n", 214 | " print(\"res[%d] = %g\"%(cycle+1,res[-1]))" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": null, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "res = np.array(res)\n", 224 | "res[1:]/res[:-1]" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": null, 230 | "metadata": {}, 231 | "outputs": [], 232 | "source": [ 233 | "plt.semilogy(res)" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": null, 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [] 242 | } 243 | ], 244 | "metadata": { 245 | "kernelspec": { 246 | "display_name": "Python 3", 247 | "language": "python", 248 | "name": "python3" 249 | }, 250 | "language_info": { 251 | "codemirror_mode": { 252 | "name": "ipython", 253 | "version": 3 254 | }, 255 | "file_extension": ".py", 256 | "mimetype": "text/x-python", 257 | "name": "python", 258 | "nbconvert_exporter": "python", 259 | "pygments_lexer": "ipython3", 260 | "version": "3.7.2" 261 | } 262 | }, 263 | "nbformat": 4, 264 | "nbformat_minor": 1 265 | } 266 | -------------------------------------------------------------------------------- /multigrid/Multigrid-in-2D.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import scipy as sp\n", 10 | "import numpy as np\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "%matplotlib inline\n", 15 | "\n", 16 | "import stencil\n", 17 | "import diffusion\n", 18 | "\n", 19 | "import warnings\n", 20 | "warnings.simplefilter(action='ignore', category=FutureWarning)" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "def hnorm(r):\n", 30 | " \"\"\"define ||r||_h = h ||r||_2\"\"\"\n", 31 | " n = len(r)\n", 32 | " h = 1.0 / (n+1)\n", 33 | " hrnorm = h * np.linalg.norm(r)\n", 34 | " return hrnorm" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "def relaxJ(A, u, f, nu):\n", 44 | " n = A.shape[0]\n", 45 | " unew = u.copy()\n", 46 | " Dinv = sparse.spdiags(1./A.diagonal(), [0], n, n)\n", 47 | " omega = 2.0 / 3.0\n", 48 | " \n", 49 | " for i in range(nu):\n", 50 | " unew += omega * Dinv * (f - A * unew)\n", 51 | "\n", 52 | " return unew\n", 53 | "\n", 54 | "def relax(A, u, f, nu):\n", 55 | " n = A.shape[0]\n", 56 | " unew = u.copy()\n", 57 | " DE = sparse.tril(A, 0).tocsc()\n", 58 | " \n", 59 | " for i in range(nu):\n", 60 | " unew += sla.spsolve(DE, f - A * unew, permc_spec='NATURAL')\n", 61 | "\n", 62 | " return unew\n", 63 | "\n", 64 | "def interpolation1d(nc, nf):\n", 65 | " d = np.repeat([[1, 2, 1]], nc, axis=0).T\n", 66 | " I = np.zeros((3,nc), dtype=int)\n", 67 | " for i in range(nc):\n", 68 | " I[:,i] = [2*i, 2*i+1, 2*i+2]\n", 69 | " J = np.repeat([np.arange(nc)], 3, axis=0)\n", 70 | " P = sparse.coo_matrix(\n", 71 | " (d.ravel(), (I.ravel(), J.ravel()))\n", 72 | " ).tocsr()\n", 73 | " return 0.5 * P\n", 74 | "\n", 75 | "def create_operator(n, sten):\n", 76 | " \"\"\"\n", 77 | " Create a 2D operator from a stencil.\n", 78 | " \"\"\"\n", 79 | " A = stencil.stencil_grid(sten, (n, n), format='csr')\n", 80 | " return A" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "n = 2**5 - 1\n", 90 | "nc = 2**4 - 1\n", 91 | "sten = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]])\n", 92 | "A = create_operator(n, sten)\n", 93 | "P1d = interpolation1d(nc, n)\n", 94 | "P = sparse.kron(P1d, P1d).tocsr()\n", 95 | "P.toarray()" 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "#### Take a function and interpolate" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "x1d = np.linspace(0, 1, nc+2)[1:-1]\n", 112 | "X, Y = np.meshgrid(x1d, x1d)\n", 113 | "uc = np.sin(np.pi * X) * np.sin(np.pi * Y)\n", 114 | "plt.figure()\n", 115 | "plt.pcolormesh(uc)\n", 116 | "plt.colorbar()\n", 117 | "\n", 118 | "u = P * uc.ravel()\n", 119 | "u = u.reshape((n,n))\n", 120 | "plt.figure()\n", 121 | "plt.pcolormesh(u)\n", 122 | "plt.colorbar()" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "### Take a function and restrict" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "x1d = np.linspace(0, 1, n+2)[1:-1]\n", 139 | "X, Y = np.meshgrid(x1d, x1d)\n", 140 | "u = np.sin(np.pi * X) * np.sin(np.pi * Y)\n", 141 | "plt.figure()\n", 142 | "plt.pcolormesh(u)\n", 143 | "plt.colorbar()\n", 144 | "\n", 145 | "uc = 0.25 * P.T * u.ravel()\n", 146 | "uc = uc.reshape((nc,nc))\n", 147 | "plt.figure()\n", 148 | "plt.pcolormesh(uc)\n", 149 | "plt.colorbar()" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "### Take a random function and smooth" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "u = np.random.rand(n*n)\n", 166 | "f = np.zeros((n*n,))\n", 167 | "plt.figure()\n", 168 | "plt.pcolormesh(u.reshape(n,n))\n", 169 | "plt.colorbar()\n", 170 | "u = relax(A, u, f, 2)\n", 171 | "plt.figure()\n", 172 | "plt.pcolormesh(u.reshape(n,n))\n", 173 | "plt.colorbar()" 174 | ] 175 | }, 176 | { 177 | "cell_type": "markdown", 178 | "metadata": {}, 179 | "source": [ 180 | "### Run a two grid cycle two times" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "k = 5\n", 190 | "n = 2**k - 1\n", 191 | "nc = 2**(k-1) - 1\n", 192 | "sten = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]])\n", 193 | "A = create_operator(n, sten)\n", 194 | "P1d = interpolation1d(nc, n)\n", 195 | "P = sparse.kron(P1d, P1d).tocsr()\n", 196 | "PT = 0.25 * P.T\n", 197 | "u0 = np.random.rand(n*n)\n", 198 | "f0 = np.random.rand(n*n)\n", 199 | "print('r0=', hnorm(f0 - A * u0))\n", 200 | "\n", 201 | "u0 = relax(A, u0, f0, 1)\n", 202 | "f1 = PT * (f0 - A * u0)\n", 203 | "u1 = sla.spsolve(PT * A * P, f1)\n", 204 | "u0 = u0 + P * u1\n", 205 | "u0 = relax(A, u0, f0, 1)\n", 206 | "print('r1=',hnorm(f0 - A * u0))\n", 207 | "\n", 208 | "u0 = relax(A, u0, f0, 1)\n", 209 | "f1 = PT * (f0 - A * u0)\n", 210 | "u1 = sla.spsolve(PT * A * P, f1)\n", 211 | "u0 = u0 + P * u1\n", 212 | "u0 = relax(A, u0, f0, 1)\n", 213 | "print('r2=',hnorm(f0 - A * u0))" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "def twogrid(A, P, A1, u0, f0, nu):\n", 223 | " u0 = relax(A, u0, f0, nu)\n", 224 | " f1 = P.T * (f0 - A * u0)\n", 225 | "\n", 226 | " u1 = sla.spsolve(A1, f1)\n", 227 | "\n", 228 | " u0 = u0 + P * u1\n", 229 | " u0 = relax(A, u0, f0, nu)\n", 230 | " return u0" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "### Run a two grid cycle" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "k = 8\n", 247 | "n = 2**k - 1\n", 248 | "nc = 2**(k-1) - 1\n", 249 | "sten = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]])\n", 250 | "A = (n+1)**2 * create_operator(n, sten)\n", 251 | "P1d = interpolation1d(nc, n)\n", 252 | "P = sparse.kron(P1d, P1d).tocsr()\n", 253 | "u = np.random.rand(n*n)\n", 254 | "f = np.zeros((n*n,))\n", 255 | "\n", 256 | "A1 = P.T * A * P\n", 257 | "res = [hnorm(f - A * u)]\n", 258 | "for i in range(10):\n", 259 | " u = twogrid(A, P, A1, u, f, 1)\n", 260 | " res.append(hnorm(f - A * u))\n", 261 | " \n", 262 | "res = np.array(res)\n", 263 | "print(res[1:] / res[:-1])" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | "k = 6\n", 273 | "n = 2**k - 1\n", 274 | "nc = 2**(k-1) - 1\n", 275 | "sten = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]])\n", 276 | "A = (n+1)**2 * create_operator(n, sten)\n", 277 | "P1d = interpolation1d(nc, n)\n", 278 | "P = sparse.kron(P1d, P1d).tocsr()\n", 279 | "\n", 280 | "x1d = np.linspace(0, 1, n+2)[1:-1]\n", 281 | "X, Y = np.meshgrid(x1d, x1d)\n", 282 | "ustar = (X**2 - X**4) * (Y**4 - Y**2)\n", 283 | "f = 2 * ((1-6*X**2) * Y**2 * (1 - Y**2) + (1-6*Y**2) * X**2 * (1-X**2))\n", 284 | "f = f.ravel()\n", 285 | "u = np.random.rand(n*n)\n", 286 | "\n", 287 | "A1 = P.T * A * P\n", 288 | "res = [hnorm(f - A * u)]\n", 289 | "err = [hnorm(ustar.ravel() - u)]\n", 290 | "for i in range(10):\n", 291 | " u = twogrid(A, P, A1, u, f, 2)\n", 292 | " res.append(hnorm(f - A * u))\n", 293 | " err.append(hnorm(ustar.ravel() - u))\n", 294 | "\n", 295 | "res = np.array(res)\n", 296 | "print(res[1:] / res[:-1])" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": null, 302 | "metadata": {}, 303 | "outputs": [], 304 | "source": [ 305 | "print([\"%e\" % e for e in err])" 306 | ] 307 | }, 308 | { 309 | "cell_type": "markdown", 310 | "metadata": {}, 311 | "source": [ 312 | "### Take an *anisotropic* problem\n", 313 | "$$\n", 314 | "-u_{xx} - \\epsilon u_{yy} = f\n", 315 | "$$" 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": null, 321 | "metadata": {}, 322 | "outputs": [], 323 | "source": [ 324 | "k = 8\n", 325 | "n = 2**k - 1\n", 326 | "nc = 2**(k-1) - 1\n", 327 | "sten = diffusion.diffusion_stencil_2d(epsilon=0.001, theta=np.pi/40, type='FD')\n", 328 | "print(sten)\n", 329 | "A = (n+1)**2 * create_operator(n, sten)\n", 330 | "P1d = interpolation1d(nc, n)\n", 331 | "P = sparse.kron(P1d, P1d).tocsr()" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "metadata": {}, 338 | "outputs": [], 339 | "source": [ 340 | "x1d = np.linspace(0, 1, n+2)[1:-1]\n", 341 | "X, Y = np.meshgrid(x1d, x1d)\n", 342 | "ustar = (X**2 - X**4) * (Y**4 - Y**2)\n", 343 | "f = 2 * ((1-6*X**2) * Y**2 * (1 - Y**2)) + ((1-6*Y**2) * X**2 * (1-X**2))\n", 344 | "f = f.ravel()\n", 345 | "u = np.random.rand(n*n)\n", 346 | "\n", 347 | "A1 = P.T * A * P\n", 348 | "res = [hnorm(f - A * u)]\n", 349 | "err = [hnorm(ustar.ravel() - u)]\n", 350 | "for i in range(10):\n", 351 | " u = twogrid(A, P, A1, u, f, 2)\n", 352 | " res.append(hnorm(f - A * u))\n", 353 | " err.append(hnorm(ustar.ravel() - u))\n", 354 | "\n", 355 | "res = np.array(res)\n", 356 | "print(res[1:] / res[:-1])" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": null, 362 | "metadata": {}, 363 | "outputs": [], 364 | "source": [ 365 | "0.87**20" 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "execution_count": null, 371 | "metadata": {}, 372 | "outputs": [], 373 | "source": [ 374 | "u = np.random.rand(n*n)\n", 375 | "f = np.zeros((n*n,))\n", 376 | "plt.pcolormesh(u.reshape(n,n))\n", 377 | "plt.colorbar()\n", 378 | "u = relax(A, u, f, 4)\n", 379 | "plt.figure()\n", 380 | "plt.pcolormesh(u.reshape(n,n))\n", 381 | "plt.colorbar()" 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": null, 387 | "metadata": {}, 388 | "outputs": [], 389 | "source": [] 390 | } 391 | ], 392 | "metadata": { 393 | "kernelspec": { 394 | "display_name": "Python 3", 395 | "language": "python", 396 | "name": "python3" 397 | }, 398 | "language_info": { 399 | "codemirror_mode": { 400 | "name": "ipython", 401 | "version": 3 402 | }, 403 | "file_extension": ".py", 404 | "mimetype": "text/x-python", 405 | "name": "python", 406 | "nbconvert_exporter": "python", 407 | "pygments_lexer": "ipython3", 408 | "version": "3.7.2" 409 | } 410 | }, 411 | "nbformat": 4, 412 | "nbformat_minor": 1 413 | } 414 | -------------------------------------------------------------------------------- /multigrid/Multigrid-with-Variable-Spacing.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import scipy as sp\n", 10 | "import numpy as np\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "%matplotlib inline" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "def hnorm(r):\n", 24 | " \"\"\"define ||r||_h = h ||r||_2\"\"\"\n", 25 | " n = len(r)\n", 26 | " h = 1.0 / (n+1)\n", 27 | " hrnorm = h * np.linalg.norm(r)\n", 28 | " return hrnorm\n", 29 | "\n", 30 | "def poissonop(n):\n", 31 | " \"\"\"\n", 32 | " Poisson operator h^{-2} * [-1 2 1]\n", 33 | " \"\"\"\n", 34 | " A = (n+1)**2 * sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 35 | " return A\n", 36 | "\n", 37 | "def acoeff(x):\n", 38 | " k = 50\n", 39 | " rho = 0.25\n", 40 | " return 1 + rho*np.sin(k*np.pi*x)\n", 41 | "\n", 42 | "def poissonop2(n):\n", 43 | " xx = np.linspace(0,1, n+2)\n", 44 | " xxhalf = (xx[:-1] + xx[1:]) / 2\n", 45 | " ahalf = acoeff(xxhalf)\n", 46 | " diagonals = [-ahalf[1:-1], ahalf[:-1]+ahalf[1:], -ahalf[1:-1]]\n", 47 | " A = (n+1)**2 * sparse.diags(diagonals, [-1, 0, 1], shape=(n,n), format='csr')\n", 48 | " return A\n", 49 | "\n", 50 | "def relax(A, u, f, nu):\n", 51 | " n = A.shape[0]\n", 52 | " unew = u.copy()\n", 53 | " DE = sparse.tril(A, 0).tocsc()\n", 54 | " \n", 55 | " for i in range(nu):\n", 56 | " unew += sla.spsolve(DE, f - A * unew, permc_spec='NATURAL')\n", 57 | "\n", 58 | " return unew\n", 59 | "\n", 60 | "def interpolation1d(nc, nf):\n", 61 | " \"\"\"\n", 62 | " Linear interpolation\n", 63 | " \"\"\"\n", 64 | " d = np.repeat([[1, 2, 1]], nc, axis=0).T\n", 65 | " I = np.zeros((3,nc), dtype=int)\n", 66 | " for i in range(nc):\n", 67 | " I[:,i] = [2*i, 2*i+1, 2*i+2]\n", 68 | " J = np.repeat([np.arange(nc)], 3, axis=0)\n", 69 | " P = sparse.coo_matrix(\n", 70 | " (d.ravel(), (I.ravel(), J.ravel()))\n", 71 | " ).tocsr()\n", 72 | " return 0.5 * P\n" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "def vcycle(Alist, Plist, u, f, nu):\n", 82 | " nlevels = len(Alist)\n", 83 | " ulist = [None for k in range(nlevels)]\n", 84 | " flist = [None for k in range(nlevels)]\n", 85 | "\n", 86 | " # down cycle\n", 87 | " for k in range(nlevels-1):\n", 88 | " Ak = Alist[k]\n", 89 | " Pk = Plist[k]\n", 90 | " u = relax(Ak, u, f, nu)\n", 91 | " ulist[k] = u\n", 92 | " flist[k] = f\n", 93 | " \n", 94 | " f = Pk.T * (f - Ak * u)\n", 95 | " u = np.zeros(f.shape)\n", 96 | " ulist[k+1] = u\n", 97 | " flist[k+1] = f\n", 98 | "\n", 99 | " # coarsest grid\n", 100 | " Ac = Alist[-1]\n", 101 | " flist[-1] = f\n", 102 | " ulist[-1] = sla.spsolve(Ac, f)\n", 103 | "\n", 104 | " # up cycle\n", 105 | " for k in range(nlevels-2, -1, -1):\n", 106 | " Ak = Alist[k]\n", 107 | " Pk = Plist[k]\n", 108 | " u = ulist[k]\n", 109 | " f = flist[k]\n", 110 | " uc = ulist[k+1]\n", 111 | " u += Pk * uc\n", 112 | " u = relax(Ak, u, f, nu)\n", 113 | " return u" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "kmax = 7\n", 123 | "kmin = 2\n", 124 | "\n", 125 | "# set up fine problem\n", 126 | "n = 2**kmax - 1\n", 127 | "\n", 128 | "xx = np.linspace(0, 1, n+2)[1:-1]\n", 129 | "\n", 130 | "f = 2 - 12 * xx**2\n", 131 | "\n", 132 | "ustar = xx**4 - xx**2\n", 133 | "A = poissonop2(len(f))\n", 134 | "udstar = sla.spsolve(A, f)\n", 135 | "\n", 136 | "Alist = [A]\n", 137 | "Plist = []\n", 138 | "nf = n\n", 139 | "while nf>3:\n", 140 | " nc = int((nf + 1) / 2 - 1)\n", 141 | " P = interpolation1d(nc, nf)\n", 142 | " Ac = P.T * Alist[-1] * P\n", 143 | " Plist.append(P)\n", 144 | " Alist.append(Ac)\n", 145 | " nf = nc" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "# set up smoothing sweeps\n", 155 | "nu = 2\n", 156 | "\n", 157 | "u = np.random.rand(len(f))\n", 158 | "res = [hnorm(f - A * u)]\n", 159 | "err = []\n", 160 | "\n", 161 | "for i in range(12):\n", 162 | " u = vcycle(Alist, Plist, u, f, nu)\n", 163 | " res.append(hnorm(f - A * u))\n", 164 | " err.append(hnorm(u - ustar))\n", 165 | "\n", 166 | "res = np.array(res)\n", 167 | "resfactor = res[1:] / res[:-1]\n", 168 | "print(resfactor)" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": null, 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "plt.plot(udstar, 'o')" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": null, 183 | "metadata": {}, 184 | "outputs": [], 185 | "source": [] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "metadata": {}, 191 | "outputs": [], 192 | "source": [] 193 | } 194 | ], 195 | "metadata": { 196 | "kernelspec": { 197 | "display_name": "Python 3", 198 | "language": "python", 199 | "name": "python3" 200 | }, 201 | "language_info": { 202 | "codemirror_mode": { 203 | "name": "ipython", 204 | "version": 3 205 | }, 206 | "file_extension": ".py", 207 | "mimetype": "text/x-python", 208 | "name": "python", 209 | "nbconvert_exporter": "python", 210 | "pygments_lexer": "ipython3", 211 | "version": "3.7.2" 212 | } 213 | }, 214 | "nbformat": 4, 215 | "nbformat_minor": 1 216 | } 217 | -------------------------------------------------------------------------------- /multigrid/Nested-Iteration.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy as sp\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import seaborn as sns\n", 15 | "sns.set_context(\"talk\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n", 16 | "%matplotlib inline" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "n = 2**7 - 1\n", 26 | "h = 1.0 / n\n", 27 | "A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 28 | "b = np.zeros((n,))\n", 29 | "I = sparse.eye(n, format='csr')\n", 30 | "Dinv = 0.5 * I\n", 31 | "D = 2 * I" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "def interpolate(vc):\n", 41 | " \"\"\"interpolate v of size 2**(m-1)-1 to 2**(m)-1\"\"\"\n", 42 | " nc = len(vc)\n", 43 | " nf = 2**(int(np.log2(nc+1))+1)-1\n", 44 | " vf = np.zeros((nf,))\n", 45 | " \n", 46 | " vf[2:-1:2] = 0.5 * vc[:-1] + 0.5 * vc[1:]\n", 47 | " vf[1::2] = vc\n", 48 | " vf[0] = 0.5 * vc[0]\n", 49 | " vf[-1] = 0.5 * vc[-1]\n", 50 | " return vf" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "omega = 2.0/3.0\n", 60 | "rnorm = []\n", 61 | "x = np.random.rand(n)\n", 62 | "for i in range(20000):\n", 63 | " x[:] = x - omega * Dinv * A * x\n", 64 | " #x[:] = x - sla.spsolve(D-E, A*x)\n", 65 | " rnorm.append(np.linalg.norm(A * x))" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "plt.semilogy(rnorm)" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "minm = 2\n", 84 | "x = np.random.rand(2**minm - 1)\n", 85 | "\n", 86 | "for m in range(minm,7):\n", 87 | " nc = 2**m - 1\n", 88 | " print(\"size: {}\".format(nc))\n", 89 | " \n", 90 | " # set up the problem\n", 91 | " hc = 1.0 / nc\n", 92 | " Ac = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(nc,nc), format='csr')\n", 93 | " bc = np.zeros((nc,))\n", 94 | " Ic = sparse.eye(nc, format='csr')\n", 95 | " Dcinv = 0.5 * Ic\n", 96 | " Dc = 2 * Ic\n", 97 | "\n", 98 | " x[:] = x - omega * Dcinv * Ac * x\n", 99 | " \n", 100 | " x = interpolate(x)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "rnorm_orig = rnorm.copy()\n", 110 | "omega = 2.0/3.0\n", 111 | "rnorm = []\n", 112 | "for i in range(20000):\n", 113 | " x[:] = x - omega * Dinv * A * x\n", 114 | " #x[:] = x - sla.spsolve(D-E, A*x)\n", 115 | " rnorm.append(np.linalg.norm(A * x))" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "plt.semilogy(rnorm_orig, label='relaxation')\n", 125 | "plt.semilogy(rnorm, label='nested relaxation')\n", 126 | "plt.legend()" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [] 135 | } 136 | ], 137 | "metadata": { 138 | "kernelspec": { 139 | "display_name": "Python 3", 140 | "language": "python", 141 | "name": "python3" 142 | }, 143 | "language_info": { 144 | "codemirror_mode": { 145 | "name": "ipython", 146 | "version": 3 147 | }, 148 | "file_extension": ".py", 149 | "mimetype": "text/x-python", 150 | "name": "python", 151 | "nbconvert_exporter": "python", 152 | "pygments_lexer": "ipython3", 153 | "version": "3.7.2" 154 | } 155 | }, 156 | "nbformat": 4, 157 | "nbformat_minor": 2 158 | } 159 | -------------------------------------------------------------------------------- /multigrid/Relaxation-and-omega.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy as sp\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "%matplotlib inline" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "n = 64\n", 24 | "h = 1.0 / n\n", 25 | "A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 26 | "b = np.zeros((n,))" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "plt.figure(figsize=(6,6))\n", 36 | "\n", 37 | "K = np.arange(1,n)\n", 38 | "\n", 39 | "omega = 1.0 /2\n", 40 | "lmbda = 1 - (omega / 2) * 4.0 * np.sin(np.pi * K / (2*(n+1)))**2\n", 41 | "plt.plot(lmbda,'-k',label='residual',linewidth=4, clip_on=False)\n", 42 | "plt.text(n+2, lmbda[-1], r'$\\omega=1/2$', fontsize=24)\n", 43 | "\n", 44 | "omega = 1.0 / 3.0\n", 45 | "lmbda = 1 - (omega / 2) * 4.0 * np.sin(np.pi * K / (2*(n+1)))**2\n", 46 | "plt.plot(lmbda,'-k',label='residual',linewidth=4, clip_on=False)\n", 47 | "plt.text(n+2, lmbda[-1], r'$\\omega=1/3$', fontsize=24)\n", 48 | "\n", 49 | "omega = 2.0 / 3.0\n", 50 | "lmbda = 1 - (omega / 2) * 4.0 * np.sin(np.pi * K / (2*(n+1)))**2\n", 51 | "plt.plot(lmbda,'-k',label='residual',linewidth=4, clip_on=False)\n", 52 | "plt.text(n+2, lmbda[-1], r'$\\omega=2/3$', fontsize=24)\n", 53 | "\n", 54 | "omega = 1\n", 55 | "lmbda = 1 - (omega / 2) * 4.0 * np.sin(np.pi * K/ (2*(n+1)))**2\n", 56 | "plt.plot(lmbda,'-k',label='residual',linewidth=4, clip_on=False)\n", 57 | "plt.text(n+2, lmbda[-1], r'$\\omega=1$', fontsize=24)\n", 58 | "\n", 59 | "####### fancy plotting vvvvvvvvvv\n", 60 | "ax = plt.gca()\n", 61 | "ax.plot([0, n], [0, 0], '--b')\n", 62 | "plt.axis([0,n,-1,1])\n", 63 | "plt.ylabel(r'$\\lambda^{\\omega_J}_k$', fontsize=32)\n", 64 | "plt.xlabel(r'wave number, $k$', fontsize=32)\n", 65 | "ax.spines[\"right\"].set_visible(False)\n", 66 | "ax.spines[\"top\"].set_visible(False)\n", 67 | "ax.get_xaxis().tick_bottom() # remove unneeded ticks \n", 68 | "ax.get_yaxis().tick_left()" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [] 105 | } 106 | ], 107 | "metadata": { 108 | "kernelspec": { 109 | "display_name": "Python 3", 110 | "language": "python", 111 | "name": "python3" 112 | }, 113 | "language_info": { 114 | "codemirror_mode": { 115 | "name": "ipython", 116 | "version": 3 117 | }, 118 | "file_extension": ".py", 119 | "mimetype": "text/x-python", 120 | "name": "python", 121 | "nbconvert_exporter": "python", 122 | "pygments_lexer": "ipython3", 123 | "version": "3.7.2" 124 | } 125 | }, 126 | "nbformat": 4, 127 | "nbformat_minor": 1 128 | } 129 | -------------------------------------------------------------------------------- /multigrid/Relaxation-on-smooth-and-non-smooth-error.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import matplotlib.pyplot as plt\n", 10 | "import numpy as np\n", 11 | "%matplotlib inline" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "e = np.array([0, 0.1, 1.0, 0.1, 0])\n", 21 | "plt.plot(e, '-bo', ms=10, lw=4, clip_on=False)\n", 22 | "plt.gca().set_ylim([0,1])\n", 23 | "plt.axis('off')" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "eold = e.copy()\n", 33 | "e[2] = 0.5 * (e[1] + e[3])\n", 34 | "plt.plot(eold, '-bo', ms=10, lw=4, clip_on=False)\n", 35 | "plt.plot(e, '-rx', ms=10, lw=4, clip_on=False)\n", 36 | "plt.gca().set_ylim([0,1])\n", 37 | "plt.axis('off')" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "e = np.array([0, 0.1, 0.15, 0.1, 0])\n", 47 | "plt.plot(e, '-bo', ms=10, lw=4, clip_on=False)\n", 48 | "plt.gca().set_ylim([0,1])\n", 49 | "plt.axis('off')" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "eold = e.copy()\n", 59 | "e[2] = 0.5 * (e[1] + e[3])\n", 60 | "plt.plot(eold, '-bo', ms=10, lw=4, clip_on=False)\n", 61 | "plt.plot(e, '-rx', ms=10, lw=4, clip_on=False)\n", 62 | "plt.gca().set_ylim([0,1])\n", 63 | "plt.axis('off')" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "e = np.array([-1, 1, -1, 1, -1, 1])\n", 73 | "plt.plot(e, '-bo', ms=10, lw=4, clip_on=False)\n", 74 | "plt.gca().set_ylim([-1,1])\n", 75 | "plt.axis('off')" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "eold = e.copy()\n", 85 | "\n", 86 | "e[0] = 0.5 * (eold[-1] + eold[1])\n", 87 | "e[1] = 0.5 * (eold[0] + eold[2])\n", 88 | "e[2] = 0.5 * (eold[1] + eold[3])\n", 89 | "e[3] = 0.5 * (eold[2] + eold[4])\n", 90 | "e[4] = 0.5 * (eold[3] + eold[5])\n", 91 | "e[5] = 0.5 * (eold[4] + eold[0])\n", 92 | "\n", 93 | "plt.plot(eold, '-bo', ms=10, lw=4, clip_on=False)\n", 94 | "plt.plot(e, '-rx', ms=10, lw=4, clip_on=False)\n", 95 | "plt.gca().set_ylim([-1,1])\n", 96 | "plt.axis('off')" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [] 133 | } 134 | ], 135 | "metadata": { 136 | "kernelspec": { 137 | "display_name": "Python 3", 138 | "language": "python", 139 | "name": "python3" 140 | }, 141 | "language_info": { 142 | "codemirror_mode": { 143 | "name": "ipython", 144 | "version": 3 145 | }, 146 | "file_extension": ".py", 147 | "mimetype": "text/x-python", 148 | "name": "python", 149 | "nbconvert_exporter": "python", 150 | "pygments_lexer": "ipython3", 151 | "version": "3.7.2" 152 | } 153 | }, 154 | "nbformat": 4, 155 | "nbformat_minor": 1 156 | } 157 | -------------------------------------------------------------------------------- /multigrid/Relaxation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy as sp\n", 11 | "import scipy.sparse as sparse\n", 12 | "import scipy.sparse.linalg as sla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "%matplotlib inline" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "n = 2**7 - 1\n", 24 | "A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n,n), format='csr')\n", 25 | "b = np.zeros((n,))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "I = sparse.eye(n, format='csr')\n", 35 | "Dinv = 0.5 * I\n", 36 | "D = 2 * I\n", 37 | "E = -sparse.tril(A, -1)" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "omega = 2.0/3.0\n", 47 | "rnorm = []\n", 48 | "monitor = True\n", 49 | "x = np.random.rand(n)\n", 50 | "for i in range(20):\n", 51 | " x[:] = x - omega * Dinv * A * x\n", 52 | " #x[:] = x - sla.spsolve(D-E, A*x)\n", 53 | " rnorm.append(np.linalg.norm(A * x))\n", 54 | "\n", 55 | " if monitor:\n", 56 | " plt.ion()\n", 57 | " plt.figure(1)\n", 58 | " plt.plot(x)\n", 59 | " plt.draw()" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "plt.plot(x)\n", 69 | "plt.axis([0,100,0,1])" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "np.linalg.norm(x - (x - omega * Dinv * A * x))" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "m = 4\n", 88 | "nf = 2**m + 1\n", 89 | "nc = 2**(m-1) + 1\n", 90 | "\n" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [] 99 | } 100 | ], 101 | "metadata": { 102 | "kernelspec": { 103 | "display_name": "Python 3", 104 | "language": "python", 105 | "name": "python3" 106 | }, 107 | "language_info": { 108 | "codemirror_mode": { 109 | "name": "ipython", 110 | "version": 3 111 | }, 112 | "file_extension": ".py", 113 | "mimetype": "text/x-python", 114 | "name": "python", 115 | "nbconvert_exporter": "python", 116 | "pygments_lexer": "ipython3", 117 | "version": "3.7.2" 118 | } 119 | }, 120 | "nbformat": 4, 121 | "nbformat_minor": 1 122 | } 123 | -------------------------------------------------------------------------------- /multigrid/diffusion.py: -------------------------------------------------------------------------------- 1 | """Generate a diffusion stencil 2 | 3 | Supports isotropic diffusion (FE,FD), anisotropic diffusion (FE, FD), and 4 | rotated anisotropic diffusion (FD). 5 | 6 | The stencils include redundancy to maintain readability for simple cases (e.g. 7 | isotropic diffusion). 8 | 9 | """ 10 | from __future__ import print_function 11 | 12 | import numpy as np 13 | 14 | __docformat__ = "restructuredtext en" 15 | 16 | __all__ = ['diffusion_stencil_2d'] 17 | 18 | 19 | def diffusion_stencil_2d(epsilon=1.0, theta=0.0, type='FE'): 20 | """ 21 | Rotated Anisotropic diffusion in 2d of the form: 22 | 23 | -div Q A Q^T grad u 24 | 25 | Q = [cos(theta) -sin(theta)] 26 | [sin(theta) cos(theta)] 27 | 28 | A = [1 0 ] 29 | [0 eps ] 30 | 31 | Parameters 32 | ---------- 33 | epsilon : float, optional 34 | Anisotropic diffusion coefficient: -div A grad u, 35 | where A = [1 0; 0 epsilon]. The default is isotropic, epsilon=1.0 36 | theta : float, optional 37 | Rotation angle `theta` in radians defines -div Q A Q^T grad, 38 | where Q = [cos(`theta`) -sin(`theta`); sin(`theta`) cos(`theta`)]. 39 | type : {'FE','FD'} 40 | Specifies the discretization as Q1 finite element (FE) or 2nd order 41 | finite difference (FD) 42 | The default is `theta` = 0.0 43 | 44 | Returns 45 | ------- 46 | stencil : numpy array 47 | A 3x3 diffusion stencil 48 | 49 | See Also 50 | -------- 51 | stencil_grid, poisson 52 | 53 | Notes 54 | ----- 55 | Not all combinations are supported. 56 | 57 | Examples 58 | -------- 59 | >>> import scipy as sp 60 | >>> from pyamg.gallery.diffusion import diffusion_stencil_2d 61 | >>> sten = diffusion_stencil_2d(epsilon=0.0001,theta=sp.pi/6,type='FD') 62 | >>> print sten 63 | [[-0.2164847 -0.750025 0.2164847] 64 | [-0.250075 2.0002 -0.250075 ] 65 | [ 0.2164847 -0.750025 -0.2164847]] 66 | 67 | """ 68 | eps = float(epsilon) # for brevity 69 | theta = float(theta) 70 | 71 | C = np.cos(theta) 72 | S = np.sin(theta) 73 | CS = C*S 74 | CC = C**2 75 | SS = S**2 76 | 77 | if(type == 'FE'): 78 | """FE approximation to:: 79 | 80 | - (eps c^2 + s^2) u_xx + 81 | -2(eps - 1) c s u_xy + 82 | - ( c^2 + eps s^2) u_yy 83 | 84 | [ -c^2*eps-s^2+3*c*s*(eps-1)-c^2-s^2*eps, 85 | 2*c^2*eps+2*s^2-4*c^2-4*s^2*eps, 86 | -c^2*eps-s^2-3*c*s*(eps-1)-c^2-s^2*eps] 87 | 88 | [-4*c^2*eps-4*s^2+2*c^2+2*s^2*eps, 89 | 8*c^2*eps+8*s^2+8*c^2+8*s^2*eps, 90 | -4*c^2*eps-4*s^2+2*c^2+2*s^2*eps] 91 | 92 | [-c^2*eps-s^2-3*c*s*(eps-1)-c^2-s^2*eps, 93 | 2*c^2*eps+2*s^2-4*c^2-4*s^2*eps, 94 | -c^2*eps-s^2+3*c*s*(eps-1)-c^2-s^2*eps] 95 | 96 | c = cos(theta) 97 | s = sin(theta) 98 | """ 99 | 100 | a = (-1*eps - 1)*CC + (-1*eps - 1)*SS + (3*eps - 3)*CS 101 | b = (2*eps - 4)*CC + (-4*eps + 2)*SS 102 | c = (-1*eps - 1)*CC + (-1*eps - 1)*SS + (-3*eps + 3)*CS 103 | d = (-4*eps + 2)*CC + (2*eps - 4)*SS 104 | e = (8*eps + 8)*CC + (8*eps + 8)*SS 105 | 106 | stencil = np.array([[a, b, c], 107 | [d, e, d], 108 | [c, b, a]]) / 6.0 109 | 110 | elif type == 'FD': 111 | """FD approximation to: 112 | 113 | - (eps c^2 + s^2) u_xx + 114 | -2(eps - 1) c s u_xy + 115 | - ( c^2 + eps s^2) u_yy 116 | 117 | c = cos(theta) 118 | s = sin(theta) 119 | 120 | A = [ 1/2(eps - 1) c s -(c^2 + eps s^2) -1/2(eps - 1) c s ] 121 | [ ] 122 | [ -(eps c^2 + s^2) 2 (eps + 1) -(eps c^2 + s^2) ] 123 | [ ] 124 | [ -1/2(eps - 1) c s -(c^2 + eps s^2) 1/2(eps - 1) c s ] 125 | """ 126 | 127 | a = 0.5*(eps - 1)*CS 128 | b = -(eps*SS + CC) 129 | c = -a 130 | d = -(eps*CC + SS) 131 | e = 2.0*(eps + 1) 132 | 133 | stencil = np.array([[a, b, c], 134 | [d, e, d], 135 | [c, b, a]]) 136 | 137 | return stencil 138 | 139 | 140 | def _symbolic_rotation_helper(): 141 | """ 142 | Simple SymPy script to generate the 3D rotation matrix and products for 143 | diffusion_stencil_3d. 144 | 145 | """ 146 | 147 | from sympy import symbols, Matrix 148 | 149 | cpsi, spsi = symbols('cpsi, spsi') 150 | cth, sth = symbols('cth, sth') 151 | cphi, sphi = symbols('cphi, sphi') 152 | Rpsi = Matrix([[cpsi, spsi, 0], [-spsi, cpsi, 0], [0, 0, 1]]) 153 | Rth = Matrix([[1, 0, 0], [0, cth, sth], [0, -sth, cth]]) 154 | Rphi = Matrix([[cphi, sphi, 0], [-sphi, cphi, 0], [0, 0, 1]]) 155 | 156 | Q = Rpsi * Rth * Rphi 157 | 158 | epsy, epsz = symbols('epsy, epsz') 159 | A = Matrix([[1, 0, 0], [0, epsy, 0], [0, 0, epsz]]) 160 | 161 | D = Q * A * Q.T 162 | 163 | for i in range(3): 164 | for j in range(3): 165 | print('D[%d, %d] = %s' % (i, j, D[i, j])) 166 | 167 | 168 | def _symbolic_product_helper(): 169 | """ 170 | Simple SymPy script to generate the 3D products for diffusion_stencil_3d. 171 | 172 | """ 173 | 174 | from sympy import symbols, Matrix 175 | 176 | D11, D12, D13, D21, D22, D23, D31, D32, D33 =\ 177 | symbols('D11, D12, D13, D21, D22, D23, D31, D32, D33') 178 | 179 | D = Matrix([[D11, D12, D13], [D21, D22, D23], [D31, D32, D33]]) 180 | grad = Matrix([['dx', 'dy', 'dz']]).T 181 | div = grad.T 182 | 183 | a = div * D * grad 184 | 185 | print(a[0]) 186 | 187 | 188 | def diffusion_stencil_3d(epsilony=1.0, epsilonz=1.0, theta=0.0, phi=0.0, 189 | psi=0.0, type='FD'): 190 | """ 191 | Rotated Anisotropic diffusion in 3d of the form: 192 | 193 | -div Q A Q^T grad u 194 | 195 | Q = Rpsi Rtheta Rphi 196 | 197 | Rpsi = [ c s 0 ] 198 | [-s c 0 ] 199 | [ 0 0 1 ] 200 | c = cos(psi) 201 | s = sin(psi) 202 | 203 | Rtheta = [ 1 0 0 ] 204 | [ 0 c s ] 205 | [ 0 -s c ] 206 | c = cos(theta) 207 | s = sin(theta) 208 | 209 | Rphi = [ c s 0 ] 210 | [-s c 0 ] 211 | [ 0 0 1 ] 212 | c = cos(phi) 213 | s = sin(phi) 214 | 215 | Here Euler Angles are used: 216 | http://en.wikipedia.org/wiki/Euler_angles 217 | 218 | This results in 219 | 220 | Q = [ cphi*cpsi - cth*sphi*spsi, cpsi*sphi + cphi*cth*spsi, spsi*sth] 221 | [ - cphi*spsi - cpsi*cth*sphi, cphi*cpsi*cth - sphi*spsi, cpsi*sth] 222 | [ sphi*sth, -cphi*sth, cth] 223 | 224 | A = [1 0 ] 225 | [0 epsy ] 226 | [0 0 epsz] 227 | 228 | D = Q A Q^T 229 | 230 | Parameters 231 | ---------- 232 | epsilony : float, optional 233 | Anisotropic diffusion coefficient in the y-direction 234 | where A = [1 0 0; 0 epsilon_y 0; 0 0 epsilon_z]. The default is 235 | isotropic, epsilon=1.0 236 | epsilonz : float, optional 237 | Anisotropic diffusion coefficient in the z-direction 238 | where A = [1 0 0; 0 epsilon_y 0; 0 0 epsilon_z]. The default is 239 | isotropic, epsilon=1.0 240 | theta : float, optional 241 | Euler rotation angle `theta` in radians. The default is 0.0. 242 | phi : float, optional 243 | Euler rotation angle `phi` in radians. The default is 0.0. 244 | psi : float, optional 245 | Euler rotation angle `psi` in radians. The default is 0.0. 246 | type : {'FE','FD'} 247 | Specifies the discretization as Q1 finite element (FE) or 2nd order 248 | finite difference (FD) 249 | 250 | Returns 251 | ------- 252 | stencil : numpy array 253 | A 3x3 diffusion stencil 254 | 255 | See Also 256 | -------- 257 | stencil_grid, poisson, _symbolic_rotation_helper, _symbolic_product_helper 258 | 259 | Notes 260 | ----- 261 | Not all combinations are supported. 262 | 263 | Examples 264 | -------- 265 | >>> import scipy as sp 266 | >>> from pyamg.gallery.diffusion import diffusion_stencil_2d 267 | >>> sten = diffusion_stencil_2d(epsilon=0.0001,theta=sp.pi/6,type='FD') 268 | >>> print sten 269 | [[-0.2164847 -0.750025 0.2164847] 270 | [-0.250075 2.0002 -0.250075 ] 271 | [ 0.2164847 -0.750025 -0.2164847]] 272 | 273 | """ 274 | 275 | epsy = float(epsilony) # for brevity 276 | epsz = float(epsilonz) # for brevity 277 | theta = float(theta) 278 | phi = float(phi) 279 | psi = float(psi) 280 | 281 | D = np.zeros((3, 3)) 282 | cphi = np.cos(phi) 283 | sphi = np.sin(phi) 284 | cth = np.cos(theta) 285 | sth = np.sin(theta) 286 | cpsi = np.cos(psi) 287 | spsi = np.sin(psi) 288 | 289 | # from _symbolic_rotation_helper 290 | D[0, 0] = epsy*(cphi*cth*spsi + cpsi*sphi)**2 + epsz*spsi**2*sth**2 +\ 291 | (cphi*cpsi - cth*sphi*spsi)**2 292 | D[0, 1] = cpsi*epsz*spsi*sth**2 +\ 293 | epsy*(cphi*cpsi*cth - sphi*spsi)*(cphi*cth*spsi + cpsi*sphi) +\ 294 | (cphi*cpsi - cth*sphi*spsi)*(-cphi*spsi - cpsi*cth*sphi) 295 | D[0, 2] = -cphi*epsy*sth*(cphi*cth*spsi + cpsi*sphi) +\ 296 | cth*epsz*spsi*sth + sphi*sth*(cphi*cpsi - cth*sphi*spsi) 297 | D[1, 0] = cpsi*epsz*spsi*sth**2 +\ 298 | epsy*(cphi*cpsi*cth - sphi*spsi)*(cphi*cth*spsi + cpsi*sphi) +\ 299 | (cphi*cpsi - cth*sphi*spsi)*(-cphi*spsi - cpsi*cth*sphi) 300 | D[1, 1] = cpsi**2*epsz*sth**2 + epsy*(cphi*cpsi*cth - sphi*spsi)**2 +\ 301 | (-cphi*spsi - cpsi*cth*sphi)**2 302 | D[1, 2] = -cphi*epsy*sth*(cphi*cpsi*cth - sphi*spsi) +\ 303 | cpsi*cth*epsz*sth + sphi*sth*(-cphi*spsi - cpsi*cth*sphi) 304 | D[2, 0] = -cphi*epsy*sth*(cphi*cth*spsi + cpsi*sphi) + cth*epsz*spsi*sth +\ 305 | sphi*sth*(cphi*cpsi - cth*sphi*spsi) 306 | D[2, 1] = -cphi*epsy*sth*(cphi*cpsi*cth - sphi*spsi) + cpsi*cth*epsz*sth +\ 307 | sphi*sth*(-cphi*spsi - cpsi*cth*sphi) 308 | D[2, 2] = cphi**2*epsy*sth**2 + cth**2*epsz + sphi**2*sth**2 309 | 310 | stencil = np.zeros((3, 3, 3)) 311 | 312 | if type == 'FD': 313 | # from _symbolic_product_helper 314 | # dx*(D11*dx + D21*dy + D31*dz) + 315 | # dy*(D12*dx + D22*dy + D32*dz) + 316 | # dz*(D13*dx + D23*dy + D33*dz) 317 | # 318 | # D00*dxx + 319 | # (D10+D01)*dxy + 320 | # (D20+D02)*dxz + 321 | # D11*dyy + 322 | # (D21+D12)*dyz + 323 | # D22*dzz 324 | 325 | i, j, k = (1, 1, 1) 326 | 327 | # dxx 328 | stencil[[i-1, i, i+1], j, k] += np.array([-1, 2, -1]) * D[0, 0] 329 | 330 | # dyy 331 | stencil[i, [j-1, j, j+1], k] += np.array([-1, 2, -1]) * D[1, 1] 332 | 333 | # dzz 334 | stencil[i, j, [k-1, k, k+1]] += np.array([-1, 2, -1]) * D[2, 2] 335 | 336 | L = np.array([-1, -1, 1, 1]) 337 | M = np.array([-1, 1, -1, 1]) 338 | # dxy 339 | stencil[i + L, j + M, k] \ 340 | += 0.25 * np.array([1, -1, -1, 1]) * (D[1, 0] + D[0, 1]) 341 | 342 | # dxz 343 | stencil[i + L, j, k + M] \ 344 | += 0.25 * np.array([1, -1, -1, 1]) * (D[2, 0] + D[0, 2]) 345 | 346 | # dyz 347 | stencil[i, j + L, k + M] \ 348 | += 0.25 * np.array([1, -1, -1, 1]) * (D[2, 1] + D[1, 2]) 349 | 350 | return stencil 351 | 352 | if type == 'FE': 353 | raise NotImplementedError('FE not implemented yet') 354 | -------------------------------------------------------------------------------- /multigrid/helper.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as pl 2 | import numpy as np 3 | from matplotlib.collections import LineCollection 4 | 5 | def draw_graph(xy,edges,edgecolor='b'): 6 | lcol = xy[edges] 7 | lc = LineCollection(xy[edges]) 8 | lc.set_linewidth(0.1) 9 | lc.set_color(edgecolor) 10 | pl.gca().add_collection(lc) 11 | pl.xlim(xy[:,0].min(), xy[:,0].max()) 12 | pl.ylim(xy[:,1].min(), xy[:,1].max()) 13 | 14 | def draw_points(xy,parts=None, markersize=10): 15 | if parts is None: 16 | pl.plot(xy[:,0], xy[:,1], 'ro', markersize=markersize, clip_on=False) 17 | else: 18 | parttypes = np.unique(parts) 19 | colors = pl.cm.jet(parttypes/float(parttypes.max())) 20 | for p in parttypes: 21 | I = np.where(parts == p)[0] 22 | pl.plot(xy[I,0], xy[I,1], 'o', markersize=markersize, markerfacecolor=colors[p,:3],clip_on=False) 23 | -------------------------------------------------------------------------------- /multigrid/onedprojection.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def onedprojection(A, b, x0=None, tol=1e-15, maxiter=None, 5 | residuals=None, method='SD', errs=None): 6 | """1D projection methods 7 | 8 | 9 | Parameters 10 | ---------- 11 | A : {array, matrix, sparse matrix, LinearOperator} 12 | n x n, linear system to solve 13 | b : {array, matrix} 14 | right hand side, shape is (n,) or (n,1) 15 | x0 : {array, matrix} 16 | initial guess, default is a vector of zeros 17 | tol : float 18 | relative convergence tolerance, i.e. tol is scaled by ||b|| 19 | maxiter : int 20 | maximum number of allowed iterations 21 | residuals : list 22 | residuals has the residual norm history, 23 | including the initial residual, appended to it 24 | errs: list 25 | A-norm of the vector x 26 | method: 'SD' or 'MR' or 'RNSD' 27 | 'SD' is steepest-decent 28 | 'MR' is minimum residual 29 | 'RNSD' is residual norm steepest-decent 30 | """ 31 | n = len(b) 32 | if maxiter is None: 33 | maxiter = int(1.3*len(b)) + 2 34 | normb = np.linalg.norm(b) 35 | if normb != 0: 36 | tol = tol*normb 37 | if x0 is None: 38 | x = np.ones((n,)) 39 | else: 40 | x = x0.copy() 41 | 42 | r = b - A*x 43 | 44 | normr = np.linalg.norm(r) 45 | if residuals is not None: 46 | residuals[:] = [normr] # initial residual 47 | if errs is not None: 48 | errs[:] = [np.sqrt(np.dot(A*x, x))] 49 | if normr < tol: 50 | return (x, 0) 51 | 52 | iter = 0 53 | while True: 54 | if method is 'SD': 55 | v = r 56 | w = r 57 | Av = A*r 58 | alpha = np.inner(r, w)/np.inner(Av, w) 59 | elif method is 'MR': 60 | v = r 61 | w = A*r 62 | Av = w 63 | alpha = np.inner(r, w)/np.inner(Av, w) 64 | elif method is 'RNSD': 65 | v = A.T * r 66 | w = A*v 67 | Av = w 68 | alpha = np.inner(v, v)/np.inner(Av, Av) 69 | 70 | x = x + alpha * v 71 | r = r - alpha * Av 72 | 73 | normr = np.linalg.norm(r) 74 | 75 | iter += 1 76 | 77 | normr = np.linalg.norm(r) 78 | if residuals is not None: 79 | residuals.append(normr) # / (np.linalg.norm(A.data) * np.linalg.norm(x))) 80 | if errs is not None: 81 | errs.append(np.sqrt(np.dot(A*x, x))) 82 | if normr < tol or iter == maxiter: 83 | return (x, iter) 84 | -------------------------------------------------------------------------------- /multigrid/stencil.py: -------------------------------------------------------------------------------- 1 | """Construct sparse matrix from a local stencil""" 2 | from __future__ import print_function 3 | 4 | __docformat__ = "restructuredtext en" 5 | 6 | import numpy as np 7 | import scipy.sparse as sparse 8 | 9 | __all__ = ['stencil_grid'] 10 | 11 | 12 | def stencil_grid(S, grid, dtype=None, format=None): 13 | """Construct a sparse matrix form a local matrix stencil 14 | 15 | Parameters 16 | ---------- 17 | S : ndarray 18 | matrix stencil stored in N-d array 19 | grid : tuple 20 | tuple containing the N grid dimensions 21 | dtype : 22 | data type of the result 23 | format : string 24 | sparse matrix format to return, e.g. "csr", "coo", etc. 25 | 26 | Returns 27 | ------- 28 | A : sparse matrix 29 | Sparse matrix which represents the operator given by applying 30 | stencil S at each vertex of a regular grid with given dimensions. 31 | 32 | Notes 33 | ----- 34 | The grid vertices are enumerated as arange(prod(grid)).reshape(grid). 35 | This implies that the last grid dimension cycles fastest, while the 36 | first dimension cycles slowest. For example, if grid=(2,3) then the 37 | grid vertices are ordered as (0,0), (0,1), (0,2), (1,0), (1,1), (1,2). 38 | 39 | This coincides with the ordering used by the NumPy functions 40 | ndenumerate() and mgrid(). 41 | 42 | Examples 43 | -------- 44 | >>> from pyamg.gallery import stencil_grid 45 | >>> stencil = [-1,2,-1] # 1D Poisson stencil 46 | >>> grid = (5,) # 1D grid with 5 vertices 47 | >>> A = stencil_grid(stencil, grid, dtype=float, format='csr') 48 | >>> A.todense() 49 | matrix([[ 2., -1., 0., 0., 0.], 50 | [-1., 2., -1., 0., 0.], 51 | [ 0., -1., 2., -1., 0.], 52 | [ 0., 0., -1., 2., -1.], 53 | [ 0., 0., 0., -1., 2.]]) 54 | 55 | >>> stencil = [[0,-1,0],[-1,4,-1],[0,-1,0]] # 2D Poisson stencil 56 | >>> grid = (3,3) # 2D grid with shape 3x3 57 | >>> A = stencil_grid(stencil, grid, dtype=float, format='csr') 58 | >>> A.todense() 59 | matrix([[ 4., -1., 0., -1., 0., 0., 0., 0., 0.], 60 | [-1., 4., -1., 0., -1., 0., 0., 0., 0.], 61 | [ 0., -1., 4., 0., 0., -1., 0., 0., 0.], 62 | [-1., 0., 0., 4., -1., 0., -1., 0., 0.], 63 | [ 0., -1., 0., -1., 4., -1., 0., -1., 0.], 64 | [ 0., 0., -1., 0., -1., 4., 0., 0., -1.], 65 | [ 0., 0., 0., -1., 0., 0., 4., -1., 0.], 66 | [ 0., 0., 0., 0., -1., 0., -1., 4., -1.], 67 | [ 0., 0., 0., 0., 0., -1., 0., -1., 4.]]) 68 | 69 | """ 70 | 71 | S = np.asarray(S, dtype=dtype) 72 | grid = tuple(grid) 73 | 74 | if not (np.asarray(S.shape) % 2 == 1).all(): 75 | raise ValueError('all stencil dimensions must be odd') 76 | 77 | if len(grid) != np.ndim(S): 78 | raise ValueError('stencil dimension must equal number of grid\ 79 | dimensions') 80 | 81 | if min(grid) < 1: 82 | raise ValueError('grid dimensions must be positive') 83 | 84 | N_v = np.prod(grid) # number of vertices in the mesh 85 | N_s = (S != 0).sum() # number of nonzero stencil entries 86 | 87 | # diagonal offsets 88 | diags = np.zeros(N_s, dtype=int) 89 | 90 | # compute index offset of each dof within the stencil 91 | strides = np.cumprod([1] + list(reversed(grid)))[:-1] 92 | indices = tuple(i.copy() for i in S.nonzero()) 93 | for i, s in zip(indices, S.shape): 94 | i -= s // 2 95 | # i = (i - s) // 2 96 | # i = i // 2 97 | # i = i - (s // 2) 98 | for stride, coords in zip(strides, reversed(indices)): 99 | diags += stride * coords 100 | 101 | data = S[S != 0].repeat(N_v).reshape(N_s, N_v) 102 | 103 | indices = np.vstack(indices).T 104 | 105 | # zero boundary connections 106 | for index, diag in zip(indices, data): 107 | diag = diag.reshape(grid) 108 | for n, i in enumerate(index): 109 | if i > 0: 110 | s = [slice(None)] * len(grid) 111 | s[n] = slice(0, i) 112 | diag[s] = 0 113 | elif i < 0: 114 | s = [slice(None)]*len(grid) 115 | s[n] = slice(i, None) 116 | diag[s] = 0 117 | 118 | # remove diagonals that lie outside matrix 119 | mask = abs(diags) < N_v 120 | if not mask.all(): 121 | diags = diags[mask] 122 | data = data[mask] 123 | 124 | # sum duplicate diagonals 125 | if len(np.unique(diags)) != len(diags): 126 | new_diags = np.unique(diags) 127 | new_data = np.zeros((len(new_diags), data.shape[1]), 128 | dtype=data.dtype) 129 | 130 | for dia, dat in zip(diags, data): 131 | n = np.searchsorted(new_diags, dia) 132 | new_data[n, :] += dat 133 | 134 | diags = new_diags 135 | data = new_data 136 | 137 | return sparse.dia_matrix((data, diags), 138 | shape=(N_v, N_v)).asformat(format) 139 | 140 | 141 | if __name__ == '__main__': 142 | D = 2 143 | 144 | if D == 1: 145 | # 1D Laplacian 146 | S = np.array([-1, 2, -1]) 147 | grid = (4,) 148 | 149 | if D == 2: 150 | # 2D Laplacian 151 | S = np.array([[0, -1, 0], 152 | [-1, 4, -1], 153 | [0, -1, 0]]) 154 | # S = array([[-1, -1, -1], 155 | # [-1, 8, -1], 156 | # [-1, -1, -1]]) 157 | grid = (2, 1) 158 | 159 | if D == 3: 160 | S = np.array([[[0, 0, 0], 161 | [0, -1, 0], 162 | [0, 0, 0]], 163 | [[0, -1, 0], 164 | [-1, 6, -1], 165 | [0, -1, 0]], 166 | [[0, 0, 0], 167 | [0, -1, 0], 168 | [0, 0, 0]]]) 169 | grid = (3, 4, 5) 170 | 171 | A = stencil_grid(S, grid) 172 | 173 | print(A.todense()) 174 | --------------------------------------------------------------------------------