├── .gitignore ├── .travis.yml ├── 01_python.ipynb ├── 02_PDEs.ipynb ├── 03_finite_differences.ipynb ├── 04_BVP_problems.ipynb ├── 05_elliptic.ipynb ├── 06_iterative.ipynb ├── 07_ivp.ipynb ├── 08_convergence_IVP.ipynb ├── 09_parabolic.ipynb ├── 10_hyperbolic-1.ipynb ├── 11_hyperbolic-2.ipynb ├── 12_mixed.ipynb ├── 13_finite_volume.ipynb ├── 14_spectral.ipynb ├── 15_finite_element.ipynb ├── 16_performance.ipynb ├── LICENSE ├── MIT_LICENSE ├── README.md ├── images ├── 2d_grid.png ├── 2d_grid_red_black.png ├── FEM_projection_assembly.png ├── LWR-Velocity.png ├── P1LocalBases.png ├── P1_Hat_function_fig1.2LB.png ├── P1_function_annotated_fig1.1LB.png ├── ave_demo.png ├── characteristic_tracing_1.png ├── characteristic_tracing_2.png ├── characteristic_tracing_3.png ├── characteristics.png ├── characteristics_rarefaction.png ├── characteristics_regions_1.png ├── characteristics_regions_2.png ├── characteristics_shock.png ├── ellipses.png ├── ellipses_CG.png ├── entropy_condition_rarefaction.png ├── entropy_condition_shock.png ├── f_interpolation_P1_01.png ├── f_interpolation_P1_02.png ├── f_projection_P1_01.png ├── fd_basic.png ├── finite_volume.png ├── flux_stencil.png ├── fmg-cycle.png ├── ghost-cell.png ├── kepler_arch.pdf ├── kepler_arch.png ├── kepler_smx.pdf ├── kepler_smx.png ├── linear_reconstruction.png ├── memory_architecture.png ├── memory_single_core.png ├── moores_law.png ├── pipeline_1.png ├── pipeline_2.png ├── projection_figure.png ├── reconstruction_1.png ├── reconstruction_2.png ├── reconstruction_3.png ├── reconstruction_4.png ├── relationships.png ├── roofline.png ├── shock_diagram_traffic_a.png ├── shock_diagram_traffic_b.png ├── v-cycle.png ├── vonneumann_architecture.png └── w-cycle.png ├── peer_review.ipynb ├── reaction-diffusion_demo.ipynb ├── requirements.txt ├── src ├── .gitignore ├── Makefile ├── coarse_grain.f90 ├── compute_pi.f90 ├── computing_pi.py ├── demo2.f90 ├── fine_grain.f90 ├── hello_world.py ├── hello_world_mpi.f90 ├── hello_world_omp.f90 ├── jacobi_mpi.f90 ├── jacobi_omp1.f90 ├── jacobi_omp2.f90 ├── matrix_multiply.f90 ├── matrix_multiply.py ├── mod_rand.f90 ├── note_passing.f90 ├── note_passing.py └── yeval.f90 ├── test.py └── utils ├── __init__.py └── animation.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | # IPython notebooks 62 | .ipynb_checkpoints 63 | 64 | # Image base 65 | images/*.afdesign 66 | 67 | # Jupyter Notebook Specific minutia 68 | *.slides.html 69 | 70 | # Course ignores 71 | *-template.ipynb -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: true 3 | 4 | python: 5 | - "2.7" 6 | - "3.6" 7 | 8 | before_install: 9 | - sudo apt-get update -qq 10 | 11 | install: 12 | - pip install -U pip setuptools # Travis breaks without this 13 | - pip install -r requirements.txt 14 | 15 | script: 16 | - python test.py -------------------------------------------------------------------------------- /01_python.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "skip" 8 | } 9 | }, 10 | "source": [ 11 | "\n", 12 | " \n", 14 | "
\n", 13 | " Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved MIT license. (c) Kyle T. Mandli
" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "slideshow": { 22 | "slide_type": "skip" 23 | } 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "from __future__ import print_function" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": { 33 | "slideshow": { 34 | "slide_type": "slide" 35 | } 36 | }, 37 | "source": [ 38 | "# Review: Python\n", 39 | "\n", 40 | "APMA 4300 Resources:\n", 41 | " - [Intro to Python](https://github.com/mandli/intro-numerical-methods/blob/master/01_intro_to_python.ipynb)\n", 42 | " - [Intro to NumPy](https://github.com/mandli/intro-numerical-methods/blob/master/02_NumPy.ipynb)\n", 43 | " - [Intro to Matplotlib](https://github.com/mandli/intro-numerical-methods/blob/master/03_matplotlib.ipynb)\n", 44 | "\n", 45 | "Other resources:\n", 46 | " - [Basic Python](https://docs.python.org/2/tutorial/introduction.html)\n", 47 | " - [Software Carpentry - Programming in Python](http://swcarpentry.github.io/python-novice-inflammation/)\n", 48 | " - [NumPy Intro](http://mentat.za.net/numpy/intro/intro.html)\n", 49 | " - [Jessica Hamrick's nbgrader Intro](http://github.com/jhamrick/nbgrader-demo)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": { 55 | "slideshow": { 56 | "slide_type": "slide" 57 | } 58 | }, 59 | "source": [ 60 | "## Installation\n", 61 | "\n", 62 | "There are two options this semester for getting the necessary software:\n", 63 | "1. Install on your own machine\n", 64 | "1. Use a cloud computing platform" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": { 70 | "slideshow": { 71 | "slide_type": "subslide" 72 | } 73 | }, 74 | "source": [ 75 | "### Your Own Machine\n", 76 | "\n", 77 | "The easiest way to install all the components you will need for the class is to use the [Anaconda](http://anaconda.com/download) distribution. We will be using python 3.x for all in class demos and homework so I strongly suggest you use the Python 3.6 version.\n", 78 | "\n", 79 | "Alternatives to using Anaconda also exist in the form of Enthought's [Canopy](http://store.enthought.com/downloads/) distribution which provides all the tools you will need as well along with an IDE (development environment)." 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": { 85 | "slideshow": { 86 | "slide_type": "subslide" 87 | } 88 | }, 89 | "source": [ 90 | "### Cloud Computing\n", 91 | "\n", 92 | "Instead of running things locally on your machine there are a number of cloud services that you are welcome to use in order to get everything running easily.\n", 93 | " 1. CoCalc - Create an account on [cocalc.com](https://www.cocalc.com) and interact with python via the provided terminal or Jupyter notebook inteface.\n", 94 | " \n", 95 | "There are a number of other options now available but note that some will not have the required packages or may be otherwise incompatible:\n", 96 | " 1. Azure Notebooks - Microsoft has free notebook hosting at http://notebooks.azure.com\n", 97 | " 1. Google - Google has notebook hosting at http://colab.research.google.com/" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": { 103 | "slideshow": { 104 | "slide_type": "slide" 105 | } 106 | }, 107 | "source": [ 108 | "## Basic Python\n", 109 | "\n", 110 | "Python is a dynamic, interpreted language used throughout computational engineering and science. Due to its ease of use, cost, and available tools we will be learning the material in this course using Python tools exclusively. For those who are coming in without prior Python knowledge but a strong programming background this lecture should acquaint you with the basics. For people who do not know Python and do not have a strong programming background it is strongly recommended that you take the time to look through the other tutorials mentioned above." 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": { 116 | "slideshow": { 117 | "slide_type": "subslide" 118 | } 119 | }, 120 | "source": [ 121 | "### Math\n", 122 | "\n", 123 | "Basic math in Python is fairly straight forward with all the usual types of operations (`+`, `-`, `*`, `/`, `**`) which are addition, subtraction, multiplication, division and power." 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": { 130 | "slideshow": { 131 | "slide_type": "skip" 132 | } 133 | }, 134 | "outputs": [], 135 | "source": [ 136 | "1 / 2" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": { 142 | "slideshow": { 143 | "slide_type": "subslide" 144 | } 145 | }, 146 | "source": [ 147 | "Note that in Python 2 this would give you `floor(1 / 2) = 1`." 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": { 154 | "slideshow": { 155 | "slide_type": "skip" 156 | } 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "4 + 4**(3/2)" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": { 166 | "slideshow": { 167 | "slide_type": "subslide" 168 | } 169 | }, 170 | "source": [ 171 | "Python also understands imaginary numbers:" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": { 178 | "slideshow": { 179 | "slide_type": "skip" 180 | } 181 | }, 182 | "outputs": [], 183 | "source": [ 184 | "4 + 3j" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": { 190 | "slideshow": { 191 | "slide_type": "subslide" 192 | } 193 | }, 194 | "source": [ 195 | "Some of the more advanced mathematical functions are stored in modules. In order to use these functions we must first `import` them into our notebook and then use them." 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": { 202 | "slideshow": { 203 | "slide_type": "skip" 204 | } 205 | }, 206 | "outputs": [], 207 | "source": [ 208 | "import math" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": { 215 | "slideshow": { 216 | "slide_type": "skip" 217 | } 218 | }, 219 | "outputs": [], 220 | "source": [ 221 | "math?" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": { 228 | "slideshow": { 229 | "slide_type": "skip" 230 | } 231 | }, 232 | "outputs": [], 233 | "source": [ 234 | "math.sqrt(2.0)" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": { 241 | "slideshow": { 242 | "slide_type": "skip" 243 | } 244 | }, 245 | "outputs": [], 246 | "source": [ 247 | "math.sin(math.pi / 2.0)" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "metadata": { 254 | "slideshow": { 255 | "slide_type": "skip" 256 | } 257 | }, 258 | "outputs": [], 259 | "source": [ 260 | "from math import *\n", 261 | "sin(pi)" 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "metadata": { 267 | "slideshow": { 268 | "slide_type": "subslide" 269 | } 270 | }, 271 | "source": [ 272 | "### Variables\n", 273 | "\n", 274 | "Variables are defined and assigned to like many other languages." 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": null, 280 | "metadata": { 281 | "slideshow": { 282 | "slide_type": "skip" 283 | } 284 | }, 285 | "outputs": [], 286 | "source": [ 287 | "num_students = 80\n", 288 | "room_capacity = 85\n", 289 | "(room_capacity - num_students) / room_capacity * 100.0" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": { 295 | "slideshow": { 296 | "slide_type": "subslide" 297 | } 298 | }, 299 | "source": [ 300 | "### Control Flow\n", 301 | "\n", 302 | "`if` statements are the most basic unit of logic and allows us to conditionally operate on things." 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "metadata": { 309 | "slideshow": { 310 | "slide_type": "skip" 311 | } 312 | }, 313 | "outputs": [], 314 | "source": [ 315 | "x = 4\n", 316 | "if x > 5:\n", 317 | " print(\"x is greater than 5\")\n", 318 | "elif x < 5:\n", 319 | " print(\"x is less than 5\")\n", 320 | "else:\n", 321 | " print(\"x is equal to 5\")" 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": { 327 | "slideshow": { 328 | "slide_type": "subslide" 329 | } 330 | }, 331 | "source": [ 332 | "`for` allows us to repeat tasks over a range of values or objects." 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "execution_count": null, 338 | "metadata": { 339 | "slideshow": { 340 | "slide_type": "skip" 341 | } 342 | }, 343 | "outputs": [], 344 | "source": [ 345 | "for i in range(5):\n", 346 | " print(i)" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": null, 352 | "metadata": { 353 | "slideshow": { 354 | "slide_type": "skip" 355 | } 356 | }, 357 | "outputs": [], 358 | "source": [ 359 | "for animal in ['cat', 'dog', 'chinchilla']:\n", 360 | " print(animal)" 361 | ] 362 | }, 363 | { 364 | "cell_type": "code", 365 | "execution_count": null, 366 | "metadata": { 367 | "slideshow": { 368 | "slide_type": "skip" 369 | } 370 | }, 371 | "outputs": [], 372 | "source": [ 373 | "for n in range(2, 10):\n", 374 | " is_prime = True\n", 375 | " for x in range(2, n):\n", 376 | " if n % x == 0:\n", 377 | " print(n, 'equals', x, '*', n / x)\n", 378 | " is_prime = False\n", 379 | " break\n", 380 | " if is_prime:\n", 381 | " print(\"%s is a prime number\" % (n))" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": { 387 | "slideshow": { 388 | "slide_type": "subslide" 389 | } 390 | }, 391 | "source": [ 392 | "### Functions\n", 393 | "\n", 394 | "Functions are a fundamental way in any language to break up the code into pieces that can be isolated and repeatedly used based on their input." 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": null, 400 | "metadata": { 401 | "slideshow": { 402 | "slide_type": "skip" 403 | } 404 | }, 405 | "outputs": [], 406 | "source": [ 407 | "def my_print_function(x):\n", 408 | " print(x)\n", 409 | "\n", 410 | "my_print_function(3)" 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": null, 416 | "metadata": { 417 | "slideshow": { 418 | "slide_type": "skip" 419 | } 420 | }, 421 | "outputs": [], 422 | "source": [ 423 | "def my_add_function(a, b):\n", 424 | " return a + b, b\n", 425 | "\n", 426 | "my_add_function(3.0, 5.0)" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": null, 432 | "metadata": { 433 | "slideshow": { 434 | "slide_type": "skip" 435 | } 436 | }, 437 | "outputs": [], 438 | "source": [ 439 | "def my_crazy_function(a, b, c=1.0):\n", 440 | " d = a + b**c\n", 441 | " return d\n", 442 | "\n", 443 | "my_crazy_function(2.0, 3.0), my_crazy_function(2.0, 3.0, 2.0), my_crazy_function(2.0, 3.0, c=2.0)" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": null, 449 | "metadata": { 450 | "slideshow": { 451 | "slide_type": "skip" 452 | } 453 | }, 454 | "outputs": [], 455 | "source": [ 456 | "def my_other_function(a, b, c=1.0):\n", 457 | " return a + b, a + b**c, a + b**(3.0 / 7.0)\n", 458 | "\n", 459 | "my_other_function(2.0, 3.0, c=2.0)" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": null, 465 | "metadata": { 466 | "scrolled": true, 467 | "slideshow": { 468 | "slide_type": "skip" 469 | } 470 | }, 471 | "outputs": [], 472 | "source": [ 473 | "def fibonacci(n):\n", 474 | " \"\"\"Return a list of the Fibonacci sequence up to n\"\"\"\n", 475 | " values = [0, 1]\n", 476 | " while values[-1] <= n:\n", 477 | " values.append(values[-1] + values[-2])\n", 478 | " print(values)\n", 479 | " return values\n", 480 | "\n", 481 | "fibonacci(100)\n", 482 | "fibonacci?" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": { 488 | "slideshow": { 489 | "slide_type": "subslide" 490 | } 491 | }, 492 | "source": [ 493 | "## NumPy\n", 494 | "\n", 495 | "The most important part of NumPy is the specification of an array object called an `ndarray`. This object as its name suggests stores array like information in multiple dimensions. These objects allow a programmer to access the data in a multitude of different ways as well as create common types of arrays and operate on these arrays easily." 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": null, 501 | "metadata": { 502 | "slideshow": { 503 | "slide_type": "skip" 504 | } 505 | }, 506 | "outputs": [], 507 | "source": [ 508 | "import numpy" 509 | ] 510 | }, 511 | { 512 | "cell_type": "markdown", 513 | "metadata": { 514 | "slideshow": { 515 | "slide_type": "subslide" 516 | } 517 | }, 518 | "source": [ 519 | "### Constructors\n", 520 | "\n", 521 | "Ways to make arrays in NumPy." 522 | ] 523 | }, 524 | { 525 | "cell_type": "code", 526 | "execution_count": null, 527 | "metadata": { 528 | "slideshow": { 529 | "slide_type": "skip" 530 | } 531 | }, 532 | "outputs": [], 533 | "source": [ 534 | "my_array = numpy.array([[1, 2], [3, 4]])\n", 535 | "print(my_array)" 536 | ] 537 | }, 538 | { 539 | "cell_type": "code", 540 | "execution_count": null, 541 | "metadata": { 542 | "slideshow": { 543 | "slide_type": "skip" 544 | } 545 | }, 546 | "outputs": [], 547 | "source": [ 548 | "numpy.linspace(-1, 1, 10)" 549 | ] 550 | }, 551 | { 552 | "cell_type": "code", 553 | "execution_count": null, 554 | "metadata": { 555 | "slideshow": { 556 | "slide_type": "skip" 557 | } 558 | }, 559 | "outputs": [], 560 | "source": [ 561 | "numpy.zeros([3, 3])" 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": null, 567 | "metadata": { 568 | "slideshow": { 569 | "slide_type": "skip" 570 | } 571 | }, 572 | "outputs": [], 573 | "source": [ 574 | "numpy.ones([2, 3, 2])" 575 | ] 576 | }, 577 | { 578 | "cell_type": "code", 579 | "execution_count": null, 580 | "metadata": { 581 | "slideshow": { 582 | "slide_type": "skip" 583 | } 584 | }, 585 | "outputs": [], 586 | "source": [ 587 | "numpy.empty([2,3])" 588 | ] 589 | }, 590 | { 591 | "cell_type": "markdown", 592 | "metadata": { 593 | "slideshow": { 594 | "slide_type": "subslide" 595 | } 596 | }, 597 | "source": [ 598 | "### Access\n", 599 | "\n", 600 | "How do we access data in an array?" 601 | ] 602 | }, 603 | { 604 | "cell_type": "code", 605 | "execution_count": null, 606 | "metadata": { 607 | "slideshow": { 608 | "slide_type": "skip" 609 | } 610 | }, 611 | "outputs": [], 612 | "source": [ 613 | "my_array[0, 1]" 614 | ] 615 | }, 616 | { 617 | "cell_type": "code", 618 | "execution_count": null, 619 | "metadata": { 620 | "slideshow": { 621 | "slide_type": "skip" 622 | } 623 | }, 624 | "outputs": [], 625 | "source": [ 626 | "my_array[:,0]" 627 | ] 628 | }, 629 | { 630 | "cell_type": "code", 631 | "execution_count": null, 632 | "metadata": { 633 | "slideshow": { 634 | "slide_type": "skip" 635 | } 636 | }, 637 | "outputs": [], 638 | "source": [ 639 | "my_vec = numpy.array([[1], [2]])\n", 640 | "print(my_vec)" 641 | ] 642 | }, 643 | { 644 | "cell_type": "code", 645 | "execution_count": null, 646 | "metadata": { 647 | "slideshow": { 648 | "slide_type": "skip" 649 | } 650 | }, 651 | "outputs": [], 652 | "source": [ 653 | "numpy.dot(my_array, my_vec)\n", 654 | "numpy.cross?" 655 | ] 656 | }, 657 | { 658 | "cell_type": "code", 659 | "execution_count": null, 660 | "metadata": { 661 | "slideshow": { 662 | "slide_type": "skip" 663 | } 664 | }, 665 | "outputs": [], 666 | "source": [ 667 | "my_array * my_vec" 668 | ] 669 | }, 670 | { 671 | "cell_type": "markdown", 672 | "metadata": { 673 | "slideshow": { 674 | "slide_type": "subslide" 675 | } 676 | }, 677 | "source": [ 678 | "### Manipulations\n", 679 | "\n", 680 | "How do we manipulate arrays beyond indexing into them?" 681 | ] 682 | }, 683 | { 684 | "cell_type": "code", 685 | "execution_count": null, 686 | "metadata": { 687 | "slideshow": { 688 | "slide_type": "skip" 689 | } 690 | }, 691 | "outputs": [], 692 | "source": [ 693 | "A = numpy.array([[1, 2, 3], [4, 5, 6]])\n", 694 | "print(\"A Shape = \", A.shape)\n", 695 | "print(A)" 696 | ] 697 | }, 698 | { 699 | "cell_type": "code", 700 | "execution_count": null, 701 | "metadata": { 702 | "slideshow": { 703 | "slide_type": "skip" 704 | } 705 | }, 706 | "outputs": [], 707 | "source": [ 708 | "B = A.reshape((6,1))\n", 709 | "print(\"A Shape = \", A.shape)\n", 710 | "print(\"B Shape = \", B.shape)\n", 711 | "print(B)" 712 | ] 713 | }, 714 | { 715 | "cell_type": "code", 716 | "execution_count": null, 717 | "metadata": { 718 | "slideshow": { 719 | "slide_type": "skip" 720 | } 721 | }, 722 | "outputs": [], 723 | "source": [ 724 | "numpy.tile(A, (2,2))" 725 | ] 726 | }, 727 | { 728 | "cell_type": "code", 729 | "execution_count": null, 730 | "metadata": { 731 | "slideshow": { 732 | "slide_type": "skip" 733 | } 734 | }, 735 | "outputs": [], 736 | "source": [ 737 | "A.transpose()" 738 | ] 739 | }, 740 | { 741 | "cell_type": "code", 742 | "execution_count": null, 743 | "metadata": { 744 | "slideshow": { 745 | "slide_type": "skip" 746 | } 747 | }, 748 | "outputs": [], 749 | "source": [ 750 | "A = numpy.array([[1,2,3],[4,5,6],[7,8,9]])\n", 751 | "print(A)\n", 752 | "print(A.shape)" 753 | ] 754 | }, 755 | { 756 | "cell_type": "code", 757 | "execution_count": null, 758 | "metadata": { 759 | "scrolled": true, 760 | "slideshow": { 761 | "slide_type": "skip" 762 | } 763 | }, 764 | "outputs": [], 765 | "source": [ 766 | "B = numpy.arange(1,10)\n", 767 | "print(B)\n", 768 | "print(B.reshape((3,3)))\n", 769 | "C = B.reshape((3,3))" 770 | ] 771 | }, 772 | { 773 | "cell_type": "code", 774 | "execution_count": null, 775 | "metadata": { 776 | "slideshow": { 777 | "slide_type": "skip" 778 | } 779 | }, 780 | "outputs": [], 781 | "source": [ 782 | "print(A * C)" 783 | ] 784 | }, 785 | { 786 | "cell_type": "code", 787 | "execution_count": null, 788 | "metadata": { 789 | "scrolled": false, 790 | "slideshow": { 791 | "slide_type": "skip" 792 | } 793 | }, 794 | "outputs": [], 795 | "source": [ 796 | "numpy.dot(A, C)" 797 | ] 798 | }, 799 | { 800 | "cell_type": "markdown", 801 | "metadata": { 802 | "slideshow": { 803 | "slide_type": "subslide" 804 | } 805 | }, 806 | "source": [ 807 | "### Mathematical Functions" 808 | ] 809 | }, 810 | { 811 | "cell_type": "code", 812 | "execution_count": null, 813 | "metadata": { 814 | "slideshow": { 815 | "slide_type": "skip" 816 | } 817 | }, 818 | "outputs": [], 819 | "source": [ 820 | "x = numpy.linspace(-2.0 * numpy.pi, 2.0 * numpy.pi, 62)\n", 821 | "y = numpy.sin(x)\n", 822 | "print(y)" 823 | ] 824 | }, 825 | { 826 | "cell_type": "code", 827 | "execution_count": null, 828 | "metadata": { 829 | "slideshow": { 830 | "slide_type": "skip" 831 | } 832 | }, 833 | "outputs": [], 834 | "source": [ 835 | "x = numpy.linspace(-1, 1, 20)\n", 836 | "numpy.sqrt(x)" 837 | ] 838 | }, 839 | { 840 | "cell_type": "code", 841 | "execution_count": null, 842 | "metadata": { 843 | "slideshow": { 844 | "slide_type": "skip" 845 | } 846 | }, 847 | "outputs": [], 848 | "source": [ 849 | "x = numpy.linspace(-1, 1, 20, dtype=complex)\n", 850 | "numpy.sqrt(x)" 851 | ] 852 | }, 853 | { 854 | "cell_type": "markdown", 855 | "metadata": { 856 | "slideshow": { 857 | "slide_type": "subslide" 858 | } 859 | }, 860 | "source": [ 861 | "### Linear Algebra\n", 862 | "\n", 863 | "Some functions for linear algebra available in NumPy. Full implementation in `scipy.linalg`." 864 | ] 865 | }, 866 | { 867 | "cell_type": "code", 868 | "execution_count": null, 869 | "metadata": { 870 | "slideshow": { 871 | "slide_type": "skip" 872 | } 873 | }, 874 | "outputs": [], 875 | "source": [ 876 | "numpy.linalg.norm(x)" 877 | ] 878 | }, 879 | { 880 | "cell_type": "code", 881 | "execution_count": null, 882 | "metadata": { 883 | "slideshow": { 884 | "slide_type": "skip" 885 | } 886 | }, 887 | "outputs": [], 888 | "source": [ 889 | "M = numpy.array([[0,2],[8,0]])\n", 890 | "b = numpy.array([1,2])\n", 891 | "print(M)\n", 892 | "print(b)" 893 | ] 894 | }, 895 | { 896 | "cell_type": "code", 897 | "execution_count": null, 898 | "metadata": { 899 | "slideshow": { 900 | "slide_type": "skip" 901 | } 902 | }, 903 | "outputs": [], 904 | "source": [ 905 | "x = numpy.linalg.solve(M,b)\n", 906 | "print(x)" 907 | ] 908 | }, 909 | { 910 | "cell_type": "code", 911 | "execution_count": null, 912 | "metadata": { 913 | "slideshow": { 914 | "slide_type": "skip" 915 | } 916 | }, 917 | "outputs": [], 918 | "source": [ 919 | "lamda, V = numpy.linalg.eig(M)\n", 920 | "print(lamda)\n", 921 | "print(V)" 922 | ] 923 | }, 924 | { 925 | "cell_type": "markdown", 926 | "metadata": { 927 | "slideshow": { 928 | "slide_type": "slide" 929 | } 930 | }, 931 | "source": [ 932 | "## SciPy\n", 933 | "\n", 934 | "NumPy contains the basic building blocks for numerical computing where as the SciPy packages contain all the higher-level functionality. Refer to the [SciPy webpage](http://www.scipy.org) for more information on what it contains. There are also a number of [SciKits](http://scikits.appspot.com/scikits) which provide additional functionality" 935 | ] 936 | }, 937 | { 938 | "cell_type": "markdown", 939 | "metadata": { 940 | "slideshow": { 941 | "slide_type": "subslide" 942 | } 943 | }, 944 | "source": [ 945 | "## Matplotlib\n", 946 | "\n", 947 | "The most common facility for plotting with the Python numerical suite is to use the `matplotlib` package. We will cover a few of the basic approaches to plotting figures. If you are interested in learning more about `matplotlib` or are looking to see how you might create a particular plot check out the matplotlib [gallery](http://matplotlib.org/gallery) for inspiration.\n", 948 | "\n", 949 | "Refer to the [APAM 4300 Notes](https://github.com/mandli/intro-numerical-methods/blob/master/03_matplotlib.ipynb) on matplotlib for detailed examples." 950 | ] 951 | }, 952 | { 953 | "cell_type": "markdown", 954 | "metadata": { 955 | "slideshow": { 956 | "slide_type": "slide" 957 | } 958 | }, 959 | "source": [ 960 | "## Jupyter Notebooks\n", 961 | "\n", 962 | "All class notes and assignments will be done within Jupyter notebooks (formally IPython notebooks). It is important that you become acquainted with these as there are a couple of pitfalls that you should be aware of.\n", 963 | " - Before turning in an assignment make sure to go to the \"Kernel\" menu and restart the notebook. After this select \"Run All\" from the \"Cell\" menu to rerun everything. This will ensure that you have properly defined everything in your notebook and have not accidentally erased a variable definition.\n", 964 | " - Use version 4.0 of the notebook so as not to run into problems with deleting notebook cells." 965 | ] 966 | }, 967 | { 968 | "cell_type": "markdown", 969 | "metadata": { 970 | "slideshow": { 971 | "slide_type": "slide" 972 | } 973 | }, 974 | "source": [ 975 | "## Debugging\n", 976 | "\n", 977 | "(based on Jessica Hamrick's [debugging demo](https://github.com/jhamrick/nbgrader-demo))" 978 | ] 979 | }, 980 | { 981 | "cell_type": "markdown", 982 | "metadata": { 983 | "slideshow": { 984 | "slide_type": "subslide" 985 | } 986 | }, 987 | "source": [ 988 | "Debugging is one of the most critical tools we have at our disposal. Apart from standard inspection approaches (`print` statements) the Jupyter notebook has a number of ways to debug as well.\n", 989 | "\n", 990 | "Jupyter notebooks provide an interface to the python debugger `pdb`. The easiest way to use this functionality is by using the \"magic\" `%pdb` at the top of your notebook which will allow cause the notebook to jump into the python debugger anytime an exception is reached. This will allow you to step through your code and figure out what is wrong. If you want to step through code or just activate the trace back for the current cell use the `%debug` magic." 991 | ] 992 | }, 993 | { 994 | "cell_type": "code", 995 | "execution_count": null, 996 | "metadata": { 997 | "scrolled": true, 998 | "slideshow": { 999 | "slide_type": "skip" 1000 | } 1001 | }, 1002 | "outputs": [], 1003 | "source": [ 1004 | "# for inline plotting in the notebook\n", 1005 | "%matplotlib inline \n", 1006 | "\n", 1007 | "# debugger\n", 1008 | "%pdb\n", 1009 | "\n", 1010 | "import numpy\n", 1011 | "import matplotlib.pyplot as plt\n", 1012 | "\n", 1013 | "def plot_log():\n", 1014 | " figure, axis = plt.subplots(2, 1)\n", 1015 | " x = numpy.linspace(1, 2, 10)\n", 1016 | " axis.plot(x, np.log(x)) # correct is plt.plot()\n", 1017 | " plt.show()\n", 1018 | "\n", 1019 | "plot_log() # Call the function, generate plot" 1020 | ] 1021 | }, 1022 | { 1023 | "cell_type": "markdown", 1024 | "metadata": { 1025 | "slideshow": { 1026 | "slide_type": "subslide" 1027 | } 1028 | }, 1029 | "source": [ 1030 | "Paths to debugging\n", 1031 | "1. Check the traceback\n", 1032 | "1. Use the `%debug` magic\n", 1033 | "1. `print` statements\n", 1034 | "\n", 1035 | "and if all else fails\n", 1036 | "1. Copy and paste your error message into Google to see if anyone else has experienced similar problems. You'd be surprised how often this works!\n", 1037 | "2. Search [StackOverflow](https://stackoverflow.com/questions/tagged/python)\n", 1038 | "3. Consult fellow classmates\n", 1039 | "4. Consult the GSIs" 1040 | ] 1041 | }, 1042 | { 1043 | "cell_type": "markdown", 1044 | "metadata": { 1045 | "slideshow": { 1046 | "slide_type": "slide" 1047 | } 1048 | }, 1049 | "source": [ 1050 | "## Code Styling\n", 1051 | "\n", 1052 | "Very important in practice to write readable and understandable code. Here are a few things to keep in mind while programming in and out of this class, we will work on this actively as the semester progresses as well. The standard for which Python program are written to is called [PEP 8](http://www.python.org/dev/peps/pep-0008) and contains the following basic guidelines:\n", 1053 | " - Use 4-space indentation, no tabs\n", 1054 | " - Wrap lines that exceed 80 characters\n", 1055 | " - Use judicious use of blank lines to separate out functions, classes, and larger blocks of contained code\n", 1056 | " - Comment! Also, put comments on their own line when possible\n", 1057 | " - Use `docstrings` (function descriptions)\n", 1058 | " - Use spaces around operators and after commas, `a = f(1, 2) + g(3, 4)`\n", 1059 | " - Name your classes and functions consistently.\n", 1060 | " - Use `CamelCase` for classes\n", 1061 | " - Use `lower_case_with_underscores` for functions and variables\n", 1062 | " - When in doubt be verbose with your comments and names of variables, functions, and classes\n", 1063 | " \n", 1064 | "Good coding style will mean that we can more easily grade your assignments and understand what you are doing. Please make sure to keep this in mind especially when naming variables, making comments, or documenting your functions." 1065 | ] 1066 | }, 1067 | { 1068 | "cell_type": "markdown", 1069 | "metadata": { 1070 | "slideshow": { 1071 | "slide_type": "slide" 1072 | } 1073 | }, 1074 | "source": [ 1075 | "## Last Notes\n", 1076 | "\n", 1077 | " - Live coding demos $\\Rightarrow$ Bring your laptops to class.\n", 1078 | " - All notebooks are found on [github](https://github.com/Hadrien-Montanelli/numerical-methods-pdes).\n", 1079 | " - Highly recommend obtaining a github account if you do not already have one. Will allow you start to become comfortable with `git`.\n", 1080 | " - Also note that you can watch what changes were made and submit **issues** to the github project page if you find mistakes (PLEASE DO THIS!)." 1081 | ] 1082 | } 1083 | ], 1084 | "metadata": { 1085 | "celltoolbar": "Slideshow", 1086 | "kernelspec": { 1087 | "display_name": "Python 3", 1088 | "language": "python", 1089 | "name": "python3" 1090 | }, 1091 | "language_info": { 1092 | "codemirror_mode": { 1093 | "name": "ipython", 1094 | "version": 3 1095 | }, 1096 | "file_extension": ".py", 1097 | "mimetype": "text/x-python", 1098 | "name": "python", 1099 | "nbconvert_exporter": "python", 1100 | "pygments_lexer": "ipython3", 1101 | "version": "3.7.6" 1102 | }, 1103 | "latex_envs": { 1104 | "bibliofile": "biblio.bib", 1105 | "cite_by": "apalike", 1106 | "current_citInitial": 1, 1107 | "eqLabelWithNumbers": true, 1108 | "eqNumInitial": 0 1109 | } 1110 | }, 1111 | "nbformat": 4, 1112 | "nbformat_minor": 1 1113 | } 1114 | -------------------------------------------------------------------------------- /05_elliptic.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "skip" 8 | } 9 | }, 10 | "source": [ 11 | "\n", 12 | " \n", 14 | "
\n", 13 | " Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved MIT license. (c) Kyle T. Mandli
" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "collapsed": true, 22 | "slideshow": { 23 | "slide_type": "skip" 24 | } 25 | }, 26 | "outputs": [], 27 | "source": [ 28 | "%matplotlib inline\n", 29 | "from __future__ import print_function\n", 30 | "import numpy\n", 31 | "import matplotlib.pyplot as plt" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": { 37 | "slideshow": { 38 | "slide_type": "slide" 39 | } 40 | }, 41 | "source": [ 42 | "# Elliptic Equations" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": { 48 | "slideshow": { 49 | "slide_type": "subslide" 50 | } 51 | }, 52 | "source": [ 53 | "Higher dimensional elliptic equations in general have the form\n", 54 | "$$\n", 55 | " a_1 u_{xx} + a_2 u_{xy} + a_3 u_{yy} + a_4 u_x + a_5 u_y + a_6 u = f\n", 56 | "$$\n", 57 | "where the coefficients must satisfy\n", 58 | "$$\n", 59 | " a_2^2 - 4 a_1 a_3 < 0.\n", 60 | "$$" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": { 66 | "slideshow": { 67 | "slide_type": "subslide" 68 | } 69 | }, 70 | "source": [ 71 | "One natural place again that equations like this come up is again in the steady-state problems arising from the heat equation. In two dimensions we have\n", 72 | "$$\n", 73 | " u_t = (\\kappa u_x)_x + (\\kappa u_y)_y + \\Psi(x,y).\n", 74 | "$$\n", 75 | "If $u_t = 0$ we then have\n", 76 | "$$\n", 77 | " (\\kappa u_x)_x + (\\kappa u_y)_y = -\\Psi(x,y).\n", 78 | "$$" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": { 84 | "slideshow": { 85 | "slide_type": "subslide" 86 | } 87 | }, 88 | "source": [ 89 | "If we assume $\\kappa$ to be constant we can rewrite this equation as\n", 90 | "$$\n", 91 | " u_{xx} + u_{yy} = -\\frac{\\Psi}{\\kappa} = f(x,y)\n", 92 | "$$\n", 93 | "which is the higher dimensional form of Poisson's equation (and Laplace's equation if $f(x,y) = 0$).\n", 94 | "\n", 95 | "We also must specify a domain $\\Omega$ and boundary conditions as before. Now the boundary conditions will have $x$ and $y$ dependence so will need to be handled a bit differently than before." 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": { 101 | "slideshow": { 102 | "slide_type": "slide" 103 | } 104 | }, 105 | "source": [ 106 | "## 5-Point Stencil\n", 107 | "\n", 108 | "Extending our work in one-dimension to two we can construct the 5-point stencil for the Laplacian operator as\n", 109 | "$$\n", 110 | " \\nabla^2 u \\approx \\frac{U_{i-1,j} - 2 U_{i,j} + U_{i+1,j}}{\\Delta x^2} + \\frac{U_{i,j-1} - 2 U_{i,j} + U_{i,j+1}}{\\Delta y^2}.\n", 111 | "$$" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": { 117 | "slideshow": { 118 | "slide_type": "subslide" 119 | } 120 | }, 121 | "source": [ 122 | "If $\\Delta x = \\Delta y$ we can simplify this expression to\n", 123 | "$$\n", 124 | " \\nabla^2 u \\approx \\frac{U_{i-1,j} + U_{i+1,j} - 4 U_{i,j} + U_{i,j-1} + U_{i,j+1}}{\\Delta x^2}\n", 125 | "$$" 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": { 131 | "slideshow": { 132 | "slide_type": "subslide" 133 | } 134 | }, 135 | "source": [ 136 | "We can the approximate the solution to the Poisson equation as\n", 137 | "$$\n", 138 | " \\frac{U_{i-1,j} + U_{i+1,j} - 4 U_{i,j} + U_{i,j-1} + U_{i,j+1}}{\\Delta x^2} = f_{i,j}\n", 139 | "$$\n", 140 | "which leads to $m^2$ equations for $m^2$ unknowns." 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": { 146 | "slideshow": { 147 | "slide_type": "slide" 148 | } 149 | }, 150 | "source": [ 151 | "## Ordering of Unknowns" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": { 157 | "slideshow": { 158 | "slide_type": "subslide" 159 | } 160 | }, 161 | "source": [ 162 | "We can think of our two-dimensional problem as laying on a grid as in the following figure. Here the unknows are in red and the boundaries are in blue:\n", 163 | "![2D Grid](./images/2d_grid.png)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": { 169 | "slideshow": { 170 | "slide_type": "subslide" 171 | } 172 | }, 173 | "source": [ 174 | "We can think of the grid as being a matrix like\n", 175 | "$$\n", 176 | " U = \\begin{bmatrix}\n", 177 | " U_{1, m} & U_{2, m} & \\cdots & U_{m-1, m} & U_{m,m} \\\\\n", 178 | " U_{1, m-1} & U_{2, m-1} & \\cdots & U_{m-1, m-1} & U_{m, m-1} \\\\\n", 179 | " \\vdots & & & & \\vdots \\\\\n", 180 | " U_{1, 2} & U_{2, 2} & \\cdots & U_{m-1, 2} & U_{m, 2} \\\\\n", 181 | " U_{1, 1} & U_{2, 1} & \\cdots & U_{m-1, 1} & U_{m, 1} \\\\\n", 182 | " \\end{bmatrix}\n", 183 | "$$\n", 184 | "It is important to note that this labeling does not match our usual matrix notation!" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": { 190 | "slideshow": { 191 | "slide_type": "subslide" 192 | } 193 | }, 194 | "source": [ 195 | "When we had a single dimension the ordering of the unknowns (and equations) was pretty clear but now we have a number of choices to make. Perhaps the obvious choice is to break up the 2d array into rows and append each to the end of the previous one. This leads to the ordering\n", 196 | "$$\n", 197 | " U = [U_{1, 1}, U_{2, 1}, \\ldots, U_{m-1,1}, U_{m, 1}, U_{1, 2}, U_{22},\\ldots, U_{m-1,m}, U_{mm}]\n", 198 | "$$\n", 199 | "\n", 200 | "See if you can figure out the structure of the matrix $A$ now with this ordering." 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": { 206 | "slideshow": { 207 | "slide_type": "subslide" 208 | } 209 | }, 210 | "source": [ 211 | "This leads to a matrix of the form\n", 212 | "$$\n", 213 | " A = \\frac{1}{\\Delta x^2} \\begin{bmatrix}\n", 214 | " T & I \\\\\n", 215 | " I & T & I \\\\\n", 216 | " & I & T & I \\\\\n", 217 | " & & \\ddots & \\ddots & \\ddots \\\\\n", 218 | " & & & I & T & I \\\\\n", 219 | " & & & & I & T\n", 220 | " \\end{bmatrix}\n", 221 | "$$\n", 222 | "where the matrix $T$ is\n", 223 | "$$\n", 224 | " T = \\begin{bmatrix}\n", 225 | " -4 & 1 \\\\\n", 226 | " 1 & -4 & 1 \\\\\n", 227 | " & 1 & -4 & 1 \\\\\n", 228 | " & & \\ddots & \\ddots & \\ddots \\\\\n", 229 | " & & & 1 & -4 & 1 \\\\\n", 230 | " & & & & 1 & -4\n", 231 | " \\end{bmatrix}\n", 232 | "$$\n", 233 | "and $I$ is the $m \\times m$ identity matrix." 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": { 239 | "slideshow": { 240 | "slide_type": "subslide" 241 | } 242 | }, 243 | "source": [ 244 | "Another possibility is *red-black* ordering (like a checker board). Here we \"color\" each unknown in our grid alternating colors, each colored unknown then is grouped together. \n", 245 | "\n", 246 | "![2D Grid Red-Black Ordering](./images/2d_grid_red_black.png)\n", 247 | "\n", 248 | "Can you figure out what the matrix looks like in this case?" 249 | ] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "metadata": { 254 | "slideshow": { 255 | "slide_type": "subslide" 256 | } 257 | }, 258 | "source": [ 259 | "For red-black ordering we are lead to the matrix\n", 260 | "$$\n", 261 | " \\begin{bmatrix}\n", 262 | " D & H \\\\\n", 263 | " H^T & D\n", 264 | " \\end{bmatrix} \\begin{bmatrix}\n", 265 | " U_{\\text{red}} \\\\\n", 266 | " U_{\\text{black}}\n", 267 | " \\end{bmatrix} =\n", 268 | " \\begin{bmatrix}\n", 269 | " f_{\\text{red}} \\\\\n", 270 | " f_{\\text{black}}\n", 271 | " \\end{bmatrix}\n", 272 | "$$\n", 273 | "Here $D = -4 / \\Delta x^2 I$, a diagonal matrix of dimension $m^2 / 2$ and $H$ a *banded matrix* with 4 non-zero diagonals.\n", 274 | "\n", 275 | "Usually the goal with ordering is either to make the gaps between non-zero elements as small as possible and reduce the amount of \"fill-in\" when solving using say Gaussian elimination. This is often taken care of automatically when using sophisticated solving routines." 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": { 281 | "slideshow": { 282 | "slide_type": "subslide" 283 | } 284 | }, 285 | "source": [ 286 | "Solve the following PDE using the ordering of your choice:\n", 287 | "$$\n", 288 | " \\nabla^2 u = -2\\sin x \\sin y \\quad \\Omega = [0, 2 \\pi] \\times [0 , 2 \\pi] \\\\\n", 289 | " u(x,0) = 0, u(x, 2 \\pi) = 0, u(0, y) = 0, u(2 \\pi, y) = 0.\n", 290 | "$$" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "metadata": { 297 | "collapsed": true, 298 | "slideshow": { 299 | "slide_type": "skip" 300 | } 301 | }, 302 | "outputs": [], 303 | "source": [ 304 | "import scipy.sparse as sparse\n", 305 | "import scipy.sparse.linalg as linalg\n", 306 | "\n", 307 | "# Problem specification\n", 308 | "m = 100\n", 309 | "x = numpy.linspace(0, 2.0 * numpy.pi, m + 2)\n", 310 | "y = numpy.linspace(0, 2.0 * numpy.pi, m + 2)\n", 311 | "delta_x = 2.0 * numpy.pi / (m + 1)\n", 312 | "\n", 313 | "# Construct A\n", 314 | "e = numpy.ones(m)\n", 315 | "T = sparse.spdiags([e, -4.0 * e, e], [-1, 0, 1], m, m)\n", 316 | "S = sparse.spdiags([e, e], [-1, 1], m, m)\n", 317 | "I = sparse.eye(m)\n", 318 | "A = sparse.kron(I, T) + sparse.kron(S, I)\n", 319 | "A /= delta_x**2\n", 320 | "\n", 321 | "# Right-hand-side\n", 322 | "X, Y = numpy.meshgrid(x[1:-1], y[1:-1])\n", 323 | "f = -2.0 * numpy.sin(X) * numpy.sin(Y)\n", 324 | "\n", 325 | "# Solve\n", 326 | "U = numpy.zeros((m+2, m+2))\n", 327 | "U[1:-1, 1:-1] = linalg.spsolve(A, f.reshape(m**2, order='F')).reshape((m, m), order='F')\n", 328 | "\n", 329 | "# Error\n", 330 | "X, Y = numpy.meshgrid(x, y)\n", 331 | "print(numpy.linalg.norm((x[1] - x[0]) * (U - numpy.sin(X) * numpy.sin(Y)), ord=1))\n", 332 | "\n", 333 | "# Plot solution\n", 334 | "fig = plt.figure()\n", 335 | "axes = fig.add_subplot(1, 1, 1)\n", 336 | "sol_plot = axes.pcolor(X, Y, U, cmap=plt.get_cmap('RdBu_r'))\n", 337 | "axes.set_title(\"Solution u(x,y)\")\n", 338 | "axes.set_xlabel(\"x\")\n", 339 | "axes.set_ylabel(\"y\")\n", 340 | "axes.set_xlim((0.0, 2.0 * numpy.pi))\n", 341 | "axes.set_ylim((0.0, 2.0 * numpy.pi))\n", 342 | "cbar = fig.colorbar(sol_plot, ax=axes)\n", 343 | "cbar.set_label(\"u(x, y)\")\n", 344 | "\n", 345 | "fig = plt.figure()\n", 346 | "axes = fig.add_subplot(1, 1, 1)\n", 347 | "sol_plot = axes.pcolor(X, Y, numpy.abs(U - numpy.sin(X) * numpy.sin(Y)), cmap=plt.get_cmap('RdBu_r'))\n", 348 | "axes.set_title(\"Error |U - u|\")\n", 349 | "axes.set_xlabel(\"x\")\n", 350 | "axes.set_ylabel(\"y\")\n", 351 | "axes.set_xlim((0.0, 2.0 * numpy.pi))\n", 352 | "axes.set_ylim((0.0, 2.0 * numpy.pi))\n", 353 | "cbar = fig.colorbar(sol_plot, ax=axes)\n", 354 | "cbar.set_label(\"u(x, y)\")\n", 355 | "plt.show()" 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": { 361 | "slideshow": { 362 | "slide_type": "slide" 363 | } 364 | }, 365 | "source": [ 366 | "## Accuracy and Stability" 367 | ] 368 | }, 369 | { 370 | "cell_type": "markdown", 371 | "metadata": { 372 | "slideshow": { 373 | "slide_type": "subslide" 374 | } 375 | }, 376 | "source": [ 377 | "We can of course (and should) ask the same questions as with the one-dimensional case, namely whether our scheme will converge. To do this we need to consider the LTE and the stability of our method." 378 | ] 379 | }, 380 | { 381 | "cell_type": "markdown", 382 | "metadata": { 383 | "slideshow": { 384 | "slide_type": "subslide" 385 | } 386 | }, 387 | "source": [ 388 | "We know that the LTE is defined as\n", 389 | "$$\\begin{aligned}\n", 390 | " \\tau_{ij} &= \\frac{1}{\\Delta x^2} (u(x_{i-1}, y_{j}) + u(x_{i+1}, y_{j}) + u(x_{i}, y_{j-1}) + u(x_{i}, y_{j+1}) - 4 u(x_{i}, y_{j})) - f(x_i, y_j)\n", 391 | "\\end{aligned}$$" 392 | ] 393 | }, 394 | { 395 | "cell_type": "markdown", 396 | "metadata": { 397 | "slideshow": { 398 | "slide_type": "subslide" 399 | } 400 | }, 401 | "source": [ 402 | "To compute this expression we need the Taylor series in each direction.\n", 403 | "\n", 404 | "For the x-direction:\n", 405 | "$$\\begin{aligned}\n", 406 | " u(x_{i+1}, y_{j}) &= u(x_{i}, y_{j}) + \\Delta x u(x_{i}, y_{j})_x + \\frac{\\Delta x^2}{2} u(x_{i}, y_{j})_{xx} + \\frac{\\Delta x^3}{6} u(x_{i}, y_{j})_{xxx} + \\frac{\\Delta x^4}{24} u(x_{i}, y_{j})_{xxxx} + \\mathcal{O}(\\Delta x^5) \\\\\n", 407 | " u(x_{i-1}, y_{j}) &= u(x_{i}, y_{j}) - \\Delta x u(x_{i}, y_{j})_x + \\frac{\\Delta x^2}{2} u(x_{i}, y_{j})_{xx} - \\frac{\\Delta^3}{6} u(x_{i}, y_{j})_{xxx} + \\frac{\\Delta x^4}{24} u(x_{i}, y_{j})_{xxxx} + \\mathcal{O}(\\Delta x^5)\n", 408 | "\\end{aligned}$$\n", 409 | "\n", 410 | "For the y-direction:\n", 411 | "$$\\begin{aligned}\n", 412 | " u(x_{i}, y_{j+1}) &= u(x_{i}, y_{j}) + \\Delta y u(x_{i}, y_{j})_y + \\frac{\\Delta y^2}{2} u(x_{i}, y_{j})_{yy} + \\frac{\\Delta y^3}{6} u(x_{i}, y_{j})_{yyy} + \\frac{\\Delta y^4}{24} u(x_{i}, y_{j})_{yyyy} + \\mathcal{O}(\\Delta y^5) \\\\\n", 413 | " u(x_{i}, y_{j-1}) &= u(x_{i}, y_{j}) - \\Delta y u(x_{i}, y_{j})_y + \\frac{\\Delta y^2}{2} u(x_{i}, y_{j})_{yy} - \\frac{\\Delta y^3}{6} u(x_{i}, y_{j})_{yyy} + \\frac{\\Delta y^4}{24} u(x_{i}, y_{j})_{yyyy} + \\mathcal{O}(\\Delta y^5)\n", 414 | "\\end{aligned}$$" 415 | ] 416 | }, 417 | { 418 | "cell_type": "markdown", 419 | "metadata": { 420 | "slideshow": { 421 | "slide_type": "subslide" 422 | } 423 | }, 424 | "source": [ 425 | "Also using the Taylor expansions in the y-direction we can write the LTE as\n", 426 | "$$\n", 427 | " \\tau_{ij} = \\frac{1}{12} \\Delta x^2 (u_{xxxx} + u_{yyyy}) + \\mathcal{O}(\\Delta x^4).\n", 428 | "$$" 429 | ] 430 | }, 431 | { 432 | "cell_type": "markdown", 433 | "metadata": { 434 | "slideshow": { 435 | "slide_type": "subslide" 436 | } 437 | }, 438 | "source": [ 439 | "The linear system for the LTE then has the form\n", 440 | "$$\n", 441 | " A_{\\Delta x} E_{\\Delta x} = -\\tau_{\\Delta x}\n", 442 | "$$\n", 443 | "where now $A$ is the discretization we wrote before. Note that the ordering of the equations does not matter when considering the error." 444 | ] 445 | }, 446 | { 447 | "cell_type": "markdown", 448 | "metadata": { 449 | "slideshow": { 450 | "slide_type": "subslide" 451 | } 452 | }, 453 | "source": [ 454 | "For the stability in the 2-norm we again can consider the eigenvalues of the system above. The eigenvalues are\n", 455 | "$$\n", 456 | " \\lambda_{pq} = \\frac{2}{\\Delta x^2} ((\\cos(p \\pi \\Delta x) - 1) + (\\cos(q \\pi \\Delta x) - 1))\n", 457 | "$$\n", 458 | "with corresponding eigenvectors\n", 459 | "$$\n", 460 | " v^{p,q}_{ij} = \\sin(p \\pi i \\Delta x) \\sin(q \\pi j \\Delta x).\n", 461 | "$$" 462 | ] 463 | }, 464 | { 465 | "cell_type": "markdown", 466 | "metadata": { 467 | "slideshow": { 468 | "slide_type": "subslide" 469 | } 470 | }, 471 | "source": [ 472 | "Since the eigenvalues are strictly negative ($A$ is in fact negative definite) the closest one to the origin is\n", 473 | "$$\n", 474 | " \\lambda_{11} = -2 \\pi^2 + \\mathcal{O}(\\Delta x^2)\n", 475 | "$$\n", 476 | "leading to the spectral radius\n", 477 | "$$\n", 478 | " \\rho((A^{\\Delta x})^{-1}) = \\frac{1}{\\lambda_{11}} \\approx -\\frac{1}{2 \\pi^2}.\n", 479 | "$$\n", 480 | "We can use this bound on $A^{-1}$ then to show stability and hence convergence of the discretization." 481 | ] 482 | }, 483 | { 484 | "cell_type": "markdown", 485 | "metadata": { 486 | "slideshow": { 487 | "slide_type": "subslide" 488 | } 489 | }, 490 | "source": [ 491 | "A similar and useful quantity to consider is the *condition number* of the matrix $A$. Recall that this can be defined as\n", 492 | "$$\n", 493 | " \\kappa(A) = ||A|| ||A^{-1}||.\n", 494 | "$$" 495 | ] 496 | }, 497 | { 498 | "cell_type": "markdown", 499 | "metadata": { 500 | "slideshow": { 501 | "slide_type": "subslide" 502 | } 503 | }, 504 | "source": [ 505 | "In the 2-norm we already know some information about $A^{-1}$ but we can use our expressions from above to also find the spectral radius of $A$. The largest eigenvalue there is \n", 506 | "$$\n", 507 | " \\lambda_{mm} \\approx -\\frac{8}{\\Delta x^2}\n", 508 | "$$\n", 509 | "leading to the condition number\n", 510 | "$$\n", 511 | " \\kappa_2(A) = \\frac{4}{\\pi^2 \\Delta x^2} = \\mathcal{O}\\left(\\frac{1}{\\Delta x^2}\\right)\n", 512 | "$$\n", 513 | "This matrix therefore becomes more ill-conditioned as $\\Delta x \\rightarrow 0$." 514 | ] 515 | }, 516 | { 517 | "cell_type": "code", 518 | "execution_count": null, 519 | "metadata": { 520 | "collapsed": true, 521 | "slideshow": { 522 | "slide_type": "skip" 523 | } 524 | }, 525 | "outputs": [], 526 | "source": [ 527 | "# Compute the eigenvalues\n", 528 | "m = 10\n", 529 | "delta_x = 1.0 / float(m + 1)\n", 530 | "eig_vals = numpy.empty(m**2)\n", 531 | "for (i, p) in enumerate(numpy.arange(1, m + 1)):\n", 532 | " for (j, q) in enumerate(numpy.arange(1, m + 1)):\n", 533 | " eig_vals[i * m + j] = 2.0 / delta_x**2 * ((numpy.cos(p * numpy.pi * delta_x) - 1.0) +\n", 534 | " (numpy.cos(q * numpy.pi * delta_x) - 1.0))\n", 535 | "\n", 536 | "# Plot of the eigenvalues of the 5-point Laplacian\n", 537 | "fig = plt.figure()\n", 538 | "axes = fig.add_subplot(1, 1, 1)\n", 539 | "axes.plot(eig_vals.real, eig_vals.imag, 'o')\n", 540 | "axes.grid(True)\n", 541 | "axes.set_xlim(numpy.amin(eig_vals), -numpy.amin(eig_vals) * 0.1)\n", 542 | "plt.show()" 543 | ] 544 | }, 545 | { 546 | "cell_type": "markdown", 547 | "metadata": { 548 | "slideshow": { 549 | "slide_type": "slide" 550 | } 551 | }, 552 | "source": [ 553 | "## 9-Point Laplacian" 554 | ] 555 | }, 556 | { 557 | "cell_type": "markdown", 558 | "metadata": { 559 | "slideshow": { 560 | "slide_type": "subslide" 561 | } 562 | }, 563 | "source": [ 564 | "Another approach to discretizing the Laplacian $\\nabla^2$ is called the 9-point stencil. This stencil is defined as\n", 565 | "$$\n", 566 | " \\nabla^2 u_{ij} = \\frac{1}{6 \\Delta x^2} \\left(4 u_{i-1,j} + 4 u_{i+1,j} + 4 u_{i,j-1} + 4 u_{i,j+1} + u_{i-1,j-1} + u_{i+1,j-1} + u_{i+1,j+1} + u_{i-1,j+1} -20 u_{i,j} \\right).\n", 567 | "$$" 568 | ] 569 | }, 570 | { 571 | "cell_type": "markdown", 572 | "metadata": { 573 | "slideshow": { 574 | "slide_type": "subslide" 575 | } 576 | }, 577 | "source": [ 578 | "We can also find the LTE as before to find\n", 579 | "$$\n", 580 | " \\tau_{ij} = \\frac{\\Delta x^2}{12} (u_{xxxx} + 2u_{xxyy} + u_{yyyy}) + \\mathcal{O}(\\Delta x^4).\n", 581 | "$$" 582 | ] 583 | }, 584 | { 585 | "cell_type": "markdown", 586 | "metadata": { 587 | "slideshow": { 588 | "slide_type": "subslide" 589 | } 590 | }, 591 | "source": [ 592 | "So what did we gain? If we look at $\\tau_{ij}$ we can actually write the derivative terms as\n", 593 | "$$\n", 594 | " u_{xxxx} + 2u_{xxyy} + u_{yyyy} = \\nabla^2 (\\nabla^2 u) \\equiv \\nabla^4 u,\n", 595 | "$$\n", 596 | "i.e. the Laplacian of the Laplacian (called the *biharmonic operator*). If we return to the original PDE, we can directly find the form of this error by noting\n", 597 | "$$\\begin{aligned}\n", 598 | " \\nabla^2 u &= f \\\\\n", 599 | " \\nabla^2 (\\nabla^2 u) &= \\nabla^2 f\n", 600 | "\\end{aligned}$$\n", 601 | "We can therefore compute the error terms directly by taking the Laplacian of the right hand side of the original PDE!" 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "metadata": { 607 | "slideshow": { 608 | "slide_type": "subslide" 609 | } 610 | }, 611 | "source": [ 612 | "This also can lead to a higher-order method. If we define\n", 613 | "$$\n", 614 | " f_{ij} = f(x_i, y_j) + \\frac{\\Delta x^2}{12} \\nabla^2 f(x_i, y_j)\n", 615 | "$$\n", 616 | "we can cancel out the next term of the LTE! We can even do this if we only have the function $f(x,y)$ on a set of discrete points as we can use the 5-point stencil." 617 | ] 618 | } 619 | ], 620 | "metadata": { 621 | "celltoolbar": "Slideshow", 622 | "kernelspec": { 623 | "display_name": "Python 3", 624 | "language": "python", 625 | "name": "python3" 626 | }, 627 | "language_info": { 628 | "codemirror_mode": { 629 | "name": "ipython", 630 | "version": 3 631 | }, 632 | "file_extension": ".py", 633 | "mimetype": "text/x-python", 634 | "name": "python", 635 | "nbconvert_exporter": "python", 636 | "pygments_lexer": "ipython3", 637 | "version": "3.7.6" 638 | }, 639 | "latex_envs": { 640 | "bibliofile": "biblio.bib", 641 | "cite_by": "apalike", 642 | "current_citInitial": 1, 643 | "eqLabelWithNumbers": true, 644 | "eqNumInitial": 0 645 | } 646 | }, 647 | "nbformat": 4, 648 | "nbformat_minor": 1 649 | } 650 | -------------------------------------------------------------------------------- /12_mixed.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "skip" 8 | } 9 | }, 10 | "source": [ 11 | "\n", 12 | " \n", 14 | "
\n", 13 | " Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved MIT license. (c) Kyle T. Mandli
" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "slideshow": { 22 | "slide_type": "skip" 23 | } 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "from __future__ import print_function\n", 28 | "%matplotlib inline\n", 29 | "import numpy\n", 30 | "import matplotlib.pyplot as plt" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": { 36 | "slideshow": { 37 | "slide_type": "slide" 38 | } 39 | }, 40 | "source": [ 41 | "# Mixed Equations" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": { 47 | "slideshow": { 48 | "slide_type": "subslide" 49 | } 50 | }, 51 | "source": [ 52 | "We now explore how we might use the methods we have analyzed and developed to solve more complex equations that do not easily fall into one of the classifications for PDEs we have studied." 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": { 58 | "slideshow": { 59 | "slide_type": "subslide" 60 | } 61 | }, 62 | "source": [ 63 | "We will focus here on PDEs of the form\n", 64 | "$$\n", 65 | " u_t = \\mathcal{A}_1(u) + \\mathcal{A}_2(u) + \\cdots + \\mathcal{A}_N(u)\n", 66 | "$$\n", 67 | "where the $\\mathcal{A}_j(u)$ are functions of $u$ and its derivatives (also possibly non-linear)." 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": { 73 | "slideshow": { 74 | "slide_type": "subslide" 75 | } 76 | }, 77 | "source": [ 78 | "Since most of the methods we will discuss can be generalized from the case of only two operators $\\mathcal{A}_j$ we will focus our attention on the PDE\n", 79 | "$$\n", 80 | " u_t = \\mathcal{A}(u) + \\mathcal{B}(u).\n", 81 | "$$\n", 82 | "Let's now consider some examples of these types of equations." 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": { 88 | "slideshow": { 89 | "slide_type": "subslide" 90 | } 91 | }, 92 | "source": [ 93 | "### Example - Multidimensional Problems\n", 94 | "\n", 95 | "We saw already how we might approach multi-dimensional problems coupled with a time derivative. These also are considered mixed and many of the methods we will consider are applicable to multidimensional problems such as the heat equation\n", 96 | "$$\n", 97 | " u_t = \\kappa(u_{xx} + u_{yy})\n", 98 | "$$\n", 99 | "or a multidimensional hyperbolic PDE\n", 100 | "$$\n", 101 | " u_t + f(u)_x + g(u)_y = 0.\n", 102 | "$$" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "### Example - Reaction-Diffusion Equations\n", 110 | "\n", 111 | "We can add another term to the heat equation which often represents a chemical reaction term (also sometimes called a source or sync term) so that we have\n", 112 | "$$\n", 113 | " u_t = \\kappa u_{xx} + R(u).\n", 114 | "$$\n", 115 | "We may want to handle the term $R(u)$ differently than the diffusion term in case it has different temporal scales, maybe is not stiff, or is difficult to solve coupled to our approach to the heat equation." 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": { 121 | "slideshow": { 122 | "slide_type": "subslide" 123 | } 124 | }, 125 | "source": [ 126 | "### Example - Advection-Diffusion Equations\n", 127 | "\n", 128 | "We have also seen cases when considering numerical methods for advection that the modified equations can represent advection-diffusion systems of the form\n", 129 | "$$\n", 130 | " u_t + a u_x = \\kappa u_{xx}.\n", 131 | "$$" 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": { 137 | "slideshow": { 138 | "slide_type": "subslide" 139 | } 140 | }, 141 | "source": [ 142 | "It turns out these types of equations are much more prevalent than just this, in fact the Navier-Stokes equations are an example of a set of constrained advection-diffusion equations (constrained due to incompressibility). We also find nonlinear hyperbolic equations with viscous terms such as\n", 143 | "$$\n", 144 | " u_t + f(u)_x = \\kappa u_{xx}\n", 145 | "$$\n", 146 | "to be common when approximating fluid dynamics in general. The viscous Burgers equation\n", 147 | "$$\n", 148 | " u_t + u u_x = \\epsilon u_{xx}\n", 149 | "$$\n", 150 | "is another example." 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": { 156 | "slideshow": { 157 | "slide_type": "subslide" 158 | } 159 | }, 160 | "source": [ 161 | "### Example - Advection-Diffusion-Reaction Equations\n", 162 | "\n", 163 | "Why not combine all of the above?\n", 164 | "$$\n", 165 | " u_t + f(u)_x = \\kappa u_{xx} + R(u)\n", 166 | "$$\n", 167 | "These types of equations are common in cases of reactive fluid flow. Modeling combustion for instance usually involves 10-100 different reaction terms with widely disparate time scales making these very difficult problems to solve." 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": { 173 | "slideshow": { 174 | "slide_type": "subslide" 175 | } 176 | }, 177 | "source": [ 178 | "### Example - Advection-Dispersion\n", 179 | "\n", 180 | "We also saw modified equations in our study of hyperbolic PDEs that contained both advective and dispersive terms. Another example of this type of equation is the Kortweg-de Vries (KdV) equation\n", 181 | "$$\n", 182 | " u_t + u u_x = \\nu u_{xxx}.\n", 183 | "$$\n", 184 | "This equation can be derived from the Euler equations modeling incompressible fluid flow and represent a number of interesting phenomena, most notably soliton waves. A similar equation is the nonlinear Schrödinger equation\n", 185 | "$$\n", 186 | " i \\Psi_t(x,t) = -\\Psi_{xx}(x,t) + V(\\Psi)\n", 187 | "$$\n", 188 | "where $V(\\Psi)$ is a nonlinear potential." 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": { 194 | "slideshow": { 195 | "slide_type": "subslide" 196 | } 197 | }, 198 | "source": [ 199 | "### Example - Advection-Diffusion-Dispersion-Hyperdiffusion-...\n", 200 | "\n", 201 | "The Kuramoto-Sivashinsky equation\n", 202 | "$$\n", 203 | " u_t + \\frac{1}{2} (u_x)^2 = -u_{xx} - u_{xxxx}\n", 204 | "$$\n", 205 | "is another equation of interest. It would appear to be ill-posed and maybe even blow up due to the signs in front of the diffusion but it turns out this is not the case and appropriate damping is supplied by the transport term (the right hand-side) to stabilize the equation." 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": { 211 | "slideshow": { 212 | "slide_type": "slide" 213 | } 214 | }, 215 | "source": [ 216 | "## Fully Coupled Method of Lines\n", 217 | "\n", 218 | "The first approach we will study is one that we have introduced before. We assume that some spatial discretization is fully applied to all the spatial terms leading to a system of equations of the form\n", 219 | "$$\n", 220 | " U'(t) = F(U(t)).\n", 221 | "$$\n", 222 | "This approach can work and provides a lot of flexibility in terms of the order of accuracy and the stencils available but can run into problems when some terms on the right are stiff while others are not. The best example of this is the advection-diffusion equation unless the relative strength of the advection vs. the diffusion (called the Peclet number) highly favors one term or the other." 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": { 228 | "slideshow": { 229 | "slide_type": "slide" 230 | } 231 | }, 232 | "source": [ 233 | "## Fully Coupled Taylor Series Methods\n", 234 | "\n", 235 | "We can also utilize Taylor series to construct methods for mixed equations. Consider the Taylor expansion in time\n", 236 | "$$\n", 237 | " u(x, t + \\Delta t) \\approx u(x, t) + \\Delta t u_t + \\cdots,\n", 238 | "$$\n", 239 | "if we replace $u_t$ with the right hand side we create the method\n", 240 | "$$\n", 241 | " U^{n+1}_j = U^n_j + \\Delta t (A(U^n_j) + B(U^n_j))\n", 242 | "$$\n", 243 | "where $A$ and $B$ are appropriately discretized versions of $\\mathcal{A}$ and $\\mathcal{B}$." 244 | ] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "metadata": { 249 | "slideshow": { 250 | "slide_type": "subslide" 251 | } 252 | }, 253 | "source": [ 254 | "We can extend this to higher order by retaining more terms in the Taylor expansion. Consider the two-dimensional hyperbolic PDE\n", 255 | "$$\n", 256 | " u_t + a u_x + b u_y = 0.\n", 257 | "$$\n", 258 | "The truncated Taylor series to second order is\n", 259 | "$$\n", 260 | " u(x, t + \\Delta t) \\approx u(x, t) + \\Delta t u_t + \\frac{\\Delta t^2}{2} u_{tt} + \\cdots,\n", 261 | "$$\n", 262 | "so we need to compute the $u_{tt}$ term. " 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": { 268 | "slideshow": { 269 | "slide_type": "subslide" 270 | } 271 | }, 272 | "source": [ 273 | "Taking the derivative of the original equation we have\n", 274 | "$$\\begin{aligned}\n", 275 | " u_{tt} &= -a u_{xt} - b u_{yt} \\\\\n", 276 | " &= -a (u_t)_x - b (u_t)_y \\\\\n", 277 | " &= -a (-a u_x - b u_y)_x - b (-a u_x - b u_y)_y \\\\\n", 278 | " &= a^2 u_{xx} + a b u_{yx} + a b u_{xy} + b^2 u_{yy} \\\\\n", 279 | " &= a^2 u_{xx} + 2 a b u_{xy} + b^2 u_{yy}.\n", 280 | "\\end{aligned}$$\n", 281 | "If these were systems of hyperbolic PDEs $a$ and $b$ would be matrices and we would have the slightly different expression\n", 282 | "$$\n", 283 | " u_{tt} = A^2 u_{xx} + (A B + B A) u_{xy} + B^2 u_{yy}.\n", 284 | "$$" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": { 290 | "slideshow": { 291 | "slide_type": "subslide" 292 | } 293 | }, 294 | "source": [ 295 | "This leads to the method\n", 296 | "$$\n", 297 | " U^{n+1}_{ij} = U^n_{ij} - \\Delta t (A D_{x} U^n_{ij} + B D_{y} U^n_{ij}) + \\frac{1}{2} \\Delta t^2 (A^2 D^2_x U^n_{ij} + (A B + B A) D_x D_y U^n_{ij} + B^2 D^2_y U^n_{ij} )\n", 298 | "$$\n", 299 | "which is the two-dimensional Lax-Wendroff method if centered approximations are used to discretize the derivatives appropriately." 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": { 305 | "slideshow": { 306 | "slide_type": "slide" 307 | } 308 | }, 309 | "source": [ 310 | "## Fractional Step Methods\n", 311 | "\n", 312 | "One of the most common ways to deal with mixed equations is through a fractional step method (a.k.a. time-split, split-step, or operator splitting methods). " 313 | ] 314 | }, 315 | { 316 | "cell_type": "markdown", 317 | "metadata": { 318 | "slideshow": { 319 | "slide_type": "subslide" 320 | } 321 | }, 322 | "source": [ 323 | "In essence we split up the problem into two steps, one that solves the terms dealing with $\\mathcal{A}$ and another that deals with $\\mathcal{B}$:\n", 324 | "$$\n", 325 | " U^\\ast = \\mathcal{N}_A(U^n, \\Delta t) \\\\\n", 326 | " U^{n+1} = \\mathcal{N}_B(U^\\ast, \\Delta t)\n", 327 | "$$\n", 328 | "where $\\mathcal{N}_{A}$ and $\\mathcal{N}_{B}$ represent one-step numerical methods for solving\n", 329 | "$$\n", 330 | " u_t = \\mathcal{A}(u)\n", 331 | "$$\n", 332 | "and\n", 333 | "$$\n", 334 | " u_t = \\mathcal{B}(u)\n", 335 | "$$\n", 336 | "respectively. This first order accurate scheme is called *Godunov splitting*." 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": { 342 | "slideshow": { 343 | "slide_type": "subslide" 344 | } 345 | }, 346 | "source": [ 347 | "This approach has the advantage of being able to use whatever method is appropriate for each of the operators $\\mathcal{A}$ and $\\mathcal{B}$, say an explicit and an implicit method for instance. As presented above however this splitting is only 1st order accurate though is convergent as $\\Delta t \\rightarrow 0$ as long as the constituent methods are convergent on their individual problems. In practice the \"splitting\" error is small compared to the time and space direct discretizations and this approach works well." 348 | ] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": { 353 | "slideshow": { 354 | "slide_type": "subslide" 355 | } 356 | }, 357 | "source": [ 358 | "Multidimensional problems also can use fractional step splitting as we saw earlier. In this context it is usually called *dimensional splitting* and can be very effective over a fully multi-dimensional approach, especially for higher dimensions" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": { 364 | "slideshow": { 365 | "slide_type": "subslide" 366 | } 367 | }, 368 | "source": [ 369 | "### Error Analysis\n", 370 | "\n", 371 | "Let's now explore what kind of error is introduced due to our splitting error. Consider the simple ODE\n", 372 | "$$\n", 373 | " u_t = A u + B u\n", 374 | "$$\n", 375 | "where $A$ and $B$ are matrices." 376 | ] 377 | }, 378 | { 379 | "cell_type": "markdown", 380 | "metadata": { 381 | "slideshow": { 382 | "slide_type": "subslide" 383 | } 384 | }, 385 | "source": [ 386 | "Since we know the solution of this ODE we can single out the error produced by the splitting alone. Take the methods $\\mathcal{N}$ and set them to the exact solution of the split equations so that\n", 387 | "$$\n", 388 | " \\mathcal{N}_A(U, \\Delta t) = e^{A \\Delta t} U\n", 389 | "$$\n", 390 | "and\n", 391 | "$$\n", 392 | " \\mathcal{N}_B(U, \\Delta t) = e^{B \\Delta t} U.\n", 393 | "$$" 394 | ] 395 | }, 396 | { 397 | "cell_type": "markdown", 398 | "metadata": { 399 | "slideshow": { 400 | "slide_type": "subslide" 401 | } 402 | }, 403 | "source": [ 404 | "The fractional step approach will lead to the solution\n", 405 | "$$\\begin{aligned}\n", 406 | " U^\\ast &= e^{A \\Delta t} U^{n} \\Rightarrow \\\\\n", 407 | " U^{n+1} &= e^{B\\Delta t} U^\\ast = e^{B\\Delta t} e^{A \\Delta t} U^{n}.\n", 408 | "\\end{aligned}$$\n", 409 | "In essense $e^{B\\Delta t} e^{A \\Delta t}$ maps the initial condition at $t_n$ to the new time $t_{n+1}$ (called a solution operator)." 410 | ] 411 | }, 412 | { 413 | "cell_type": "markdown", 414 | "metadata": { 415 | "slideshow": { 416 | "slide_type": "subslide" 417 | } 418 | }, 419 | "source": [ 420 | "The exact solution to the original ODE is also\n", 421 | "$$\n", 422 | " u(t_n + \\Delta t) = e^{(A + B) \\Delta t} u(t_n)\n", 423 | "$$\n", 424 | "demonstrating that there is a difference in the approaches." 425 | ] 426 | }, 427 | { 428 | "cell_type": "markdown", 429 | "metadata": { 430 | "slideshow": { 431 | "slide_type": "subslide" 432 | } 433 | }, 434 | "source": [ 435 | "Taylor expanding the matrix exponentials of the exact solution (the solution operator) we see that we should have\n", 436 | "$$\n", 437 | " e^{(A + B) \\Delta t} = I + \\Delta t (A + B) + \\frac{\\Delta t^2}{2} (A+B)^2 + \\cdots\n", 438 | "$$\n", 439 | "while Taylor expanding the matrix exponentials in the fractional step approach leads to\n", 440 | "$$\\begin{aligned}\n", 441 | " e^{B\\Delta t} e^{A \\Delta t} &= \\left (I + \\Delta t B + \\frac{\\Delta t^2}{2} B^2 + \\cdots \\right ) \\left (I + \\Delta t A + \\frac{\\Delta t^2}{2} A^2 + \\cdots \\right ) \\\\\n", 442 | " &= I + \\Delta t (A + B) + \\frac{\\Delta t^2}{2} (A^2 + 2 B A + B^2) + \\cdots.\n", 443 | "\\end{aligned}$$" 444 | ] 445 | }, 446 | { 447 | "cell_type": "markdown", 448 | "metadata": { 449 | "slideshow": { 450 | "slide_type": "subslide" 451 | } 452 | }, 453 | "source": [ 454 | "Comparing the two expressions we see that the series are equivalent to the $\\mathcal{O}(\\Delta t^2)$ where the true solution has\n", 455 | "$$\n", 456 | " (A + B)^2 = A^2 + A B + B A + B^2\n", 457 | "$$\n", 458 | "where as the approximation is\n", 459 | "$$\n", 460 | " A^2 + 2 B A + B^2.\n", 461 | "$$\n", 462 | "Consequently subtracting the two expressions to find the error leaves us with \n", 463 | "$$\n", 464 | " e^{(A + B) \\Delta t} - e^{B \\Delta t} e^{A \\Delta t} = \\tau^n = \\frac{\\Delta t^2}{2} (A B - B A) + \\mathcal{O}(\\Delta t^3).\n", 465 | "$$" 466 | ] 467 | }, 468 | { 469 | "cell_type": "markdown", 470 | "metadata": { 471 | "slideshow": { 472 | "slide_type": "subslide" 473 | } 474 | }, 475 | "source": [ 476 | "This term in the middle is often called the commutator of the operators $A$ and $B$ denoted by $[A, B] = A B - B A$. If the commutator is 0 then the splitting is higher order accurate! Note that this is true always when solving a scalar equation, in this case the entire Taylor series are identical and we find that there is no error introduced by the splitting." 477 | ] 478 | }, 479 | { 480 | "cell_type": "markdown", 481 | "metadata": { 482 | "slideshow": { 483 | "slide_type": "subslide" 484 | } 485 | }, 486 | "source": [ 487 | "We can also formulate a second order method called *Strang splitting* which uses\n", 488 | "$$\n", 489 | " U^\\ast = \\mathcal{A}(U^n, \\Delta t / 2) \\\\\n", 490 | " U^{\\ast\\ast} = \\mathcal{B}(U^\\ast, \\Delta t) \\\\\n", 491 | " U^{n+1} = \\mathcal{A}(U^{\\ast\\ast}, \\Delta t / 2).\n", 492 | "$$\n", 493 | "Following the analysis above the solution operator is\n", 494 | "$$\n", 495 | " e^{A \\Delta t / 2} e^{B \\Delta t} e^{A \\Delta t /2}\n", 496 | "$$\n", 497 | "which agrees with the exact Taylor series regardless of the Taylor series up to third order." 498 | ] 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "metadata": { 503 | "slideshow": { 504 | "slide_type": "subslide" 505 | } 506 | }, 507 | "source": [ 508 | "This may seem like a lot of work but in practice if we are taking two steps we can combine two evaluations of the individual methods to show that except for the first step we can simply alternate steps on the numerical method:\n", 509 | "$$\\begin{aligned}\n", 510 | " U^{n+3} &= \\left (e^{A \\Delta t / 2} e^{B \\Delta t} e^{A \\Delta t /2} \\right)\\left (e^{A \\Delta t / 2} e^{B \\Delta t} e^{A \\Delta t /2} \\right)\\left (e^{A \\Delta t / 2} e^{B \\Delta t} e^{A \\Delta t /2} \\right) U^{n} \\\\\n", 511 | " &= e^{A \\Delta t / 2} e^{B \\Delta t} e^{A \\Delta t} e^{B \\Delta t} e^{A \\Delta t} e^{B \\Delta t} e^{A \\Delta t /2} U^{n}\n", 512 | "\\end{aligned}$$" 513 | ] 514 | }, 515 | { 516 | "cell_type": "markdown", 517 | "metadata": { 518 | "slideshow": { 519 | "slide_type": "subslide" 520 | } 521 | }, 522 | "source": [ 523 | "In terms of the multi-dimensional approach to the heat equation we discussed which has the form of Godunov splitting we saw that it was actually second order accurate. Just to refresh the equation is\n", 524 | "$$\n", 525 | " u_t = u_{xx} + u_{yy}\n", 526 | "$$\n", 527 | "with $\\mathcal{A} = u_{xx}$ and $\\mathcal{B} = u_{yy}$. Since these operators commute with each other we see second order accurate approximations." 528 | ] 529 | }, 530 | { 531 | "cell_type": "markdown", 532 | "metadata": { 533 | "slideshow": { 534 | "slide_type": "subslide" 535 | } 536 | }, 537 | "source": [ 538 | "Consider instead though what would happen if we have a spatial varying diffusion constant such that\n", 539 | "$$\n", 540 | " u_t = (\\kappa(x,y)u_x)_x + (\\kappa(x,y)u_y)_y\n", 541 | "$$\n", 542 | "so that $\\mathcal{A} = (\\kappa(x,y)u_x)_x$ and $\\mathcal{B} = (\\kappa(x,y)u_y)_y$. These no longer commute and the method as formulated is only formally first order accurate." 543 | ] 544 | }, 545 | { 546 | "cell_type": "markdown", 547 | "metadata": { 548 | "slideshow": { 549 | "slide_type": "subslide" 550 | } 551 | }, 552 | "source": [ 553 | "One final caveat to fractional splitting is that the boundary conditions can be non-trivial to implement. This arises due to the need for specifying a boundary condition at the intermediate stages and in general can be an issue in general if there are any time-dependent factors in the equation as what time to evaluate these functions at for the intermediate stages is not obvious." 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "execution_count": null, 559 | "metadata": { 560 | "slideshow": { 561 | "slide_type": "skip" 562 | } 563 | }, 564 | "outputs": [], 565 | "source": [ 566 | "# Solve the advection-diffusion equation u_t + u_x = u_{xx}\n", 567 | "import scipy.sparse as sparse\n", 568 | "import scipy.sparse.linalg as linalg\n", 569 | "\n", 570 | "def CN_step(delta_x, delta_t, t_0, U_0, kappa=1.0):\n", 571 | " \"\"\"Solve the heat equation u_t = \\kappa u_{xx}\"\"\"\n", 572 | " \n", 573 | " # Build solving matrix\n", 574 | " m = U_0.shape[0]\n", 575 | " r = 0.5 * kappa * delta_t / delta_x**2\n", 576 | " e = numpy.ones(m + 1) * r\n", 577 | " D2 = sparse.spdiags([e ,-2.0 * e, e], [-1, 0, 1], m, m).tolil()\n", 578 | " D2[0, -1] = r\n", 579 | " D2[-1, 0] = r\n", 580 | " I = sparse.eye(m)\n", 581 | " A1 = (I - D2).tocsr()\n", 582 | " A2 = (I + D2).tolil()\n", 583 | " \n", 584 | " # Construct right-hand side\n", 585 | " b = A2.dot(U_0.copy())\n", 586 | " \n", 587 | " # Solve system\n", 588 | " return linalg.spsolve(A1, b)\n", 589 | "\n", 590 | "def LW_step(delta_x, delta_t, t_0, U_0, a):\n", 591 | " \"\"\"Solve the advection equation u_t + a u_x = 0\"\"\"\n", 592 | "\n", 593 | " U = numpy.empty(U_0.shape)\n", 594 | " \n", 595 | " U[0] = U_0[0] - a * delta_t / (2.0 * delta_x) * (U_0[1] - U_0[-1]) \\\n", 596 | " + a * delta_t**2 / (2.0 * delta_x**2) * (U_0[1] - 2.0 * U_0[0] + U_0[-1])\n", 597 | " U[1:-1] = U_0[1:-1] - a * delta_t / (2.0 * delta_x) * (U_0[2:] - U_0[:-2]) \\\n", 598 | " + a * delta_t**2 / (2.0 * delta_x**2) * (U_0[2:] - 2.0 * U_0[1:-1] + U_0[:-2])\n", 599 | " U[-1] = U_0[-1] - a * delta_t / (2.0 * delta_x) * (U_0[0] - U_0[-2]) \\\n", 600 | " + a * delta_t**2 / (2.0 * delta_x**2) * (U_0[0] - 2.0 * U_0[-1] + U_0[-2])\n", 601 | " \n", 602 | " return U\n", 603 | "\n", 604 | "# Problem specification\n", 605 | "L = 25.0\n", 606 | "a = 5.0\n", 607 | "kappa = 0.5\n", 608 | "\n", 609 | "# Spatial discretization\n", 610 | "m = 250\n", 611 | "x = numpy.linspace(0.0, L, m)\n", 612 | "delta_x = L / (m + 1.0)\n", 613 | " \n", 614 | "# Time discretization\n", 615 | "C = 1.0\n", 616 | "delta_t = C * delta_x / a\n", 617 | "\n", 618 | "# Initial Condition\n", 619 | "U = numpy.exp(-20.0 * (x - 2.0)**2) + numpy.exp(-(x - 5.0)**2)\n", 620 | "\n", 621 | "# Godunov operator-splitting\n", 622 | "t = 0.0\n", 623 | "TOLERANCE = 1e-8\n", 624 | "output_times = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]\n", 625 | "\n", 626 | "# output_times = (10.0 * delta_t, 100.0 * delta_t, 200.0 * delta_t, 300.0 * delta_t, 400.0 * delta_t)\n", 627 | "N = int(numpy.ceil((len(output_times) + 1) / 2.0))\n", 628 | "fig = plt.figure()\n", 629 | "fig.set_figwidth(fig.get_figwidth() * N)\n", 630 | "fig.set_figheight(fig.get_figheight() * N)\n", 631 | "axes = fig.add_subplot(N, 2, 1)\n", 632 | "axes.plot(x, U, 'ro')\n", 633 | "axes.set_ylim((-0.3, 1.1))\n", 634 | "axes.set_title(\"t = %s\" % 0.0)\n", 635 | "\n", 636 | "for (n, t_final) in enumerate(output_times):\n", 637 | " while t_final - t > TOLERANCE:\n", 638 | " \n", 639 | " # Take time step on Crank-Nickolson\n", 640 | " U = CN_step(delta_x, delta_t, t, U, kappa)\n", 641 | " \n", 642 | " # Take time step on Lax-Wendroff\n", 643 | " U = LW_step(delta_x, delta_t, t, U, a)\n", 644 | "\n", 645 | " \n", 646 | " # Complete time step and choose new delta_t based on CFL from advection step\n", 647 | " t += delta_t\n", 648 | " delta_t = min(delta_x / a, t_final - t)\n", 649 | " \n", 650 | " axes = fig.add_subplot(N, 2, n + 2)\n", 651 | " axes.plot(x, U, 'ro')\n", 652 | " axes.set_ylim((-0.3, 1.1))\n", 653 | " axes.set_title(\"t = %s\" % t)" 654 | ] 655 | }, 656 | { 657 | "cell_type": "markdown", 658 | "metadata": { 659 | "slideshow": { 660 | "slide_type": "slide" 661 | } 662 | }, 663 | "source": [ 664 | "## Implicit-Explicit (IMEX) Methods \n", 665 | "\n", 666 | "Suppose that one of our operators $\\mathcal{A}(u)$ represents stiff terms that we really would rather integrate using an implicit method while $\\mathcal{B}(u)$ is non-stiff and we would like to apply an explicit method. Instead of employing a fractional step method we can an *implicit-explicit* (IMEX) method. These have the advantage that they are fully coupled (hence no splitting error) and are designed to be able to choose which terms to treat explicitly and which implicitly." 667 | ] 668 | }, 669 | { 670 | "cell_type": "markdown", 671 | "metadata": { 672 | "slideshow": { 673 | "slide_type": "subslide" 674 | } 675 | }, 676 | "source": [ 677 | "One simple example of this is combining backward and forward Euler, evaluating the non-stiff terms at $U^n$ and the stiff terms at $U^{n+1}$ to find\n", 678 | "$$\n", 679 | " U^{n+1} = U^n + \\Delta t (\\mathcal{A}(U^{n+1}) + \\mathcal{B}(U^n)).\n", 680 | "$$" 681 | ] 682 | }, 683 | { 684 | "cell_type": "markdown", 685 | "metadata": { 686 | "slideshow": { 687 | "slide_type": "subslide" 688 | } 689 | }, 690 | "source": [ 691 | "Another example that is second-order using Adams-Bashforth for the explicit component and the trapezoidal rule for the implicit leads to\n", 692 | "$$\n", 693 | " U^{n+1} = U^n + \\frac{\\Delta t}{2} \\left(\\mathcal{A}(U^n) + \\mathcal{A}(U^{n+1}) + 3 \\mathcal{B}(U^n) - \\mathcal{B}(U^{n-1}) \\right ).\n", 694 | "$$" 695 | ] 696 | }, 697 | { 698 | "cell_type": "markdown", 699 | "metadata": { 700 | "slideshow": { 701 | "slide_type": "subslide" 702 | } 703 | }, 704 | "source": [ 705 | "In general we can combine many of the methods we have studied to form these types of methods including both multi-step methods like the previous example and Runge-Kutta like, multi-stage methods." 706 | ] 707 | }, 708 | { 709 | "cell_type": "markdown", 710 | "metadata": { 711 | "slideshow": { 712 | "slide_type": "slide" 713 | } 714 | }, 715 | "source": [ 716 | "## Exponential Time Differencing Methods\n", 717 | "\n", 718 | "For the final method we will look at consider the general nonlinear ODE\n", 719 | "$$\n", 720 | " u' = f(u)\n", 721 | "$$\n", 722 | "and over the time interval $t \\in [t_n, t_{n+1}]$ we write this ODE as\n", 723 | "$$\n", 724 | " u'(t) = A_n u(t) + \\mathcal{B}_n(u(t))\n", 725 | "$$\n", 726 | "where our notation suggests that $A_n$ is a matrix and therefore the first term is linear while the second term may not be and in general contains the nonlinearities from the original ODE." 727 | ] 728 | }, 729 | { 730 | "cell_type": "markdown", 731 | "metadata": { 732 | "slideshow": { 733 | "slide_type": "subslide" 734 | } 735 | }, 736 | "source": [ 737 | "The basic idea of *exponential time differencing* (ETD) methods is to apply exact solution techniques (such as Duhamel's principle) to handle the linear part and a numerical method for the nonlinear part. It is often common to let the linear part contain the stiff components of the equation (such as if we discretized a diffusion operator) and then use an explicit method to handle $\\mathcal{B}_n$." 738 | ] 739 | }, 740 | { 741 | "cell_type": "markdown", 742 | "metadata": { 743 | "slideshow": { 744 | "slide_type": "subslide" 745 | } 746 | }, 747 | "source": [ 748 | "We can arrive at this splitting in two common ways:\n", 749 | "1. Let $A_n = f'(U^n)$ (the Jacobian) and then let\n", 750 | "$$\n", 751 | " \\mathcal{B}_n(u) = f(u) - A_n u.\n", 752 | "$$\n", 753 | "1. If we know more about the structure of $f(u)$ we can leverage its structure to build $A_n$ and $\\mathcal{B}_n$. Consider for example if we had an advection-diffusion problem, if we discretize only the diffusion operator and set this to $A_n$ and let the (possibly nonlinear) advection terms be $\\mathcal{B}$. The same applies to reaction-diffusion for non-stiff reaction terms." 754 | ] 755 | }, 756 | { 757 | "cell_type": "markdown", 758 | "metadata": { 759 | "slideshow": { 760 | "slide_type": "subslide" 761 | } 762 | }, 763 | "source": [ 764 | "From here we compute the matrix exponential to solve the $A_n$ part of the problem and use this to evaluate the explicit problem via a numerical method. Note that if $A_n$ is constant we can actually compute the matrix exponential once and continue to apply it without any extra computation. " 765 | ] 766 | }, 767 | { 768 | "cell_type": "markdown", 769 | "metadata": { 770 | "slideshow": { 771 | "slide_type": "subslide" 772 | } 773 | }, 774 | "source": [ 775 | "So how does this work? Consider Duhamel's principle in the case we have proposed:\n", 776 | "$$\n", 777 | " u(t_{n+1}) = e^{A_n \\Delta t} u(t_n) + \\int^{t_{n+1}}_{t_n} e^{A_n (t_{n+1} - \\tau)} \\mathcal{B}_n(u(\\tau)) d\\tau.\n", 778 | "$$\n", 779 | "We must approximate the integral due to $\\mathcal{B}_n(u(\\tau))$. There are of course multiple ways to approach the approximation to varying orders but let's consider the first order method where we simply evaluate $\\mathcal{B}_n(U^n)$ and pull it out of the integral and evaluate the matrix exponential of the integral to find\n", 780 | "$$\\begin{aligned}\n", 781 | " \\int^{t_{n+1}}_{t_n} e^{A_n (t_{n+1} - \\tau)} d\\tau &= \\Delta t + \\frac{\\Delta t^2}{2} A_n + \\frac{\\Delta t^3}{6} A^2_n + \\cdots \\\\\n", 782 | " &= A^{-1}_n \\left( e^{A_n \\Delta t} - I \\right)\n", 783 | "\\end{aligned}$$\n", 784 | "assuming $A_n$ is non-singular." 785 | ] 786 | }, 787 | { 788 | "cell_type": "markdown", 789 | "metadata": { 790 | "slideshow": { 791 | "slide_type": "subslide" 792 | } 793 | }, 794 | "source": [ 795 | "From here we can obtain the numerical method\n", 796 | "$$\n", 797 | " U^{n+1} = e^{A_n \\Delta t} U^n + A^{-1}_n \\left( e^{A_n \\Delta t} - I \\right) \\mathcal{B}_n(U^n).\n", 798 | "$$\n", 799 | "In the first, more general case above we can write \n", 800 | "$$\n", 801 | " \\mathcal{B}(U^n) = f(U^n) - A_n U^n\n", 802 | "$$\n", 803 | "we can rewrite the above as\n", 804 | "$$\n", 805 | " U^{n+1} = U^n + A^{-1}_n \\left( e^{A_n \\Delta t} - I \\right) f(U^n).\n", 806 | "$$\n", 807 | "Here we can also identify this as related to forward Euler as the case where $A_n = 0$ reduces to exactly forward Euler." 808 | ] 809 | }, 810 | { 811 | "cell_type": "markdown", 812 | "metadata": { 813 | "slideshow": { 814 | "slide_type": "subslide" 815 | } 816 | }, 817 | "source": [ 818 | "We can compute the truncation error as\n", 819 | "$$\\begin{aligned}\n", 820 | " \\tau^n &= \\left(\\frac{u(t_{n+1}) - u(t_n)}{\\Delta t}\\right ) - \\frac{1}{\\Delta t} A^{-1}_n \\left(e^{A_n \\Delta t} - I \\right ) u'(t_n) \\\\\n", 821 | " &= \\left[ u'(t_n) + \\frac{\\Delta t}{2} u''(t_n) + \\frac{\\Delta t^2}{6} u'''(t_n) + \\cdots \\right ] - \\left[ I + \\frac{\\Delta t}{2} A_n + \\frac{\\Delta t^2}{6} A^2_n + \\cdots \\right ] u'(t_n) \\\\\n", 822 | " &= \\frac{\\Delta t}{2} (u''(t_n) - A_n u'(t_n)) + \\frac{\\Delta t^2}{6} (u'''(t_n) - A_n u'(t_n)) + \\cdots \\\\\n", 823 | " &= \\frac{\\Delta t}{2} (f'(u(t_n)) - A_n) u'(t_n) + \\mathcal{O}(\\Delta t^2)\n", 824 | "\\end{aligned}$$\n", 825 | "therefore the method is second order accurate if $A_n \\equiv f'(U^n)$." 826 | ] 827 | }, 828 | { 829 | "cell_type": "markdown", 830 | "metadata": { 831 | "slideshow": { 832 | "slide_type": "subslide" 833 | } 834 | }, 835 | "source": [ 836 | "We can of course use higher-order methods to approximate the integral including multi-step methods (i.e. using an interpolating polynomial in time) or multi-stage Runge-Kutta like methods." 837 | ] 838 | }, 839 | { 840 | "cell_type": "markdown", 841 | "metadata": { 842 | "slideshow": { 843 | "slide_type": "subslide" 844 | } 845 | }, 846 | "source": [ 847 | "Equations that have dispersive terms in them, such as the KdV equation\n", 848 | "$$\n", 849 | " u_t + uu_x = u_{xxx}\n", 850 | "$$\n", 851 | "can be particularly amenable to this approach.\n", 852 | "Note that here the linear operator would be the dispersive term and using the ETD approach, trying to solve this term exactly, has a huge advantage over attempting to solve this term numerically." 853 | ] 854 | } 855 | ], 856 | "metadata": { 857 | "celltoolbar": "Slideshow", 858 | "kernelspec": { 859 | "display_name": "Python 3", 860 | "language": "python", 861 | "name": "python3" 862 | }, 863 | "language_info": { 864 | "codemirror_mode": { 865 | "name": "ipython", 866 | "version": 3 867 | }, 868 | "file_extension": ".py", 869 | "mimetype": "text/x-python", 870 | "name": "python", 871 | "nbconvert_exporter": "python", 872 | "pygments_lexer": "ipython3", 873 | "version": "3.7.6" 874 | }, 875 | "latex_envs": { 876 | "bibliofile": "biblio.bib", 877 | "cite_by": "apalike", 878 | "current_citInitial": 1, 879 | "eqLabelWithNumbers": true, 880 | "eqNumInitial": 0 881 | } 882 | }, 883 | "nbformat": 4, 884 | "nbformat_minor": 1 885 | } 886 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution 4.0 International Public License 58 | 59 | By exercising the Licensed Rights (defined below), You accept and agree 60 | to be bound by the terms and conditions of this Creative Commons 61 | Attribution 4.0 International Public License ("Public License"). To the 62 | extent this Public License may be interpreted as a contract, You are 63 | granted the Licensed Rights in consideration of Your acceptance of 64 | these terms and conditions, and the Licensor grants You such rights in 65 | consideration of benefits the Licensor receives from making the 66 | Licensed Material available under these terms and conditions. 67 | 68 | 69 | Section 1 -- Definitions. 70 | 71 | a. Adapted Material means material subject to Copyright and Similar 72 | Rights that is derived from or based upon the Licensed Material 73 | and in which the Licensed Material is translated, altered, 74 | arranged, transformed, or otherwise modified in a manner requiring 75 | permission under the Copyright and Similar Rights held by the 76 | Licensor. For purposes of this Public License, where the Licensed 77 | Material is a musical work, performance, or sound recording, 78 | Adapted Material is always produced where the Licensed Material is 79 | synched in timed relation with a moving image. 80 | 81 | b. Adapter's License means the license You apply to Your Copyright 82 | and Similar Rights in Your contributions to Adapted Material in 83 | accordance with the terms and conditions of this Public License. 84 | 85 | c. Copyright and Similar Rights means copyright and/or similar rights 86 | closely related to copyright including, without limitation, 87 | performance, broadcast, sound recording, and Sui Generis Database 88 | Rights, without regard to how the rights are labeled or 89 | categorized. For purposes of this Public License, the rights 90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 91 | Rights. 92 | 93 | d. Effective Technological Measures means those measures that, in the 94 | absence of proper authority, may not be circumvented under laws 95 | fulfilling obligations under Article 11 of the WIPO Copyright 96 | Treaty adopted on December 20, 1996, and/or similar international 97 | agreements. 98 | 99 | e. Exceptions and Limitations means fair use, fair dealing, and/or 100 | any other exception or limitation to Copyright and Similar Rights 101 | that applies to Your use of the Licensed Material. 102 | 103 | f. Licensed Material means the artistic or literary work, database, 104 | or other material to which the Licensor applied this Public 105 | License. 106 | 107 | g. Licensed Rights means the rights granted to You subject to the 108 | terms and conditions of this Public License, which are limited to 109 | all Copyright and Similar Rights that apply to Your use of the 110 | Licensed Material and that the Licensor has authority to license. 111 | 112 | h. Licensor means the individual(s) or entity(ies) granting rights 113 | under this Public License. 114 | 115 | i. Share means to provide material to the public by any means or 116 | process that requires permission under the Licensed Rights, such 117 | as reproduction, public display, public performance, distribution, 118 | dissemination, communication, or importation, and to make material 119 | available to the public including in ways that members of the 120 | public may access the material from a place and at a time 121 | individually chosen by them. 122 | 123 | j. Sui Generis Database Rights means rights other than copyright 124 | resulting from Directive 96/9/EC of the European Parliament and of 125 | the Council of 11 March 1996 on the legal protection of databases, 126 | as amended and/or succeeded, as well as other essentially 127 | equivalent rights anywhere in the world. 128 | 129 | k. You means the individual or entity exercising the Licensed Rights 130 | under this Public License. Your has a corresponding meaning. 131 | 132 | 133 | Section 2 -- Scope. 134 | 135 | a. License grant. 136 | 137 | 1. Subject to the terms and conditions of this Public License, 138 | the Licensor hereby grants You a worldwide, royalty-free, 139 | non-sublicensable, non-exclusive, irrevocable license to 140 | exercise the Licensed Rights in the Licensed Material to: 141 | 142 | a. reproduce and Share the Licensed Material, in whole or 143 | in part; and 144 | 145 | b. produce, reproduce, and Share Adapted Material. 146 | 147 | 2. Exceptions and Limitations. For the avoidance of doubt, where 148 | Exceptions and Limitations apply to Your use, this Public 149 | License does not apply, and You do not need to comply with 150 | its terms and conditions. 151 | 152 | 3. Term. The term of this Public License is specified in Section 153 | 6(a). 154 | 155 | 4. Media and formats; technical modifications allowed. The 156 | Licensor authorizes You to exercise the Licensed Rights in 157 | all media and formats whether now known or hereafter created, 158 | and to make technical modifications necessary to do so. The 159 | Licensor waives and/or agrees not to assert any right or 160 | authority to forbid You from making technical modifications 161 | necessary to exercise the Licensed Rights, including 162 | technical modifications necessary to circumvent Effective 163 | Technological Measures. For purposes of this Public License, 164 | simply making modifications authorized by this Section 2(a) 165 | (4) never produces Adapted Material. 166 | 167 | 5. Downstream recipients. 168 | 169 | a. Offer from the Licensor -- Licensed Material. Every 170 | recipient of the Licensed Material automatically 171 | receives an offer from the Licensor to exercise the 172 | Licensed Rights under the terms and conditions of this 173 | Public License. 174 | 175 | b. No downstream restrictions. You may not offer or impose 176 | any additional or different terms or conditions on, or 177 | apply any Effective Technological Measures to, the 178 | Licensed Material if doing so restricts exercise of the 179 | Licensed Rights by any recipient of the Licensed 180 | Material. 181 | 182 | 6. No endorsement. Nothing in this Public License constitutes or 183 | may be construed as permission to assert or imply that You 184 | are, or that Your use of the Licensed Material is, connected 185 | with, or sponsored, endorsed, or granted official status by, 186 | the Licensor or others designated to receive attribution as 187 | provided in Section 3(a)(1)(A)(i). 188 | 189 | b. Other rights. 190 | 191 | 1. Moral rights, such as the right of integrity, are not 192 | licensed under this Public License, nor are publicity, 193 | privacy, and/or other similar personality rights; however, to 194 | the extent possible, the Licensor waives and/or agrees not to 195 | assert any such rights held by the Licensor to the limited 196 | extent necessary to allow You to exercise the Licensed 197 | Rights, but not otherwise. 198 | 199 | 2. Patent and trademark rights are not licensed under this 200 | Public License. 201 | 202 | 3. To the extent possible, the Licensor waives any right to 203 | collect royalties from You for the exercise of the Licensed 204 | Rights, whether directly or through a collecting society 205 | under any voluntary or waivable statutory or compulsory 206 | licensing scheme. In all other cases the Licensor expressly 207 | reserves any right to collect such royalties. 208 | 209 | 210 | Section 3 -- License Conditions. 211 | 212 | Your exercise of the Licensed Rights is expressly made subject to the 213 | following conditions. 214 | 215 | a. Attribution. 216 | 217 | 1. If You Share the Licensed Material (including in modified 218 | form), You must: 219 | 220 | a. retain the following if it is supplied by the Licensor 221 | with the Licensed Material: 222 | 223 | i. identification of the creator(s) of the Licensed 224 | Material and any others designated to receive 225 | attribution, in any reasonable manner requested by 226 | the Licensor (including by pseudonym if 227 | designated); 228 | 229 | ii. a copyright notice; 230 | 231 | iii. a notice that refers to this Public License; 232 | 233 | iv. a notice that refers to the disclaimer of 234 | warranties; 235 | 236 | v. a URI or hyperlink to the Licensed Material to the 237 | extent reasonably practicable; 238 | 239 | b. indicate if You modified the Licensed Material and 240 | retain an indication of any previous modifications; and 241 | 242 | c. indicate the Licensed Material is licensed under this 243 | Public License, and include the text of, or the URI or 244 | hyperlink to, this Public License. 245 | 246 | 2. You may satisfy the conditions in Section 3(a)(1) in any 247 | reasonable manner based on the medium, means, and context in 248 | which You Share the Licensed Material. For example, it may be 249 | reasonable to satisfy the conditions by providing a URI or 250 | hyperlink to a resource that includes the required 251 | information. 252 | 253 | 3. If requested by the Licensor, You must remove any of the 254 | information required by Section 3(a)(1)(A) to the extent 255 | reasonably practicable. 256 | 257 | 4. If You Share Adapted Material You produce, the Adapter's 258 | License You apply must not prevent recipients of the Adapted 259 | Material from complying with this Public License. 260 | 261 | 262 | Section 4 -- Sui Generis Database Rights. 263 | 264 | Where the Licensed Rights include Sui Generis Database Rights that 265 | apply to Your use of the Licensed Material: 266 | 267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 268 | to extract, reuse, reproduce, and Share all or a substantial 269 | portion of the contents of the database; 270 | 271 | b. if You include all or a substantial portion of the database 272 | contents in a database in which You have Sui Generis Database 273 | Rights, then the database in which You have Sui Generis Database 274 | Rights (but not its individual contents) is Adapted Material; and 275 | 276 | c. You must comply with the conditions in Section 3(a) if You Share 277 | all or a substantial portion of the contents of the database. 278 | 279 | For the avoidance of doubt, this Section 4 supplements and does not 280 | replace Your obligations under this Public License where the Licensed 281 | Rights include other Copyright and Similar Rights. 282 | 283 | 284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 285 | 286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 296 | 297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 306 | 307 | c. The disclaimer of warranties and limitation of liability provided 308 | above shall be interpreted in a manner that, to the extent 309 | possible, most closely approximates an absolute disclaimer and 310 | waiver of all liability. 311 | 312 | 313 | Section 6 -- Term and Termination. 314 | 315 | a. This Public License applies for the term of the Copyright and 316 | Similar Rights licensed here. However, if You fail to comply with 317 | this Public License, then Your rights under this Public License 318 | terminate automatically. 319 | 320 | b. Where Your right to use the Licensed Material has terminated under 321 | Section 6(a), it reinstates: 322 | 323 | 1. automatically as of the date the violation is cured, provided 324 | it is cured within 30 days of Your discovery of the 325 | violation; or 326 | 327 | 2. upon express reinstatement by the Licensor. 328 | 329 | For the avoidance of doubt, this Section 6(b) does not affect any 330 | right the Licensor may have to seek remedies for Your violations 331 | of this Public License. 332 | 333 | c. For the avoidance of doubt, the Licensor may also offer the 334 | Licensed Material under separate terms or conditions or stop 335 | distributing the Licensed Material at any time; however, doing so 336 | will not terminate this Public License. 337 | 338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 339 | License. 340 | 341 | 342 | Section 7 -- Other Terms and Conditions. 343 | 344 | a. The Licensor shall not be bound by any additional or different 345 | terms or conditions communicated by You unless expressly agreed. 346 | 347 | b. Any arrangements, understandings, or agreements regarding the 348 | Licensed Material not stated herein are separate from and 349 | independent of the terms and conditions of this Public License. 350 | 351 | 352 | Section 8 -- Interpretation. 353 | 354 | a. For the avoidance of doubt, this Public License does not, and 355 | shall not be interpreted to, reduce, limit, restrict, or impose 356 | conditions on any use of the Licensed Material that could lawfully 357 | be made without permission under this Public License. 358 | 359 | b. To the extent possible, if any provision of this Public License is 360 | deemed unenforceable, it shall be automatically reformed to the 361 | minimum extent necessary to make it enforceable. If the provision 362 | cannot be reformed, it shall be severed from this Public License 363 | without affecting the enforceability of the remaining terms and 364 | conditions. 365 | 366 | c. No term or condition of this Public License will be waived and no 367 | failure to comply consented to unless expressly agreed to by the 368 | Licensor. 369 | 370 | d. Nothing in this Public License constitutes or may be interpreted 371 | as a limitation upon, or waiver of, any privileges and immunities 372 | that apply to the Licensor or You, including from the legal 373 | processes of any jurisdiction or authority. 374 | 375 | 376 | ======================================================================= 377 | 378 | Creative Commons is not a party to its public 379 | licenses. Notwithstanding, Creative Commons may elect to apply one of 380 | its public licenses to material it publishes and in those instances 381 | will be considered the “Licensor.” The text of the Creative Commons 382 | public licenses is dedicated to the public domain under the CC0 Public 383 | Domain Dedication. Except for the limited purpose of indicating that 384 | material is shared under a Creative Commons public license or as 385 | otherwise permitted by the Creative Commons policies published at 386 | creativecommons.org/policies, Creative Commons does not authorize the 387 | use of the trademark "Creative Commons" or any other trademark or logo 388 | of Creative Commons without its prior written consent including, 389 | without limitation, in connection with any unauthorized modifications 390 | to any of its public licenses or any other arrangements, 391 | understandings, or agreements concerning use of licensed material. For 392 | the avoidance of doubt, this paragraph does not form part of the 393 | public licenses. 394 | 395 | Creative Commons may be contacted at creativecommons.org. 396 | -------------------------------------------------------------------------------- /MIT_LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kyle T. Mandli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Numerical Methods for PDEs 2 | 3 | Lecture notes for Numerical Methods for PDEs at Columbia. Text and figures are 4 | licensed under a Creative Commons
 6 | License
Creative Commons Attribution 9 | 4.0 International License. Code is licensed under an MIT license. 10 | 11 | [![Build Status](https://travis-ci.org/mandli/numerical-methods-pdes.svg?branch=master)](https://travis-ci.org/mandli/numerical-methods-pdes) 12 | -------------------------------------------------------------------------------- /images/2d_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/2d_grid.png -------------------------------------------------------------------------------- /images/2d_grid_red_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/2d_grid_red_black.png -------------------------------------------------------------------------------- /images/FEM_projection_assembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/FEM_projection_assembly.png -------------------------------------------------------------------------------- /images/LWR-Velocity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/LWR-Velocity.png -------------------------------------------------------------------------------- /images/P1LocalBases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/P1LocalBases.png -------------------------------------------------------------------------------- /images/P1_Hat_function_fig1.2LB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/P1_Hat_function_fig1.2LB.png -------------------------------------------------------------------------------- /images/P1_function_annotated_fig1.1LB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/P1_function_annotated_fig1.1LB.png -------------------------------------------------------------------------------- /images/ave_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/ave_demo.png -------------------------------------------------------------------------------- /images/characteristic_tracing_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/characteristic_tracing_1.png -------------------------------------------------------------------------------- /images/characteristic_tracing_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/characteristic_tracing_2.png -------------------------------------------------------------------------------- /images/characteristic_tracing_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/characteristic_tracing_3.png -------------------------------------------------------------------------------- /images/characteristics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/characteristics.png -------------------------------------------------------------------------------- /images/characteristics_rarefaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/characteristics_rarefaction.png -------------------------------------------------------------------------------- /images/characteristics_regions_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/characteristics_regions_1.png -------------------------------------------------------------------------------- /images/characteristics_regions_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/characteristics_regions_2.png -------------------------------------------------------------------------------- /images/characteristics_shock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/characteristics_shock.png -------------------------------------------------------------------------------- /images/ellipses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/ellipses.png -------------------------------------------------------------------------------- /images/ellipses_CG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/ellipses_CG.png -------------------------------------------------------------------------------- /images/entropy_condition_rarefaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/entropy_condition_rarefaction.png -------------------------------------------------------------------------------- /images/entropy_condition_shock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/entropy_condition_shock.png -------------------------------------------------------------------------------- /images/f_interpolation_P1_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/f_interpolation_P1_01.png -------------------------------------------------------------------------------- /images/f_interpolation_P1_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/f_interpolation_P1_02.png -------------------------------------------------------------------------------- /images/f_projection_P1_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/f_projection_P1_01.png -------------------------------------------------------------------------------- /images/fd_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/fd_basic.png -------------------------------------------------------------------------------- /images/finite_volume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/finite_volume.png -------------------------------------------------------------------------------- /images/flux_stencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/flux_stencil.png -------------------------------------------------------------------------------- /images/fmg-cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/fmg-cycle.png -------------------------------------------------------------------------------- /images/ghost-cell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/ghost-cell.png -------------------------------------------------------------------------------- /images/kepler_arch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/kepler_arch.pdf -------------------------------------------------------------------------------- /images/kepler_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/kepler_arch.png -------------------------------------------------------------------------------- /images/kepler_smx.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/kepler_smx.pdf -------------------------------------------------------------------------------- /images/kepler_smx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/kepler_smx.png -------------------------------------------------------------------------------- /images/linear_reconstruction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/linear_reconstruction.png -------------------------------------------------------------------------------- /images/memory_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/memory_architecture.png -------------------------------------------------------------------------------- /images/memory_single_core.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/memory_single_core.png -------------------------------------------------------------------------------- /images/moores_law.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/moores_law.png -------------------------------------------------------------------------------- /images/pipeline_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/pipeline_1.png -------------------------------------------------------------------------------- /images/pipeline_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/pipeline_2.png -------------------------------------------------------------------------------- /images/projection_figure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/projection_figure.png -------------------------------------------------------------------------------- /images/reconstruction_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/reconstruction_1.png -------------------------------------------------------------------------------- /images/reconstruction_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/reconstruction_2.png -------------------------------------------------------------------------------- /images/reconstruction_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/reconstruction_3.png -------------------------------------------------------------------------------- /images/reconstruction_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/reconstruction_4.png -------------------------------------------------------------------------------- /images/relationships.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/relationships.png -------------------------------------------------------------------------------- /images/roofline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/roofline.png -------------------------------------------------------------------------------- /images/shock_diagram_traffic_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/shock_diagram_traffic_a.png -------------------------------------------------------------------------------- /images/shock_diagram_traffic_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/shock_diagram_traffic_b.png -------------------------------------------------------------------------------- /images/v-cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/v-cycle.png -------------------------------------------------------------------------------- /images/vonneumann_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/vonneumann_architecture.png -------------------------------------------------------------------------------- /images/w-cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/images/w-cycle.png -------------------------------------------------------------------------------- /peer_review.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Peer Review Guidelines\n", 8 | "\n", 9 | "When writing a peer review give the code a look over and think about the questions below. Giving a couple of sentences for each of the questions is sufficient. Code examples are even better!\n", 10 | "\n", 11 | "1. Easy to read and understand?\n", 12 | " - Are the `doc-strings` useful?\n", 13 | " - Are variables and functions named descriptively when useful?\n", 14 | " - Are the comments helpful?\n", 15 | "1. Does the code follow a consistent style?\n", 16 | " - Is there a consistent style? [PEP8](https://www.python.org/dev/peps/pep-0008/)\n", 17 | "1. Is the code written succinctly and efficiently?\n", 18 | " - Was there superfluous code sections?\n", 19 | " - Was the code written clearly?\n", 20 | " - Was the code written elegantly without decreasing readability?\n", 21 | " \n", 22 | "Make sure to suggest **constructive** ways to improve all of the above along with:\n", 23 | " - Clever ways to use built-in functionality (when appropriate)\n", 24 | " - Simpler ways to implent the same functionality\n", 25 | " - General improvements to structure, style, and naming\n", 26 | "\n", 27 | "\n", 28 | "## Rubric\n", 29 | "\n", 30 | " \n", 31 | " \n", 32 | " \n", 33 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 38 | " \n", 44 | " \n", 50 | "\n", 51 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 68 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 80 | " \n", 85 | " \n", 90 | " \n", 91 | "
Criteria3 points2 points1 point
Readability
    \n", 39 | "
  • Doc-strings are clear and accurate
  • \n", 40 | "
  • Variables and functions are named descriptively when useful
  • \n", 41 | "
  • Comments are placed in appropriate places and are clear and accurate
  • \n", 42 | "
\n", 43 | "
    \n", 45 | "
  • Doc-strings are present but not completely descriptive
  • \n", 46 | "
  • Variables and functions are sometimes named helpfully
  • \n", 47 | "
  • Comments are present but not always accurate or in the most helpful of places
  • \n", 48 | "
\n", 49 | "
    \n", 52 | "
  • No doc-strings
  • \n", 53 | "
  • Variables and functions are named indecipherably
  • \n", 54 | "
  • No or inaccurate comments
  • \n", 55 | "
\n", 56 | "
Style
    \n", 61 | "
  • PEP 8 or other style is consistent
  • \n", 62 | "
  • Indentation is clean and not mixed
  • \n", 63 | "
\n", 64 | "
    \n", 65 | "
  • Style is mostly consistent with something
  • \n", 66 | "
\n", 67 | "
    \n", 69 | "
  • Style is of a by-gone era but may come back someday (not today)
  • \n", 70 | "
\n", 71 | "
Code Awesome
    \n", 76 | "
  • Code was succinct and clean
  • \n", 77 | "
  • Upon a glance you completely understood the code
  • \n", 78 | "
  • This code defines elegance
  • \n", 79 | "
    \n", 81 | "
  • Code was overall clean but there were a couple spots
  • \n", 82 | "
  • Code was mostly clear except for a couple of spots
  • \n", 83 | "
  • Code worked but may have been a bit hard to follow
  • \n", 84 | "
    \n", 86 | "
  • Code had large sections that did nothing
  • \n", 87 | "
  • Code was very hard to follow
  • \n", 88 | "
  • Code got the job done but no one would ever understand why
  • \n", 89 | "
\n" 92 | ] 93 | } 94 | ], 95 | "metadata": { 96 | "kernelspec": { 97 | "display_name": "Python 2", 98 | "language": "python", 99 | "name": "python2" 100 | }, 101 | "language_info": { 102 | "codemirror_mode": { 103 | "name": "ipython", 104 | "version": 2 105 | }, 106 | "file_extension": ".py", 107 | "mimetype": "text/x-python", 108 | "name": "python", 109 | "nbconvert_exporter": "python", 110 | "pygments_lexer": "ipython2", 111 | "version": "2.7.12" 112 | }, 113 | "latex_envs": { 114 | "bibliofile": "biblio.bib", 115 | "cite_by": "apalike", 116 | "current_citInitial": 1, 117 | "eqLabelWithNumbers": true, 118 | "eqNumInitial": 0 119 | } 120 | }, 121 | "nbformat": 4, 122 | "nbformat_minor": 1 123 | } 124 | -------------------------------------------------------------------------------- /reaction-diffusion_demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | " \n", 10 | "
\n", 9 | " Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved MIT license. (c) Kyle T. Mandli
\n", 11 | "Based on an example from https://github.com/ketch/finite-difference-course" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "%matplotlib inline\n", 21 | "\n", 22 | "from __future__ import print_function\n", 23 | "\n", 24 | "import numpy\n", 25 | "import matplotlib.pyplot as plt\n", 26 | "import scipy.sparse as sparse\n", 27 | "import scipy.sparse.linalg as linalg" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "# Mixed Equations In-Class Project" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "Consider the reaction-diffusion PDE\n", 42 | "$$\\begin{aligned}\n", 43 | " u_t &= \\sigma D_1 \\nabla^2 u + f(u, v) \\\\\n", 44 | " v_t &= \\sigma D_2 \\nabla^2 v + g(u, v)\n", 45 | "\\end{aligned}$$\n", 46 | "in two-dimensions (i.e. $\\nabla^2 u = u_{xx} + u_{yy}$) and with the source terms\n", 47 | "$$\\begin{aligned}\n", 48 | " f(u,v) &= \\alpha u (1 - \\tau_1 v^2) + v (1 - \\tau_2 u) \\\\\n", 49 | " g(u,v) &= \\beta v + \\alpha \\tau_1 u v^2 + u (\\gamma + \\tau_2 v).\n", 50 | "\\end{aligned}$$\n", 51 | "These equations with the appropriate parameters $\\sigma, D_1, D_2, \\alpha, \\beta, \\tau_1, \\tau_2, \\gamma$ can be used to study emergent patterns from seemingly random initial data which we will investigate numerically." 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "def f_reaction(U, V, sigma, tau_1, tau_2, alpha, beta, gamma):\n", 61 | " return alpha * U * (1.0 - tau_1 * V**2) + V * (1.0 - tau_2 * U)\n", 62 | "\n", 63 | "def g_reaction(U, V, sigma, tau_1, tau_2, alpha, beta, gamma):\n", 64 | " return beta * V * (1.0 + alpha * tau_1 / beta * U * V) + U * (gamma + tau_2 * V)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "## Spatial Derivative Discretization\n", 72 | "\n", 73 | "Let's consider the above PDEs on the a square domain $\\Omega = [-1, 1] \\times [-1, 1]$ with periodic boundary conditions. First write a function that uses a five-point stencil to represent the Laplacian operator in 2d and returns the appropriate sparse matrix reprsentation." 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "def laplacian_discretization(m):\n", 83 | " \"\"\"Constructs a sparse matrix that discretizes the 2d Laplacian\n", 84 | " \n", 85 | " Uses a five-point stencil and periodic boundary conditions.\n", 86 | " \"\"\"\n", 87 | " delta_x = 2.0 / (m + 1)\n", 88 | " \n", 89 | " # Primary discretization\n", 90 | " e = numpy.ones(m)\n", 91 | " T = sparse.spdiags([e, -4.0 * e, e], [-1, 0, 1], m, m)\n", 92 | " S = sparse.spdiags([e, e], [-1, 1], m, m)\n", 93 | " I = sparse.eye(m)\n", 94 | " A = sparse.kron(I, T) + sparse.kron(S, I)\n", 95 | " \n", 96 | " # Construct periodic BCs\n", 97 | " e = numpy.ones(m**2)\n", 98 | " A_periodic = sparse.spdiags([e, e],[m - m**2, m**2 - m], m**2, m**2).tolil()\n", 99 | " # Left & right BCs:\n", 100 | " for i in range(m):\n", 101 | " A_periodic[i * m, (i + 1) * m - 1] = 1.0\n", 102 | " A_periodic[(i + 1) * m - 1, i * m] = 1.0\n", 103 | " \n", 104 | " # Combine two matrices\n", 105 | " A = A + A_periodic\n", 106 | " A /= delta_x**2\n", 107 | " A = A.todia()\n", 108 | " \n", 109 | " return A\n", 110 | "\n", 111 | "A = laplacian_discretization(4)\n", 112 | "plt.spy(A)\n", 113 | "plt.show()" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "## Time Stepping\n", 121 | "\n", 122 | "First let's see if we can make a simple explicit method, in this case forward Euler, work for us. We know this might not be such as great idea due to the diffusion term but maybe the reaction terms will be helpfull.\n", 123 | "\n", 124 | "First write a function that uses forward Euler to take a single time step to solve the equations of interest." 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "def forward_euler_step(U, V, delta_t, A, sigma, f, g, D1=0.5, D2=1.0):\n", 134 | " \"\"\"Take a single forward Euler step on the reaction-diffusion equation\"\"\"\n", 135 | " \n", 136 | " U_new = U + delta_t * (sigma * D1 * A * U + f(U, V))\n", 137 | " V_new = V + delta_t * (sigma * D2 * A * V + g(U, V))\n", 138 | " \n", 139 | " return U_new, V_new" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "Let's now try to solve the PDE given the parameters\n", 147 | "$$\n", 148 | " \\sigma = 0.0021, ~ \\tau_1 = 3.5, ~ \\tau_2 = 0.0, ~ \\alpha = 0.899, ~ \\beta=-0.91, ~\\gamma=-\\alpha\n", 149 | "$$\n", 150 | "with the default values of $D_1 = 0.5$ and $D_2 = 1.0$. We will also take a random initial condition.\n", 151 | "\n", 152 | "Note what step-size we might need here. For the two-dimensional heat equation we can show that forward Euler is going to require a step size of\n", 153 | "$$\n", 154 | " \\Delta t \\leq \\frac{\\Delta x^2}{4 \\kappa}\n", 155 | "$$\n", 156 | "where now $\\kappa$ is the coefficient out in front of the Laplacian. Here we will take the maximum of the coefficient in front of the Laplacians to remain stable." 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "def forward_euler_coupled_solver(sigma, tau_1, tau_2, alpha, beta, gamma, D_1, D_2):\n", 166 | " # Alias reaction functions with the above parameters\n", 167 | " f = lambda U, V: f_reaction(U, V, sigma, tau_1, tau_2, alpha, beta, gamma)\n", 168 | " g = lambda U, V: g_reaction(U, V, sigma, tau_1, tau_2, alpha, beta, gamma)\n", 169 | "\n", 170 | " # Set up grid\n", 171 | " m = 150\n", 172 | " delta_x = 2.0 / m\n", 173 | " x = numpy.linspace(-1.0, 1.0, m)\n", 174 | " y = numpy.linspace(-1.0, 1.0, m)\n", 175 | " Y, X = numpy.meshgrid(y, x)\n", 176 | "\n", 177 | " # Initial data\n", 178 | " U = numpy.random.randn(m, m) / 2.0\n", 179 | " V = numpy.random.randn(m, m) / 2.0\n", 180 | " fig = plt.figure()\n", 181 | " axes = fig.add_subplot(1, 1, 1, aspect='equal')\n", 182 | " plot = axes.pcolor(x, y, U, cmap=plt.get_cmap(\"viridis\"))\n", 183 | " fig.colorbar(plot)\n", 184 | "\n", 185 | " # Setup spatial discretization\n", 186 | " U = U.reshape(-1)\n", 187 | " V = V.reshape(-1)\n", 188 | " A = laplacian_discretization(m)\n", 189 | "\n", 190 | " # Time\n", 191 | " t = 0.0\n", 192 | " t_final = 300.0\n", 193 | " delta_t = delta_x**2 / (5.0 * sigma)\n", 194 | " num_steps = int(numpy.round(t_final / delta_t))\n", 195 | "\n", 196 | " # Evolve in time\n", 197 | " next_output_time = 0.0\n", 198 | " for j in range(num_steps):\n", 199 | " U, V = forward_euler_step(U, V, delta_t, A, sigma, f, g)\n", 200 | " t += delta_t\n", 201 | "\n", 202 | " if t >= next_output_time:\n", 203 | " next_output_time += 50.0\n", 204 | " U_output = U.reshape((m, m))\n", 205 | "\n", 206 | " fig = plt.figure()\n", 207 | " axes = fig.add_subplot(1, 1, 1, aspect='equal')\n", 208 | " plot = axes.pcolor(x, y, U_output, cmap=plt.get_cmap(\"viridis\"))\n", 209 | " fig.colorbar(plot)\n", 210 | " axes.set_title(\"t = %s\" % t)\n", 211 | "\n", 212 | " plt.show()\n", 213 | " \n", 214 | "forward_euler_coupled_solver(sigma=0.0021, tau_1=3.5, tau_2=0, alpha=0.899, beta=-0.91, gamma=-0.899, D_1=0.5, D_2=1.0)" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": {}, 220 | "source": [ 221 | "### Implicit-Explicit Splitting\n", 222 | "\n", 223 | "The previous approach was clearly very slow so let's try applying one of our splitting techniques to the problem instead. IMEX methods are actually pretty ideal for this case so let's try using backwards Euler for the stiff diffusion term and the forward Euler time step for the explicit reaction terms.\n", 224 | "\n", 225 | "Implicit:\n", 226 | "$$\\begin{aligned}\n", 227 | " u_t &= \\sigma D_1 \\nabla^2 u \\\\\n", 228 | " v_t &= \\sigma D_2 \\nabla^2 v\n", 229 | "\\end{aligned}$$\n", 230 | "\n", 231 | "Explicit:\n", 232 | "$$\\begin{aligned}\n", 233 | " u_t &= f(u, v) \\\\\n", 234 | " v_t &= g(u, v)\n", 235 | "\\end{aligned}$$\n", 236 | "\n", 237 | "Numerical method:\n", 238 | "$$\\begin{aligned}\n", 239 | " U^\\ast &= U^n + \\Delta t \\sigma D_1 \\nabla^2 U^\\ast \\\\\n", 240 | " V^\\ast &= V^n + \\Delta t \\sigma D_2 \\nabla^2 V^\\ast \\\\\n", 241 | " U^{n+1} &= U^\\ast + \\Delta t f(U^\\ast, V^\\ast) \\\\\n", 242 | " V^{n+1} &= V^\\ast + \\Delta t g(U^\\ast, V^\\ast) \\\\\n", 243 | "\\end{aligned}$$" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": null, 249 | "metadata": {}, 250 | "outputs": [], 251 | "source": [ 252 | "def backward_euler_diffusion_step(U, V, A, delta_t, sigma, D_1, D_2):\n", 253 | " U = linalg.spsolve((sparse.eye(A.shape[0]) - delta_t * sigma * D_1 * A), U)\n", 254 | " V = linalg.spsolve((sparse.eye(A.shape[0]) - delta_t * sigma * D_2 * A), V)\n", 255 | " return U, V\n", 256 | "\n", 257 | "def forward_euler_reaction_step(U, V, delta_t, f, g):\n", 258 | " U_new = U + delta_t * f(U, V)\n", 259 | " V_new = V + delta_t * g(U, V)\n", 260 | " return U_new, V_new" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": null, 266 | "metadata": {}, 267 | "outputs": [], 268 | "source": [ 269 | "def imex_solver(sigma, tau_1, tau_2, alpha, beta, gamma, D_1, D_2):\n", 270 | " # Alias reaction functions with the above parameters\n", 271 | " f = lambda U, V: f_reaction(U, V, sigma, tau_1, tau_2, alpha, beta, gamma)\n", 272 | " g = lambda U, V: g_reaction(U, V, sigma, tau_1, tau_2, alpha, beta, gamma)\n", 273 | "\n", 274 | " # Set up grid\n", 275 | " m = 150\n", 276 | " delta_x = 2.0 / m\n", 277 | " x = numpy.linspace(-1.0, 1.0, m)\n", 278 | " y = numpy.linspace(-1.0, 1.0, m)\n", 279 | " Y, X = numpy.meshgrid(y, x)\n", 280 | "\n", 281 | " # Initial data\n", 282 | " U = numpy.random.randn(m, m) / 2.0\n", 283 | " V = numpy.random.randn(m, m) / 2.0\n", 284 | " fig = plt.figure()\n", 285 | " axes = fig.add_subplot(1, 1, 1, aspect='equal')\n", 286 | " plot = axes.pcolor(x, y, U, cmap=plt.get_cmap(\"viridis\"))\n", 287 | " fig.colorbar(plot)\n", 288 | "\n", 289 | " # Setup spatial discretization\n", 290 | " U = U.reshape(-1)\n", 291 | " V = V.reshape(-1)\n", 292 | " A = laplacian_discretization(m)\n", 293 | "\n", 294 | " # Time\n", 295 | " t = 0.0\n", 296 | " t_final = 30.0\n", 297 | " delta_t = delta_x / (10.0 * sigma)\n", 298 | " num_steps = int(numpy.round(t_final / delta_t))\n", 299 | "\n", 300 | " # Evolve in time\n", 301 | " next_output_time = 0.0\n", 302 | " for j in range(num_steps):\n", 303 | " U, V = backward_euler_diffusion_step(U, V, A, delta_t, sigma, D_1, D_2)\n", 304 | " U, V = forward_euler_step(U, V, delta_t, A, sigma, f, g)\n", 305 | " t += delta_t\n", 306 | "\n", 307 | " if t >= next_output_time:\n", 308 | " next_output_time += 5.0\n", 309 | " U_output = U.reshape((m, m))\n", 310 | "\n", 311 | " fig = plt.figure()\n", 312 | " axes = fig.add_subplot(1, 1, 1, aspect='equal')\n", 313 | " plot = axes.pcolor(x, y, U_output, cmap=plt.get_cmap(\"viridis\"))\n", 314 | " fig.colorbar(plot)\n", 315 | " axes.set_title(\"t = %s\" % t)\n", 316 | "\n", 317 | " plt.show()\n", 318 | " \n", 319 | "# Parameters\n", 320 | "imex_solver(sigma=0.0021, tau_1=3.5, tau_2=0, alpha=0.899, beta=-0.91, gamma=-0.899, D_1=0.5, D_2=1.0)" 321 | ] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "metadata": {}, 326 | "source": [ 327 | "Try playing with the input parameters and see what kind of behavior you see." 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": null, 333 | "metadata": {}, 334 | "outputs": [], 335 | "source": [ 336 | "sigma=0.0045; tau1=2.02; tau2=0.; alpha=2.0; beta=-0.91; gamma=-alpha;\n", 337 | "sigma=0.0005; tau1=2.02; tau2=0.; alpha=2.0; beta=-0.91; gamma=-alpha;\n", 338 | "sigma=0.0021; tau1=3.5; tau2=0; alpha=0.899; beta=-0.91; gamma=-alpha;\n", 339 | "sigma=0.0045; tau1=0.02; tau2=0.2; alpha=1.9; beta=-0.85; gamma=-alpha;\n", 340 | "sigma=0.0001; tau1=0.02; tau2=0.2; alpha=0.899; beta=-0.91; gamma=-alpha;\n", 341 | "sigma=0.0045; tau1=0.02; tau2=0.2; alpha=1.9; beta=-0.91; gamma=-alpha;" 342 | ] 343 | } 344 | ], 345 | "metadata": { 346 | "kernelspec": { 347 | "display_name": "Python 3", 348 | "language": "python", 349 | "name": "python3" 350 | }, 351 | "language_info": { 352 | "codemirror_mode": { 353 | "name": "ipython", 354 | "version": 3 355 | }, 356 | "file_extension": ".py", 357 | "mimetype": "text/x-python", 358 | "name": "python", 359 | "nbconvert_exporter": "python", 360 | "pygments_lexer": "ipython3", 361 | "version": "3.6.4" 362 | }, 363 | "latex_envs": { 364 | "bibliofile": "biblio.bib", 365 | "cite_by": "apalike", 366 | "current_citInitial": 1, 367 | "eqLabelWithNumbers": true, 368 | "eqNumInitial": 0 369 | } 370 | }, 371 | "nbformat": 4, 372 | "nbformat_minor": 1 373 | } 374 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | six 2 | numpy >= 1.12 3 | matplotlib >= 2.0.0 4 | sympy 5 | scipy 6 | jupyter -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries 2 | coarse_grain 3 | compute_pi 4 | demo2 5 | fine_grain 6 | hello_world_mpi 7 | hello_world_omp 8 | jacobi_mpi 9 | jacobi_omp1 10 | jacobi_omp2 11 | mat_mult 12 | note_passing 13 | yeval 14 | 15 | # Output files 16 | jacobi_omp1.txt 17 | jacobi_omp2.txt 18 | jacobi_mpi.txt -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | FC = gfortran 2 | MPI_FC = mpif90 3 | LINK = $(FC) 4 | MPI_LINK = $(MPI_FC) 5 | # GFortran flags 6 | FFLAGS = -O3 -funroll-loops -finline-functions -fdefault-real-8 -fopenmp 7 | # Intel Flags 8 | # FFLAGS = -O3 -fast -ipo -no-prec-div -openmp 9 | NAME = mat_mult 10 | TEST_ITER = 1000 11 | 12 | # Uncomment the appropriate flags for your platform 13 | # Linux 14 | LFLAGS = $(FFLAGS) -lblas 15 | # Mac OS X framework specific, flag above also works for Mac OS X 16 | # LFLAGS = $(FFLAGS) -framework veclib 17 | 18 | # Default fortran rules 19 | %.o : %.f90 ; $(FC) -c $< -o $@ $(FFLAGS) 20 | 21 | all: mat_mult yeval demo2 fine_grain coarse_grain hello_world_omp \ 22 | hello_world_mpi compute_pi note_passing \ 23 | jacobi_omp1 jacobi_omp2 jacobi_mpi 24 | 25 | # Beginning Demo 26 | mat_mult: mod_rand.o matrix_multiply.o 27 | $(LINK) $(LFLAGS) $^ -o $@ 28 | 29 | mat_mult_test: mat_mult 30 | -echo "Non-threaded Tests:" 31 | ./mat_mult $(TEST_ITER) 1 1 32 | ./mat_mult $(TEST_ITER) 2 1 33 | ./mat_mult $(TEST_ITER) 3 1 34 | ./mat_mult $(TEST_ITER) 4 1 35 | -echo "Threaded Tests:" 36 | ./mat_mult $(TEST_ITER) 1 4 37 | ./mat_mult $(TEST_ITER) 2 4 38 | ./mat_mult $(TEST_ITER) 3 4 39 | ./mat_mult $(TEST_ITER) 4 4 40 | 41 | # OpenMP Demos 42 | hello_world_omp: hello_world_omp.o 43 | $(LINK) $(LFLAGS) $^ -o $@ 44 | 45 | yeval: yeval.o 46 | $(LINK) $(LFLAGS) $^ -o $@ 47 | 48 | demo2: demo2.o 49 | $(LINK) $(LFLAGS) $^ -o $@ 50 | 51 | fine_grain: fine_grain.o 52 | $(LINK) $(LFLAGS) $^ -o $@ 53 | 54 | coarse_grain: coarse_grain.o 55 | $(LINK) $(LFLAGS) $^ -o $@ 56 | 57 | jacobi_omp1: jacobi_omp1.o 58 | $(LINK) $(LFLAGS) $^ -o $@ 59 | 60 | jacobi_omp2: jacobi_omp2.o 61 | $(LINK) $(LFLAGS) $^ -o $@ 62 | 63 | jacobi_omp3: jacobi_omp3.o 64 | $(LINK) $(LFLAGS) $^ -o $@ 65 | 66 | # MPI demos 67 | hello_world_mpi.o: hello_world_mpi.f90 68 | $(MPI_FC) -c $< -o $@ $(FFLAGS) 69 | 70 | hello_world_mpi: hello_world_mpi.o 71 | $(MPI_LINK) $^ -o $@ 72 | 73 | compute_pi.o: compute_pi.f90 74 | $(MPI_FC) -c $< -o $@ $(FFLAGS) 75 | 76 | compute_pi: compute_pi.o 77 | $(MPI_LINK) $^ -o $@ 78 | 79 | note_passing.o: note_passing.f90 80 | $(MPI_FC) -c $< -o $@ $(FFLAGS) 81 | 82 | note_passing: note_passing.o 83 | $(MPI_LINK) $^ -o $@ 84 | 85 | jacobi_mpi.o: jacobi_mpi.f90 86 | $(MPI_FC) -c $< -o $@ $(FFLAGS) 87 | 88 | jacobi_mpi: jacobi_mpi.o 89 | $(MPI_LINK) $^ -o $@ 90 | 91 | clean: 92 | -rm -f mod_rand.o mod_rand.mod matrix_multiply.o mat_mult 93 | -rm -f yeval.o yeval 94 | -rm -f demo2.o demo2 95 | -rm -f fine_grain.o fine_grain 96 | -rm -f coarse_grain.o coarse_grain 97 | -rm -f hello_world_omp.o hello_world_omp 98 | -rm -f hello_world_mpi.o hello_world_mpi 99 | -rm -f compute_pi.o compute_pi 100 | -rm -f note_passing.o note_passing 101 | -rm -f jacobi_omp1.o jacobi_omp1 jacobi_omp1.txt 102 | -rm -f jacobi_omp2.o jacobi_omp2 jacobi_omp2.txt 103 | -rm -f jacobi_mpi.o jacobi_mpi jacobi_mpi.txt 104 | 105 | new: 106 | $(MAKE) clean 107 | $(MAKE) all 108 | 109 | 110 | ### DO NOT remove this line - make depends on it ### -------------------------------------------------------------------------------- /src/coarse_grain.f90: -------------------------------------------------------------------------------- 1 | program coarse_grain 2 | 3 | use omp_lib 4 | implicit none 5 | integer(kind=8), parameter :: n = 2**10 6 | real(kind=8), dimension(n) :: x,y 7 | real(kind=8) :: norm,norm_thread,ynorm,ynorm_thread 8 | integer :: nthreads, points_per_thread,thread_num 9 | integer :: i,istart,iend 10 | 11 | ! Specify number of threads to use: 12 | nthreads = 1 ! need this value in serial mode 13 | !$ nthreads = 4 14 | !$ call omp_set_num_threads(nthreads) 15 | !$ print "('Using OpenMP with ',i3,' threads')", nthreads 16 | 17 | ! Determine how many points to handle with each thread. 18 | ! Note that dividing two integers and assigning to an integer will 19 | ! round down if the result is not an integer. 20 | ! This, together with the min(...) in the definition of iend below, 21 | ! insures that all points will get distributed to some thread. 22 | points_per_thread = (n + nthreads - 1) / nthreads 23 | print *, "points_per_thread = ",points_per_thread 24 | 25 | ! initialize x: 26 | do i=1,n 27 | x(i) = dble(i) ! convert to double float 28 | enddo 29 | 30 | norm = 0.d0 31 | ynorm = 0.d0 32 | 33 | !$omp parallel private(i,norm_thread, & 34 | !$omp istart,iend,thread_num,ynorm_thread) 35 | 36 | thread_num = 0 ! needed in serial mode 37 | !$ thread_num = omp_get_thread_num() ! unique for each thread 38 | 39 | ! Determine start and end index for the set of points to be 40 | ! handled by this thread: 41 | istart = thread_num * points_per_thread + 1 42 | iend = min((thread_num+1) * points_per_thread, n) 43 | 44 | !$omp critical 45 | print '("Thread ",i2," will take i = ",i6," through i = ",i6)', thread_num, istart, iend 46 | !$omp end critical 47 | 48 | norm_thread = 0.d0 49 | do i=istart,iend 50 | norm_thread = norm_thread + abs(x(i)) 51 | enddo 52 | 53 | ! update global norm with value from each thread: 54 | !$omp critical 55 | norm = norm + norm_thread 56 | print *, "norm updated to: ",norm 57 | !$omp end critical 58 | 59 | ! make sure all have updated norm before proceeding: 60 | !$omp barrier 61 | 62 | ynorm_thread = 0.d0 63 | do i=istart,iend 64 | y(i) = x(i) / norm 65 | ynorm_thread = ynorm_thread + abs(y(i)) 66 | enddo 67 | 68 | ! update global ynorm with value from each thread: 69 | !$omp critical 70 | ynorm = ynorm + ynorm_thread 71 | print *, "ynorm updated to: ",ynorm 72 | !$omp end critical 73 | !$omp barrier 74 | 75 | !$omp end parallel 76 | 77 | print *, "norm of x = ",norm, " n(n+1)/2 = ",n*(n+1)/2 78 | print *, 'ynorm should be 1.0: ynorm = ', ynorm 79 | 80 | end program coarse_grain -------------------------------------------------------------------------------- /src/compute_pi.f90: -------------------------------------------------------------------------------- 1 | ! Example program that computes pi in parallel 2 | program compute_pi 3 | 4 | use mpi 5 | 6 | implicit none 7 | 8 | integer :: error, num_procs, proc_id, points_per_proc, n, i, start, end 9 | real(kind=8) :: x, dx, pi_sum, pi_sum_proc 10 | 11 | real(kind=8), parameter :: pi = 3.1415926535897932384626433832795d0 12 | 13 | call mpi_init(error) 14 | call mpi_comm_size(MPI_COMM_WORLD, num_procs, error) 15 | call mpi_comm_rank(MPI_COMM_WORLD, proc_id, error) 16 | 17 | ! Proc 0 will ask the user for the number of points 18 | if (proc_id == 0) then 19 | print *, "Using ",num_procs," processors" 20 | print *, "Input n ... " 21 | read *, n 22 | end if 23 | 24 | ! Broadcast to all procs; everybody gets the value of n from proc 0 25 | call mpi_bcast(n, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, error) 26 | 27 | dx = 1.d0 / n 28 | 29 | ! Determine how many points to handle with each proc 30 | points_per_proc = (n + num_procs - 1) / num_procs 31 | ! Only print out the number of points per proc by process 0 32 | if (proc_id == 0) then 33 | print *, "points_per_proc = ", points_per_proc 34 | end if 35 | 36 | ! Determine start and end index for this proc's points 37 | start = proc_id * points_per_proc + 1 38 | end = min((proc_id + 1) * points_per_proc, n) 39 | 40 | ! Diagnostic: tell the user which points will be handled by which proc 41 | print '("Process ",i2," will take i = ",i8," through i = ",i8)', & 42 | proc_id, start, end 43 | 44 | pi_sum_proc = 0.d0 45 | do i=start,end 46 | x = (i - 0.5d0) * dx 47 | pi_sum_proc = pi_sum_proc + 1.d0 / (1.d0 + x**2) 48 | enddo 49 | 50 | call MPI_REDUCE(pi_sum_proc, pi_sum, 1, MPI_DOUBLE_PRECISION, MPI_SUM, 0, & 51 | MPI_COMM_WORLD, error) 52 | 53 | if (proc_id == 0) then 54 | print *, "The approximation to pi is ", 4.d0 * dx * pi_sum 55 | print *, "Difference = ", abs(pi - 4.d0 * dx * pi_sum) 56 | endif 57 | 58 | call mpi_finalize(error) 59 | 60 | end program compute_pi -------------------------------------------------------------------------------- /src/computing_pi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | """Note passing from Python""" 5 | 6 | from __future__ import absolute_import 7 | from __future__ import print_function 8 | 9 | from mpi4py import MPI 10 | import numpy 11 | 12 | comm = MPI.COMM_WORLD 13 | rank = comm.Get_rank() 14 | size = comm.Get_size() 15 | 16 | # This does not seem to work well in Python 17 | # if rank == 0: 18 | # print("Using %s processes" % size) 19 | # N = input(" Input N...") 20 | 21 | N = 100 22 | N = comm.bcast(N, root=0) 23 | 24 | dx = 1.0 / N 25 | 26 | points_per_proc = (N + size - 1) / size 27 | 28 | if rank == 0: 29 | print("Points/process = %s" % points_per_proc) 30 | 31 | start = int(rank * points_per_proc + 1) 32 | end = int(min((rank + 1) * points_per_proc, N)) 33 | 34 | print("Process %s will take i = %s through i = %s" % (rank, start, end)) 35 | 36 | pi_sum_local = 0.0 37 | for i in range(start, end + 1): 38 | x = (i - 0.5) * dx 39 | pi_sum_local += 1.0 / (1.0 + x**2) 40 | pi_sum_local = numpy.array(pi_sum_local, dtype='d') 41 | 42 | pi_sum = numpy.zeros(1) 43 | comm.Reduce([pi_sum_local, MPI.DOUBLE], [pi_sum, MPI.DOUBLE], 44 | op=MPI.SUM, root=0) 45 | 46 | if rank == 0: 47 | pi = 4.0 * dx * pi_sum 48 | print("The approximation to pi is %s" % (pi)) 49 | print("Difference = %s" % (numpy.abs(numpy.pi - pi))) 50 | -------------------------------------------------------------------------------- /src/demo2.f90: -------------------------------------------------------------------------------- 1 | program demo2 2 | 3 | use omp_lib 4 | implicit none 5 | integer :: i 6 | integer(kind=8), parameter :: n = 2**16 7 | real(kind=8), dimension(n) :: x,y,z 8 | 9 | ! Specify number of threads to use: 10 | !$ call omp_set_num_threads(2) 11 | 12 | !$omp parallel ! spawn two threads 13 | !$omp sections ! split up work between them 14 | 15 | !$omp section 16 | x = 1.d0 ! one thread initializes x array 17 | 18 | !$omp section 19 | y = 1.d0 ! another thread initializes y array 20 | 21 | !$omp end sections 22 | !$omp barrier ! not needed, implied at end of sections 23 | 24 | !$omp single ! only want to print once: 25 | print *, "Done initializing x and y" 26 | !$omp end single nowait ! ok for other thread to continue 27 | 28 | !$omp do ! split work between threads: 29 | do i=1,n 30 | z(i) = x(i) + y(i) 31 | enddo 32 | 33 | !$omp end parallel 34 | print *, "max value of z is ",maxval(z) 35 | 36 | 37 | end program demo2 -------------------------------------------------------------------------------- /src/fine_grain.f90: -------------------------------------------------------------------------------- 1 | program fine_grain 2 | 3 | use omp_lib 4 | 5 | implicit none 6 | 7 | integer :: i, thread_num 8 | integer(kind=8), parameter :: n = 2**10 9 | 10 | real(kind=8), dimension(n) :: x, y 11 | real(kind=8) :: norm,ynorm 12 | 13 | integer :: nthreads 14 | 15 | ! Specify number of threads to use: 16 | nthreads = 1 ! need this value in serial mode 17 | !$ nthreads = 8 18 | !$ call omp_set_num_threads(nthreads) 19 | !$ print "('Using OpenMP with ',i3,' threads')", nthreads 20 | 21 | ! Specify number of threads to use: 22 | !$ call omp_set_num_threads(nthreads) 23 | 24 | ! initialize x: 25 | !$omp parallel do 26 | do i=1,n 27 | x(i) = real(i, kind=8) ! convert to double float 28 | enddo 29 | 30 | norm = 0.d0 31 | ynorm = 0.d0 32 | 33 | !$omp parallel private(i) 34 | 35 | !$omp do reduction(+ : norm) 36 | do i=1,n 37 | norm = norm + abs(x(i)) 38 | enddo 39 | 40 | !$omp barrier ! not needed (implicit) 41 | 42 | !$omp do reduction(+ : ynorm) 43 | do i=1,n 44 | y(i) = x(i) / norm 45 | ynorm = ynorm + abs(y(i)) 46 | enddo 47 | 48 | !$omp end parallel 49 | 50 | print *, "norm of x = ",norm, " n(n+1)/2 = ",n*(n+1)/2 51 | print *, 'ynorm should be 1.0: ynorm = ', ynorm 52 | 53 | end program fine_grain 54 | -------------------------------------------------------------------------------- /src/hello_world.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | """MPI Hello World from Python""" 5 | 6 | from __future__ import absolute_import 7 | from __future__ import print_function 8 | 9 | from mpi4py import MPI 10 | 11 | comm = MPI.COMM_WORLD 12 | rank = comm.Get_rank() 13 | size = comm.Get_size() 14 | 15 | print('Hello from Process number %s of %s processes.' % (rank + 1, size)) 16 | -------------------------------------------------------------------------------- /src/hello_world_mpi.f90: -------------------------------------------------------------------------------- 1 | program hello_world_mpi 2 | 3 | use mpi 4 | 5 | implicit none 6 | integer :: error, num_procs, proc_num 7 | 8 | call mpi_init(error) 9 | call mpi_comm_size(MPI_COMM_WORLD, num_procs, error) 10 | call mpi_comm_rank(MPI_COMM_WORLD, proc_num, error) 11 | 12 | print *, 'Hello from Process number', proc_num + 1, & 13 | ' of ', num_procs, ' processes' 14 | 15 | call mpi_finalize(error) 16 | 17 | end program hello_world_mpi 18 | -------------------------------------------------------------------------------- /src/hello_world_omp.f90: -------------------------------------------------------------------------------- 1 | program hello_world_omp 2 | 3 | use omp_lib 4 | 5 | implicit none 6 | integer :: num_threads, thread_id 7 | 8 | !$OMP parallel private(num_threads, thread_id) 9 | !$ num_threads = omp_get_num_threads() 10 | !$ thread_id = omp_get_thread_num() 11 | print *, 'Hello from thread number', thread_id + 1, & 12 | ' of ', num_threads, ' processes' 13 | 14 | !$OMP end parallel 15 | 16 | end program hello_world_omp 17 | -------------------------------------------------------------------------------- /src/jacobi_mpi.f90: -------------------------------------------------------------------------------- 1 | ! Solve Poisson equation 2 | ! u_{xx} = f(x) x \in [a, b] 3 | ! with 4 | ! u(a) = alpha, u(b) = beta 5 | ! using Jacobi iteration using MPI. 6 | program jacobi_mpi 7 | 8 | use mpi 9 | 10 | implicit none 11 | 12 | real (kind=8), parameter :: alpha = 0.d0, beta = 3.d0 13 | 14 | integer :: i, start, end, points_per_task, task_id, N 15 | integer :: error, num_tasks, my_id, req1, req2 16 | integer, dimension(MPI_STATUS_SIZE) :: mpi_status 17 | real(kind=8), dimension(:), allocatable :: f, u, u_old 18 | real(kind=8) :: x, du_max_task, du_max_global, dx, tolerance 19 | 20 | integer(kind=8) :: num_iterations 21 | integer(kind=8), parameter :: MAX_ITERATIONS = 2**32_8 22 | integer, parameter :: PRINT_INTERVAL = 5000 23 | 24 | ! Initialize MPI; get total number of tasks and ID of this task 25 | call mpi_init(error) 26 | call mpi_comm_size(MPI_COMM_WORLD, num_tasks, error) 27 | call mpi_comm_rank(MPI_COMM_WORLD, my_id, error) 28 | 29 | ! Ask the user for the number of points 30 | ! if (me == 0) then 31 | ! print *, "Input n ... " 32 | ! read *, n 33 | ! end if 34 | N = 100 35 | ! Broadcast to all tasks; everybody gets the value of n from task 0 36 | call mpi_bcast(N, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, error) 37 | 38 | dx = 1.d0 / real(N + 1, kind=8) 39 | tolerance = 0.1d0*dx**2 40 | 41 | ! Determine how many points to handle with each task 42 | points_per_task = (N + num_tasks - 1) / num_tasks 43 | if (my_id == 0) then ! Only one task should print to avoid clutter 44 | print *, "points_per_task = ", points_per_task 45 | end if 46 | 47 | ! Determine start and end index for this task's points 48 | start = my_id * points_per_task + 1 49 | end = min((my_id + 1) * points_per_task, n) 50 | 51 | ! Diagnostic: tell the user which points will be handled by which task 52 | print '("Task ",i2," will take i = ",i6," through i = ",i6)', & 53 | my_id, start, end 54 | 55 | 56 | ! Initialize: 57 | ! ----------- 58 | 59 | ! This makes the indices run from istart-1 to end+1 60 | ! This is more or less cosmetic, but makes things easier to think about 61 | allocate(f(start - 1:end + 1)) 62 | allocate(u(start - 1:end + 1)) 63 | allocate(u_old(start - 1:end + 1)) 64 | 65 | ! Each task sets its own, independent array 66 | do i = start, end 67 | ! Each task is a single thread with all its variables private 68 | ! so re-using the scalar variable x from one loop iteration to 69 | ! the next does not produce a race condition. 70 | x = dx * real(i, kind=8) 71 | f(i) = exp(x) ! RHS 72 | u(i) = alpha + x * (beta - alpha) ! Initial guess 73 | end do 74 | 75 | ! Set boundary conditions if this task is keeping track of a boundary 76 | ! point 77 | if (my_id == 0) then 78 | u(start - 1) = alpha 79 | end if 80 | if (my_id == num_tasks - 1) then 81 | u(end + 1) = beta 82 | end if 83 | 84 | 85 | ! Jacobi iteratation: 86 | ! ------------------- 87 | 88 | do num_iterations = 1, MAX_ITERATIONS 89 | u_old = u 90 | 91 | ! Send endpoint values to tasks handling neighboring sections 92 | ! of the array. Note that non-blocking sends are used; note 93 | ! also that this sends from u_old, so the buffer we're sending 94 | ! from won't be modified while it's being sent. 95 | ! 96 | ! tag = 1 is used for messages sent to the left 97 | ! tag = 2 is used for messages sent to the right 98 | 99 | if (my_id > 0) then 100 | ! Send left endpoint value to process to the "left" 101 | ! Note that this is a "non-blocking send" 102 | call mpi_isend(u_old(start), 1, MPI_DOUBLE_PRECISION, my_id - 1, & 103 | 1, MPI_COMM_WORLD, req1, error) 104 | end if 105 | if (my_id < num_tasks - 1) then 106 | ! Send right endpoint value to process on the "right" 107 | ! Note that this is a "non-blocking send" 108 | call mpi_isend(u_old(end), 1, MPI_DOUBLE_PRECISION, my_id + 1, & 109 | 2, MPI_COMM_WORLD, req2, error) 110 | end if 111 | 112 | ! Accept incoming endpoint values from other tasks. Note that 113 | ! these are blocking receives, because we can't run the next step 114 | ! of the Jacobi iteration until we've received all the 115 | ! incoming data. 116 | 117 | if (my_id < num_tasks-1) then 118 | ! Receive right endpoint value 119 | call mpi_recv(u_old(end+1), 1, MPI_DOUBLE_PRECISION, my_id + 1, & 120 | 1, MPI_COMM_WORLD, mpi_status, error) 121 | end if 122 | if (my_id > 0) then 123 | ! Receive left endpoint value 124 | call mpi_recv(u_old(start-1), 1, MPI_DOUBLE_PRECISION, my_id - 1, & 125 | 2, MPI_COMM_WORLD, mpi_status, error) 126 | end if 127 | 128 | du_max_task = 0.d0 ! Max seen by this task 129 | 130 | ! Apply Jacobi iteration on this task's section of the array 131 | do i = start, end 132 | u(i) = 0.5d0*(u_old(i-1) + u_old(i+1) - dx**2*f(i)) 133 | du_max_task = max(du_max_task, abs(u(i) - u_old(i))) 134 | end do 135 | 136 | ! Take global maximum of dumax values 137 | call mpi_allreduce(du_max_task, du_max_global, 1, & 138 | MPI_DOUBLE_PRECISION, MPI_MAX, MPI_COMM_WORLD, error) 139 | ! Note that this MPI_ALLREDUCE call acts as an implicit barrier, 140 | ! since no process can return from it until all processes 141 | ! have called it. Because of this, after this call we know 142 | ! that all the send and receive operations initiated at the 143 | ! top of the loop have finished -- all the MPI_RECV calls have 144 | ! finished in order for each process to get here, and if the 145 | ! MPI_RECV calls have finished, the corresponding MPI_ISEND 146 | ! calls have also finished. Thus we can safely modify u_old 147 | ! again. 148 | 149 | ! Also periodically report progress to the user 150 | if (my_id == 0) then 151 | if (mod(num_iterations, PRINT_INTERVAL)==0) then 152 | print '("After ",i8," iterations, dumax = ",d16.6,/)', & 153 | num_iterations, du_max_global 154 | end if 155 | end if 156 | 157 | ! All tasks now have du_max_global, and can check for convergence 158 | if (du_max_global < tolerance) exit 159 | end do 160 | 161 | print '("Task number ",i2," finished after ",i9," iterations, dumax = ",& 162 | e16.6)', my_id, num_iterations, du_max_global 163 | 164 | 165 | ! Output result: 166 | ! -------------- 167 | 168 | ! Note: this only works if all processes share a file system 169 | ! and can all open and write to the same file! 170 | 171 | ! Synchronize to keep the next part from being non-deterministic 172 | call mpi_barrier(MPI_COMM_WORLD, error) 173 | 174 | ! Check to make sure that we were successful 175 | if (num_iterations >= MAX_ITERATIONS) then 176 | if (my_id == 0) then 177 | print *, "Jacobi failed to converge!" 178 | print *, "Reached du_max = ", du_max_global 179 | print *, "Tolerance = ", tolerance 180 | end if 181 | call mpi_finalize(error) 182 | stop 183 | endif 184 | 185 | ! Have each task output to a file in sequence, using messages to 186 | ! coordinate 187 | 188 | if (my_id == 0) then ! Task 0 goes first 189 | ! Open file for writing, replacing any previous version: 190 | open(unit=20, file="jacobi_mpi.txt", status="replace") 191 | write(20, '(2e20.10)') 0.d0, u(0) ! Boundary value at left end 192 | 193 | do i = start, end 194 | write(20, '(2e20.10)') i * dx, u(i) 195 | end do 196 | 197 | close(unit=20) 198 | ! Closing the file should guarantee that all the ouput 199 | ! will be written to disk. 200 | ! If the file isn't closed before the next process starts writing, 201 | ! output may be jumbled or missing. 202 | 203 | ! Send go-ahead message to next task 204 | ! Only the fact that the message was sent is important, not its contents 205 | ! so we send the special address MPI_BOTTOM and length 0. 206 | ! tag=4 is used for this message. 207 | 208 | if (num_tasks > 1) then 209 | call mpi_send(MPI_BOTTOM, 0, MPI_INTEGER, 1, 4, & 210 | MPI_COMM_WORLD, error) 211 | endif 212 | 213 | else 214 | ! Wait for go-ahead message from previous task 215 | call mpi_recv(MPI_BOTTOM, 0, MPI_INTEGER, my_id - 1, 4, & 216 | MPI_COMM_WORLD, mpi_status, error) 217 | ! Open file for appending; do not destroy previous contents 218 | open(unit=20, file="jacobi_mpi.txt", status="old", access="append") 219 | do i = start, end 220 | write(20, '(2e20.10)') i * dx, u(i) 221 | end do 222 | 223 | ! Boundary value at right end: 224 | if (my_id == num_tasks - 1) write(20, '(2e20.10)') 1.d0, u(end + 1) 225 | 226 | ! Flush all pending writes to disk 227 | close(unit=20) 228 | 229 | if (my_id < num_tasks - 1) then 230 | ! Send go-ahead message to next task 231 | call mpi_send(MPI_BOTTOM, 0, MPI_INTEGER, my_id + 1, 4, & 232 | MPI_COMM_WORLD, error) 233 | end if 234 | end if 235 | 236 | ! Close out MPI 237 | call mpi_finalize(error) 238 | 239 | end program jacobi_mpi 240 | -------------------------------------------------------------------------------- /src/jacobi_omp1.f90: -------------------------------------------------------------------------------- 1 | ! Solve Poisson equation 2 | ! u_{xx} = f(x) x \in [a, b] 3 | ! with 4 | ! u(a) = alpha, u(b) = beta 5 | ! using Jacobi iteration and a fine-grain parallelism approach using OpenMP. 6 | program jacobi_omp1 7 | 8 | use omp_lib 9 | 10 | implicit none 11 | 12 | ! Problem specification and storage 13 | real(kind=8) :: a, b, alpha, beta 14 | real(kind=8), dimension(:), allocatable :: x, u, u_new, f 15 | real(kind=8) :: dx, tolerance, du_max 16 | 17 | integer(kind=4) :: n, num_threads 18 | integer(kind=8) :: i, num_iterations 19 | integer(kind=8), parameter :: MAX_ITERATIONS = 100000 20 | real(kind=8) :: time(2) 21 | 22 | ! Boundaries 23 | a = 0.d0 24 | b = 1.d0 25 | alpha = 0.d0 26 | beta = 3.d0 27 | 28 | ! Specify number of threads to use: 29 | num_threads = 1 30 | !$ num_threads = 4 31 | !$ call omp_set_num_threads(num_threads) 32 | !$ print "('Using OpenMP with ',i3,' threads')", num_threads 33 | 34 | N = 100 35 | 36 | ! Allocate storage for boundary points too 37 | allocate(x(0:N + 1), u(0:N + 1), u_new(0:N + 1), f(0:N + 1)) 38 | 39 | call cpu_time(time(1)) 40 | 41 | ! grid spacing: 42 | dx = (b - a) / (N + 1.d0) 43 | 44 | ! Set iniital guess and construct the grid 45 | ! Note that here we are breaking up the problem already to the threads 46 | !$omp parallel do 47 | do i=0, N + 1 48 | ! grid points: 49 | x(i) = i * dx 50 | ! source term: 51 | f(i) = exp(x(i)) 52 | ! initial guess (satisfies boundary conditions and sets them) 53 | u(i) = alpha + x(i) * (beta - alpha) 54 | enddo 55 | 56 | ! Tolerance 57 | tolerance = 0.1d0 * dx**2 58 | 59 | ! Main Jacobi iteration loop 60 | ! Copy old values to new 61 | num_iterations = 0 62 | du_max = 1d99 63 | do while (du_max >= tolerance .and. num_iterations <= MAX_ITERATIONS) 64 | du_max = 0.d0 65 | !$omp parallel do reduction(max : du_max) 66 | do i=1, N 67 | u_new(i) = 0.5d0 * (u(i-1) + u(i+1) - dx**2 * f(i)) 68 | ! print *, abs(u(i) - u_old(i)) 69 | du_max = max(du_max, abs(u_new(i) - u(i))) 70 | end do 71 | 72 | if (mod(num_iterations, 1000) == 0) then 73 | print *, "du_max, iteration = ", du_max, num_iterations 74 | end if 75 | 76 | ! Copy old data into new 77 | !$omp parallel do 78 | do i=1, N 79 | u(i) = u_new(i) 80 | end do 81 | num_iterations = num_iterations + 1 82 | end do 83 | 84 | call cpu_time(time(2)) 85 | if (num_iterations > MAX_ITERATIONS) then 86 | print *, "Iteration failed!" 87 | stop 88 | end if 89 | print '("CPU time = ",f12.8, " seconds")', time(2) - time(1) 90 | print *, "Total number of iterations: ", num_iterations 91 | 92 | ! Write out solution for checking 93 | open(unit=20, file="jacobi_omp1.txt", status="unknown") 94 | do i=0,n+1 95 | write(20,'(2e20.10)') x(i), u(i) 96 | enddo 97 | close(20) 98 | 99 | end program jacobi_omp1 100 | -------------------------------------------------------------------------------- /src/jacobi_omp2.f90: -------------------------------------------------------------------------------- 1 | ! Solve Poisson equation 2 | ! u_{xx} = f(x) x \in [a, b] 3 | ! with 4 | ! u(a) = alpha, u(b) = beta 5 | ! using Jacobi iteration and a coarse-grain parallelism approach using OpenMP. 6 | program jacobi_omp2 7 | 8 | use omp_lib 9 | 10 | implicit none 11 | 12 | ! Problem specification and storage 13 | real(kind=8) :: a, b, alpha, beta 14 | real(kind=8), dimension(:), allocatable :: x, u, u_new, f 15 | real(kind=8) :: dx, tolerance, du_max, du_max_thread 16 | 17 | integer(kind=4) :: n, num_threads, points_per_thread, thread_id, start, end 18 | integer(kind=8) :: i, num_iterations 19 | integer(kind=8), parameter :: MAX_ITERATIONS = 100000 20 | real(kind=8) :: time(2) 21 | 22 | ! Boundaries 23 | a = 0.d0 24 | b = 1.d0 25 | alpha = 0.d0 26 | beta = 3.d0 27 | 28 | ! Specify number of threads to use: 29 | num_threads = 2 30 | !$ call omp_set_num_threads(num_threads) 31 | !$ print "('Using OpenMP with ',i3,' threads')", num_threads 32 | 33 | N = 100 34 | 35 | ! Allocate storage for boundary points too 36 | allocate(x(0:N + 1), u(0:N + 1), u_new(0:N + 1), f(0:N + 1)) 37 | 38 | call cpu_time(time(1)) 39 | 40 | ! grid spacing: 41 | dx = (b - a) / (N + 1.d0) 42 | 43 | ! Tolerance 44 | tolerance = 0.1d0 * dx**2 45 | 46 | ! Determine how many points to handle with each thread. 47 | ! Note that dividing two integers and assigning to an integer will 48 | ! round down if the result is not an integer. 49 | ! This, together with the min(...) in the definition of iend below, 50 | ! insures that all points will get distributed to some thread. 51 | points_per_thread = (n + num_threads - 1) / num_threads 52 | print *, "points_per_thread = ", points_per_thread 53 | 54 | ! Start of the parallel block... 55 | ! ------------------------------ 56 | 57 | ! This is the only time threads are forked in this program: 58 | !$omp parallel private(thread_id, num_iterations, start, end, i, & 59 | !$OMP du_max_thread) 60 | 61 | ! Set thread is, default to 0 if in serial 62 | thread_id = 0 63 | !$ thread_id = omp_get_thread_num() 64 | 65 | ! Determine start and end index 66 | start = thread_id * points_per_thread + 1 67 | end = min((thread_id + 1) * points_per_thread, N) 68 | 69 | ! Output some thread information and indexing 70 | !$omp critical 71 | print '("Thread ",i2," will take i = ",i6," through i = ",i6)', & 72 | thread_id, start, end 73 | !$omp end critical 74 | 75 | ! Set iniital guess and construct the grid 76 | do i=start, end 77 | ! grid points: 78 | x(i) = i * dx 79 | ! source term: 80 | f(i) = exp(x(i)) 81 | ! initial guess (satisfies boundary conditions and sets them) 82 | u(i) = alpha + x(i) * (beta - alpha) 83 | enddo 84 | ! Note that the above does not set the boundaries, do this in a single thread 85 | !$omp single 86 | x(0) = a 87 | x(N + 1) = b 88 | u(0) = alpha 89 | u(N + 1) = beta 90 | !$omp end single nowait 91 | 92 | ! Main Jacobi iteration loop 93 | do num_iterations=1, MAX_ITERATIONS 94 | 95 | ! Make one thread reset the global du_max 96 | !$omp single 97 | du_max = 0.d0 98 | !$omp end single 99 | 100 | ! Private to each thread 101 | du_max_thread = 0.d0 102 | do i=start, end 103 | u_new(i) = 0.5d0 * (u(i-1) + u(i+1) - dx**2 * f(i)) 104 | du_max_thread = max(du_max_thread, abs(u_new(i) - u(i))) 105 | end do 106 | 107 | ! Compute global du_max 108 | !$omp critical 109 | du_max = max(du_max, du_max_thread) 110 | !$omp end critical 111 | 112 | ! Make sure all threads are done contributing to du_max 113 | !$omp barrier 114 | 115 | ! Have one thread print out the convergence info 116 | !$omp single 117 | if (mod(num_iterations, 1000) == 0) then 118 | print '("After ",i8," iterations, dumax = ",d16.6,/)', num_iterations, du_max 119 | end if 120 | !$omp end single nowait 121 | 122 | ! Copy new data into u 123 | do i=start, end 124 | u(i) = u_new(i) 125 | end do 126 | 127 | ! Check exit criteria 128 | if (du_max < tolerance) then 129 | exit 130 | end if 131 | 132 | ! Make sure all threads are caught up to this point before starting 133 | ! another iteration 134 | !$omp barrier 135 | end do 136 | 137 | !$omp end parallel 138 | 139 | call cpu_time(time(2)) 140 | if (num_iterations > MAX_ITERATIONS) then 141 | print *, "Iteration failed!" 142 | stop 143 | end if 144 | print '("CPU time = ",f12.8, " seconds")', time(2) - time(1) 145 | print *, "Total number of iterations: ", num_iterations 146 | 147 | ! Write out solution for checking 148 | open(unit=20, file="jacobi_omp2.txt", status="unknown") 149 | do i=0,n+1 150 | write(20,'(2e20.10)') x(i), u(i) 151 | enddo 152 | close(20) 153 | 154 | end program jacobi_omp2 -------------------------------------------------------------------------------- /src/matrix_multiply.f90: -------------------------------------------------------------------------------- 1 | real function matrix_multiply_test(N,method) 2 | 3 | use mod_rand 4 | implicit none 5 | 6 | external DGEMM,DDOT 7 | 8 | double precision :: DDOT 9 | integer, intent(in) :: N,method 10 | integer :: start,finish,count_rate 11 | double precision, dimension(:,:), allocatable :: A,B,C 12 | 13 | ! Local 14 | integer :: i,j,k 15 | 16 | ! Create the random arrays 17 | call init_random_seed() 18 | allocate(A(N,N),B(N,N),C(N,N)) 19 | call random_number(A) 20 | call random_number(B) 21 | 22 | ! Start the timer and start multiplying 23 | call system_clock(start,count_rate) 24 | select case(method) 25 | case(1) ! Default method provided as an intrinsic method 26 | C = matmul(A,B) 27 | case(2) ! Simple three loop multiplication 28 | !$OMP parallel do private(j,k) 29 | do i=1,N 30 | do j=1,N 31 | do k=1,N 32 | C(i,j) = C(i,j) + A(i,k) * B(k,j) 33 | enddo 34 | enddo 35 | enddo 36 | case(3) ! OpenMP parallelized double loop 37 | !$OMP parallel do private(j) 38 | do i=1,N 39 | do j=1,N 40 | C(i,j) = DDOT(N, A(i,:), 1, B(:,j), 1) 41 | enddo 42 | enddo 43 | case(4) ! BLAS Routine call 44 | ! call DGEMM(transa,transb,l,n,m,alpha,a,lda,b,ldb,beta,c,ldc) 45 | call DGEMM('N', 'N', N, N, N, 1.d0, A, N, B, N, 0.d0, C, N) 46 | case default 47 | print *, "***ERROR*** Invalid multiplication method!" 48 | matrix_multiply_test = -1 49 | return 50 | end select 51 | call system_clock(finish,count_rate) 52 | 53 | matrix_multiply_test = float(finish - start) / float(count_rate) 54 | 55 | end function matrix_multiply_test 56 | 57 | program matrix_multiply 58 | 59 | use omp_lib 60 | 61 | implicit none 62 | 63 | integer :: N, method, threads 64 | character(len=10) :: input_N, input_method, input_threads 65 | real :: matrix_multiply_test, time 66 | 67 | select case(iargc()) 68 | case(0) 69 | N = 1000 70 | method = 1 71 | threads = 1 72 | case(1) 73 | call getarg(1,input_N) 74 | read(input_N,'(I10)') N 75 | method = 1 76 | case(2) 77 | call getarg(1,input_N) 78 | call getarg(2,input_method) 79 | read(input_N,'(I10)') N 80 | read(input_method,'(I10)') method 81 | case(3) 82 | call getarg(1,input_N) 83 | call getarg(2,input_method) 84 | call getarg(3,input_threads) 85 | read(input_N,'(I10)') N 86 | read(input_method,'(I10)') method 87 | read(input_threads,'(I10)') threads 88 | case default 89 | print *, "***ERROR*** Too many arguments!" 90 | stop 91 | end select 92 | 93 | !$ call omp_set_num_threads(threads) 94 | 95 | time = matrix_multiply_test(N, method) 96 | 97 | print '("Timing for ",i5,"x",i5," matrices: ",f10.5," s")',N,N,time 98 | 99 | end program matrix_multiply -------------------------------------------------------------------------------- /src/matrix_multiply.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | matrix_multiply.py 5 | 6 | Created by Kyle Mandli on 2009-03-25. 7 | Copyright (c) 2009 University of Washington. All rights reserved. 8 | 9 | Simple matrix multiply script with timings 10 | """ 11 | 12 | import time 13 | import numpy as np 14 | 15 | N_array = [100,1000,1500,2000,2500,3000] 16 | time_dot = [] 17 | time_loop = [] 18 | 19 | # Python version 20 | for N in N_array: 21 | A = np.random.rand(N,N) 22 | B = np.random.rand(N,N) 23 | 24 | # Dot method 25 | print "Dot method ",N 26 | C = np.zeros((N,N)) 27 | timing = time.time() 28 | C = np.dot(A,B) 29 | time_dot.append(time.time() - timing) 30 | 31 | # Loop method 32 | if N <= 1000: 33 | print "Loop method ",N 34 | C = np.zeros((N,N)) 35 | timing = time.time() 36 | for i in xrange(0,N): 37 | for j in xrange(0,N): 38 | for k in xrange(0,N): 39 | C[i,j] = C[i,j] + A[i,k] * B[k,j] 40 | 41 | time_loop.append(time.time() - timing) 42 | 43 | print "Dot times = ", time_dot 44 | print "Loop times =", time_loop 45 | -------------------------------------------------------------------------------- /src/mod_rand.f90: -------------------------------------------------------------------------------- 1 | ! ============================================================================ 2 | ! Program: 3 | ! File: untitled.f95 4 | ! Created: 2009-03-25 5 | ! Author: Kyle Mandli 6 | ! ============================================================================ 7 | ! Description: 8 | ! 9 | ! ============================================================================ 10 | 11 | module mod_rand 12 | 13 | implicit none 14 | 15 | contains 16 | 17 | SUBROUTINE init_random_seed() 18 | INTEGER :: i, n, clock 19 | INTEGER, DIMENSION(:), ALLOCATABLE :: seed 20 | 21 | CALL RANDOM_SEED(size = n) 22 | ALLOCATE(seed(n)) 23 | 24 | CALL SYSTEM_CLOCK(COUNT=clock) 25 | 26 | seed = clock + 37 * (/ (i - 1, i = 1, n) /) 27 | CALL RANDOM_SEED(PUT = seed) 28 | 29 | DEALLOCATE(seed) 30 | END SUBROUTINE 31 | 32 | end module mod_rand 33 | -------------------------------------------------------------------------------- /src/note_passing.f90: -------------------------------------------------------------------------------- 1 | ! Demonstration for message passing between MPI processes 2 | program note_passing 3 | 4 | use mpi 5 | 6 | implicit none 7 | 8 | integer :: proc_id, num_procs, error, tag 9 | real(kind=8) :: important_note 10 | integer, dimension(MPI_STATUS_SIZE) :: status 11 | 12 | call mpi_init(error) 13 | call mpi_comm_size(MPI_COMM_WORLD, num_procs, error) 14 | call mpi_comm_rank(MPI_COMM_WORLD, proc_id, error) 15 | 16 | if (num_procs == 1) then 17 | print *, "Only one process, cannot do anything." 18 | call MPI_FINALIZE(error) 19 | stop 20 | endif 21 | 22 | ! Not really important in this case but important to sort through messages 23 | tag = 42 24 | 25 | ! If we are process 0 then set the value to be passed 26 | if (proc_id == 0) then 27 | important_note = 2.718281828d0 28 | print '("Process ",i3," note = ",e18.8)', proc_id, important_note 29 | 30 | call mpi_send(important_note, 1, MPI_DOUBLE_PRECISION, 1, tag, & 31 | MPI_COMM_WORLD, error) 32 | 33 | ! If we are one of the processes in between pass it on to the next process 34 | else if (proc_id < num_procs - 1) then 35 | 36 | call MPI_RECV(important_note, 1, MPI_DOUBLE_PRECISION, proc_id-1, tag, & 37 | MPI_COMM_WORLD, status, error) 38 | 39 | print '("Process ",i3," received note = ",e18.8)', proc_id, important_note 40 | 41 | call mpi_send(important_note, 1, MPI_DOUBLE_PRECISION, proc_id + 1, & 42 | tag, MPI_COMM_WORLD, error) 43 | 44 | print '("Process ",i3," sent note = ",e18.8)', proc_id, important_note 45 | 46 | ! If we are the last process in the class to find out, well... 47 | else if (proc_id == num_procs - 1) then 48 | 49 | call mpi_recv(important_note, 1, MPI_DOUBLE_PRECISION, proc_id - 1, & 50 | tag, MPI_COMM_WORLD, status, error) 51 | 52 | print '("Process ",i3," ends up with note = ",e18.8)', proc_id, important_note 53 | endif 54 | 55 | call MPI_FINALIZE(error) 56 | 57 | end program note_passing 58 | -------------------------------------------------------------------------------- /src/note_passing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | """Note passing from Python""" 5 | 6 | from __future__ import absolute_import 7 | from __future__ import print_function 8 | 9 | from mpi4py import MPI 10 | import numpy 11 | 12 | comm = MPI.COMM_WORLD 13 | rank = comm.Get_rank() 14 | size = comm.Get_size() 15 | 16 | tag = 42 17 | N = 10 18 | 19 | if rank == 0: 20 | data = numpy.arange(N, dtype=numpy.float64) 21 | print("Process %s note = %s" % (rank, data)) 22 | # Note here that MPI datatype discovery is automatic 23 | comm.Send(data, dest=rank + 1, tag=tag) 24 | 25 | elif rank < size - 1: 26 | data = numpy.empty(N, dtype=numpy.float64) 27 | comm.Recv(data, source=rank - 1, tag=tag) 28 | 29 | print("Process %s note = %s" % (rank, data)) 30 | 31 | comm.Send(data, dest=rank + 1, tag=tag) 32 | 33 | elif rank == size - 1: 34 | data = numpy.empty(N, dtype=numpy.float64) 35 | comm.Recv(data, source=rank - 1, tag=tag) 36 | 37 | print("Process %s note = %s" % (rank, data)) 38 | 39 | else: 40 | raise Exception("Invalid rank.") 41 | -------------------------------------------------------------------------------- /src/yeval.f90: -------------------------------------------------------------------------------- 1 | program yeval 2 | 3 | use omp_lib 4 | 5 | implicit none 6 | 7 | integer(kind=8), parameter :: n = 2**16 8 | integer(kind=4) :: i, nthreads 9 | real(kind=8), dimension(n) :: y 10 | real(kind=8) :: dx, x 11 | 12 | ! Specify number of threads to use: 13 | !$ print *, "How many threads to use? " 14 | !$ read *, nthreads 15 | !$ call omp_set_num_threads(nthreads) 16 | !$ print "('Using OpenMP with ',i3,' threads')", nthreads 17 | 18 | dx = 1.d0 / (n+1.d0) 19 | 20 | !$omp parallel do private(x) 21 | do i=1, n 22 | x = i * dx 23 | y(i) = exp(x) * cos(x) * sin(x) * sqrt(5.d0 * x + 6.d0) 24 | enddo 25 | !$omp end parallel do 26 | 27 | print *, "Filled vector y of length", n 28 | 29 | end program yeval -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 2 | """Basic test script that runs all notebooks.""" 3 | 4 | import os 5 | import subprocess 6 | import tempfile 7 | import sys 8 | import nbformat 9 | import doctest 10 | import glob 11 | 12 | if sys.version_info >= (3,0): 13 | kernel = 'python3' 14 | else: 15 | kernel = 'python2' 16 | 17 | # Construct notebook list 18 | notebooks = glob.glob("*.ipynb") 19 | # Note we leave out the python intro as there are purposeful exceptions 20 | notebooks.remove('01_python.ipynb') 21 | # The following either require additional packages (FV and FE), do not entirely 22 | # work yet (spectral), or do not contain any substantial Python code 23 | notebooks.remove('13_finite_volume.ipynb') 24 | notebooks.remove('14_spectral.ipynb') 25 | notebooks.remove('15_finite_element.ipynb') 26 | notebooks.remove('16_performance.ipynb') 27 | 28 | def _notebook_run(path): 29 | """Execute a notebook via nbconvert and collect output. 30 | :returns (parsed nb object, execution errors) 31 | """ 32 | with tempfile.NamedTemporaryFile(suffix=".ipynb") as fout: 33 | args = ["jupyter", "nbconvert", "--to", "notebook", "--execute", 34 | "--ExecutePreprocessor.timeout=60", 35 | "--ExecutePreprocessor.kernel_name="+kernel, 36 | "--output", fout.name, path] 37 | subprocess.check_call(args) 38 | 39 | fout.seek(0) 40 | nb = nbformat.reads(fout.read().decode('utf-8'), nbformat.current_nbformat) 41 | 42 | errors = [output for cell in nb.cells if "outputs" in cell 43 | for output in cell["outputs"] 44 | if output.output_type == "error"] 45 | 46 | return nb, errors 47 | 48 | def run_tests(): 49 | 50 | for filename in notebooks: 51 | if (filename.split('.')[-1] == 'ipynb'): 52 | nb, errors = _notebook_run(filename) 53 | if errors != []: 54 | raise(Exception) 55 | 56 | 57 | if __name__ == '__main__': 58 | run_tests() -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandli/numerical-methods-pdes/254fcbe217fe3cce8373f61540227da781322bc4/utils/__init__.py -------------------------------------------------------------------------------- /utils/animation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | *NOTE:* This version is slightly modified from the one in 4 | $CLAW/visclaw/src/python/visclaw/animation_tools.py 5 | 6 | Some functions requires JSAnimation, either from Clawpack 7 | or by installing it separately from 8 | https://github.com/jakevdp/JSAnimation 9 | 10 | This animation_tools module contains tools to create animations in Python and 11 | Jupyter notebooks. 12 | 13 | Three types of animations are supported: 14 | - using the ipywidget interact to create a figure with a slider bar, 15 | - using JSAnimation to create Javascript code that loops over a set of 16 | images and adds controls to play as an animation. 17 | - creation of mp4 files using ffmpeg (provided this package is installed). 18 | 19 | The set of images to combine in an animation can be specified as a 20 | list of images, a list of `matplotlib` figures, or a directory of 21 | `png` or other image files. 22 | 23 | Utilities are provided to convert between these. 24 | 25 | Functions are provided to create inline animations in Jupyter notebooks or 26 | stand-alone files that can be viewed in other ways, including 27 | - An html file with the JSAnimation version, 28 | - A mp4 file, 29 | - A reStructured text file with the JSAnimation for inclusion in Sphinx docs. 30 | 31 | The utility function make_anim_from_plotdir can be used to convert the png 32 | files in a Clawpack _plots directory into standalone animations of the types 33 | listed above. See the file make_anim.py for an example of how this can be 34 | invoked from an applications directory. 35 | 36 | See also: 37 | https://ipywidgets.readthedocs.io/en/latest/#ipywidgets 38 | https://github.com/jakevdp/JSAnimation 39 | 40 | More documentation of these functions is needed and they can probably be 41 | improved. 42 | 43 | """ 44 | 45 | # use Python 3 style print function rather than Python 2 print statements: 46 | from __future__ import print_function 47 | 48 | from IPython.display import display 49 | from matplotlib import image, animation 50 | from matplotlib import pyplot as plt 51 | from ipywidgets import interact, interact_manual 52 | import ipywidgets 53 | import io 54 | from matplotlib import pyplot as plt 55 | 56 | try: 57 | from JSAnimation import IPython_display 58 | except: 59 | try: 60 | from clawpack.visclaw.JSAnimation import IPython_display 61 | except: 62 | print("*** Warning: JSAnimation not found") 63 | 64 | 65 | 66 | def make_plotdir(plotdir='_plots', clobber=True): 67 | """ 68 | Utility function to create a directory for storing a sequence of plot 69 | files, or if the directory already exists, clear out any old plots. 70 | If clobber==False then it will abort instead of deleting existing files. 71 | """ 72 | 73 | import os 74 | if os.path.isdir(plotdir): 75 | if clobber: 76 | os.system("rm %s/*" % plotdir) 77 | else: 78 | raise IOError('*** Cannot clobber existing directory %s' % plotdir) 79 | else: 80 | os.system("mkdir %s" % plotdir) 81 | print("Figure files for each frame will be stored in ", plotdir) 82 | 83 | 84 | def save_frame(frameno, plotdir='_plots', fname_base='frame', format='png', 85 | verbose=False, **kwargs): 86 | """ 87 | After giving matplotlib commands to create the plot for a single frame 88 | of the desired animation, this can be called to save the figure with 89 | the appropriate file name such as _plots/frame00001.png. 90 | """ 91 | 92 | plt.draw() 93 | filename = '%s/%s%s.%s' % (plotdir, fname_base, str(frameno).zfill(5), format) 94 | plt.savefig(filename, **kwargs) 95 | if verbose: 96 | print("Saved ",filename) 97 | 98 | 99 | def make_anim(plotdir, fname_pattern='frame*.png', figsize=(10,6), dpi=None): 100 | """ 101 | Assumes that a set of frames are available as png files in directory _plots, 102 | numbered consecutively, e.g. frame0000.png, frame0001.png, etc. 103 | 104 | Creates an animation based display each frame in turn, and returns anim. 105 | 106 | You can then display anim in an IPython notebook, or 107 | call make_html(anim) to create a stand-alone webpage. 108 | """ 109 | 110 | import matplotlib 111 | 112 | if matplotlib.backends.backend in ['MacOSX']: 113 | print("*** animation.FuncAnimation doesn't work with backend %s" \ 114 | % matplotlib.backends.backend) 115 | print("*** Suggest using 'Agg'") 116 | return 117 | 118 | 119 | import glob # for finding all files matching a pattern 120 | 121 | # Find all frame files: 122 | filenames = glob.glob('%s/%s' % (plotdir, fname_pattern)) 123 | 124 | # sort them into increasing order: 125 | filenames=sorted(filenames) 126 | 127 | fig = plt.figure(figsize=figsize, dpi=dpi) 128 | ax = fig.add_axes([0, 0, 1, 1]) 129 | ax.axis('off') # so there's not a second set of axes 130 | im = plt.imshow(image.imread(filenames[0])) 131 | 132 | def init(): 133 | im.set_data(image.imread(filenames[0])) 134 | return im, 135 | 136 | def animate(i): 137 | image_i=image.imread(filenames[i]) 138 | im.set_data(image_i) 139 | return im, 140 | 141 | anim = animation.FuncAnimation(fig, animate, init_func=init, 142 | frames=len(filenames), interval=200, blit=True) 143 | 144 | return anim 145 | 146 | 147 | def JSAnimate_images(images, figsize=(10,6), dpi=None): 148 | 149 | import matplotlib 150 | 151 | if matplotlib.backends.backend in ['MacOSX']: 152 | print("*** animation.FuncAnimation doesn't work with backend %s" \ 153 | % matplotlib.backends.backend) 154 | print("*** Suggest using 'Agg'") 155 | return 156 | 157 | 158 | fig = plt.figure(figsize=figsize, dpi=None) 159 | ax = fig.add_axes([0, 0, 1, 1]) 160 | ax.axis('off') # so there's not a second set of axes 161 | 162 | im = plt.imshow(images[0]) 163 | 164 | def init(): 165 | im.set_data(images[0]) 166 | return im, 167 | 168 | def animate(i): 169 | im.set_data(images[i]) 170 | return im, 171 | 172 | anim = animation.FuncAnimation(fig, animate, init_func=init, 173 | frames=len(images), interval=200, blit=True) 174 | 175 | plt.close(fig) 176 | return anim 177 | 178 | 179 | def make_html(anim, file_name='anim.html', title=None, raw_html='', \ 180 | fps=None, embed_frames=True, default_mode='once'): 181 | """ 182 | Take an animation created by make_anim and convert it into a stand-alone 183 | html file. 184 | """ 185 | try: 186 | from JSAnimation.IPython_display import anim_to_html 187 | except: 188 | try: 189 | from clawpack.visclaw.JSAnimation.IPython_display import anim_to_html 190 | except: 191 | print("*** Warning: JSAnimation not found, cannot import anim_to_html") 192 | 193 | html_body = anim_to_html(anim, fps=fps, embed_frames=embed_frames, \ 194 | default_mode=default_mode) 195 | 196 | html_file = open(file_name,'w') 197 | html_file.write("\n

%s

\n" % title) 198 | html_file.write(raw_html) 199 | html_file.write(html_body) 200 | html_file.close() 201 | print("Created %s" % file_name) 202 | 203 | 204 | def make_rst(anim, file_name='anim.rst', 205 | fps=None, embed_frames=True, default_mode='once'): 206 | """ 207 | Take an animation created by make_anim and convert it into an rst file 208 | (reStructuredText, for inclusion in Sphinx documentation, for example). 209 | """ 210 | try: 211 | from JSAnimation.IPython_display import anim_to_html 212 | except: 213 | try: 214 | from clawpack.visclaw.JSAnimation.IPython_display import anim_to_html 215 | except: 216 | print("*** Warning: JSAnimation not found, cannot import anim_to_html") 217 | 218 | rst_body = anim_to_html(anim, fps=fps, embed_frames=embed_frames, \ 219 | default_mode=default_mode) 220 | 221 | rst_body = rst_body.split('\n') 222 | 223 | rst_file = open(file_name,'w') 224 | rst_file.write(".. raw:: html\n") 225 | for line in rst_body: 226 | rst_file.write(" %s\n" % line) 227 | rst_file.close() 228 | print("Created %s" % file_name) 229 | print("Imbed this in another rst file using:") 230 | print(".. include:: %s" % file_name) 231 | 232 | 233 | def make_mp4(anim, file_name='anim.mp4', 234 | fps=None, embed_frames=True, default_mode='once'): 235 | """ 236 | Take an animation and covert to mp4 file using ffmpeg, which must be 237 | installed. 238 | """ 239 | import os 240 | 241 | if not animation.writers.is_available('ffmpeg'): 242 | print("** ffmpeg must be installed to create mp4 file") 243 | return 244 | 245 | if os.path.splitext(file_name)[1] != '.mp4': 246 | print("*** Might not work if file extension is not .mp4") 247 | if fps is None: 248 | fps = 3 249 | writer = animation.writers['ffmpeg'](fps=fps) 250 | anim.save(file_name, writer=writer) 251 | print("Created %s" % file_name) 252 | 253 | 254 | def read_images(plotdir, fname_pattern='*.png'): 255 | 256 | import glob, os 257 | images = [] 258 | files = glob.glob(os.path.join(plotdir, fname_pattern)) 259 | for file in files: 260 | im = plt.imread(file) 261 | images.append(im) 262 | return images 263 | 264 | def save_images(images, figsize=(8,6), plotdir='_plots', clobber=True, \ 265 | fname_base='frame', format='png', verbose=False, **kwargs): 266 | 267 | make_plotdir(plotdir=plotdir, clobber=clobber) 268 | for frameno,image in enumerate(images): 269 | fig = imshow_noaxes(image, figsize) 270 | filename = '%s/%s%s.%s' % (plotdir, fname_base, str(frameno).zfill(5), format) 271 | plt.savefig(filename, format=format, **kwargs) 272 | plt.close(fig) 273 | if verbose: 274 | print("Saved ",filename) 275 | 276 | def save_figs(figs, plotdir='_plots', clobber=True, \ 277 | fname_base='frame', format='png', verbose=False, **kwargs): 278 | 279 | make_plotdir(plotdir=plotdir, clobber=clobber) 280 | for frameno,fig in enumerate(figs): 281 | filename = '%s/%s%s.%s' % (plotdir, fname_base, str(frameno).zfill(5), format) 282 | fig.savefig(filename, format=format, **kwargs) 283 | plt.close(fig) 284 | if verbose: 285 | print("Saved ",filename) 286 | 287 | 288 | def make_image(fig, **kwargs): 289 | """ 290 | Take a matplotlib figure *fig* and convert it to an image *im* that 291 | can be viewed with imshow. 292 | """ 293 | 294 | import io 295 | png = io.BytesIO() 296 | fig.savefig(png,format='png', **kwargs) 297 | png.seek(0) 298 | im = plt.imread(png) 299 | return im 300 | 301 | def make_images(figs, **kwargs): 302 | """ 303 | Take a list of matplotlib figures *figs* and convert to list of images. 304 | """ 305 | 306 | images = [] 307 | for fig in figs: 308 | im = make_image(fig, **kwargs) 309 | images.append(im) 310 | return images 311 | 312 | def imshow_noaxes(im, figsize=(8,6)): 313 | fig = plt.figure(figsize=figsize) 314 | ax = plt.axes() 315 | plt.imshow(im) 316 | ax.axis('off') 317 | return fig 318 | 319 | def interact_animate_images(images, figsize=(10,6), manual=False, TextInput=False): 320 | 321 | def display_frame(frameno): 322 | imshow_noaxes(images[frameno], figsize=figsize) 323 | plt.show() 324 | 325 | if TextInput: 326 | if TextInput: 327 | print("Valid frameno values: from %i to %i" % (0,len(images)-1)) 328 | widget = ipywidgets.IntText(min=0,max=len(images)-1, value=0) 329 | else: 330 | widget = ipywidgets.IntSlider(min=0,max=len(images)-1, value=0) 331 | 332 | if manual: 333 | interact_manual(display_frame, frameno=widget) 334 | else: 335 | interact(display_frame, frameno=widget) 336 | 337 | def interact_animate_figs(figs, manual=False, TextInput=False): 338 | 339 | def display_frame(frameno): 340 | display(figs[frameno]) 341 | 342 | if TextInput: 343 | widget = ipywidgets.IntText(min=0,max=len(figs)-1, value=0) 344 | else: 345 | widget = ipywidgets.IntSlider(min=0,max=len(figs)-1, value=0) 346 | 347 | if manual: 348 | if TextInput: 349 | print("Valid frameno values: from %i to %i" % (0,len(figs)-1)) 350 | interact_manual(display_frame, frameno=widget) 351 | else: 352 | interact(display_frame, frameno=widget) 353 | 354 | 355 | def animate_figs(figs, style='ipywidgets', figsize=(10,6), **kwargs): 356 | """ 357 | Create an animation from a set of figures, 358 | style = 'ipywidgets' or 'JSAnimation' 359 | returns anim 360 | """ 361 | 362 | images = make_images(figs) 363 | 364 | if style == 'ipywidgets': 365 | anim = interact_animate_images(images, figsize=figsize, **kwargs) 366 | elif style == 'JSAnimation': 367 | anim = JSAnimate_images(images, figsize=figsize, **kwargs) 368 | else: 369 | raise ValueError('** Unrecognized style = %s' % style) 370 | 371 | return anim 372 | 373 | def make_anim_from_plotdir(plotdir='_plots', fignos='all', 374 | outputs=[], file_name_prefix='', figsize=(5,4), dpi=None, fps=5): 375 | 376 | """ 377 | After running `make plots` using VisClaw, convert the png files in 378 | the plots directory into an animation, and perhaps also 379 | stand-alone files that can be embedded in webpages or Sphinx documentation. 380 | 381 | outputs can be a list containing any of 'mp4','html','rst' 382 | 383 | Call this from a script that starts with: 384 | import matplotlib 385 | matplotlib.use('Agg') 386 | """ 387 | import glob, re 388 | 389 | if fignos == 'all': 390 | # determine what fignos are used in the plotdir 391 | movie_files = glob.glob(plotdir + '/movie*html') 392 | if len(movie_files) == 0: 393 | print('No movie files found in %s' % plotdir) 394 | return 395 | 396 | fignos = [] 397 | regexp = re.compile(r"movie[^ ]*fig(?P[0-9]*)[.html]") 398 | for f in movie_files: 399 | result = regexp.search(f) 400 | fignos.append(result.group('figno')) 401 | 402 | print("Found these figures: %s" % fignos) 403 | 404 | 405 | for figno in fignos: 406 | 407 | fname_pattern = 'frame*fig%s.png' % figno 408 | anim = make_anim(plotdir, fname_pattern, figsize, dpi) 409 | 410 | if 'mp4' in outputs: 411 | file_name = file_name_prefix + 'fig%s.mp4' % figno 412 | make_mp4(anim, file_name, fps=fps, \ 413 | embed_frames=True, default_mode='once') 414 | 415 | if 'html' in outputs: 416 | file_name = file_name_prefix + 'fig%s.html' % figno 417 | make_html(anim, file_name, fps=fps, \ 418 | embed_frames=True, default_mode='once') 419 | 420 | if 'rst' in outputs: 421 | file_name = file_name_prefix + 'fig%s.rst' % figno 422 | make_rst(anim, file_name, fps=fps, \ 423 | embed_frames=True, default_mode='once') 424 | 425 | return anim 426 | --------------------------------------------------------------------------------