├── .gitignore ├── LICENSE ├── README.md ├── course_description.tex ├── lessons ├── 00_Quick_Python_Intro.ipynb ├── 01_Step_1.ipynb ├── 02_Step_2.ipynb ├── 03_CFL_Condition.ipynb ├── 04_Step_3.ipynb ├── 05_Step_4.ipynb ├── 06_Array_Operations_with_NumPy.ipynb ├── 07_Step_5.ipynb ├── 08_Step_6.ipynb ├── 09_Step_7.ipynb ├── 10_Step_8.ipynb ├── 11_Defining_Function_in_Python.ipynb ├── 12_Step_9.ipynb ├── 13_Step_10.ipynb ├── 14_Step_11.ipynb └── 15_Step_12.ipynb ├── paper.bib ├── paper.md ├── requirements.txt └── styles └── custom.css /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | lessons/.ipynb_checkpoints 3 | .env 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2013 Lorena A. Barba, Gilbert F. Forsyth 3 | 4 | 5 | Instructional Material 6 | ====================== 7 | 8 | All instructional material is made available under the Creative 9 | Commons Attribution license. You are free: 10 | 11 | * to Share---to copy, distribute and transmit the work 12 | * to Remix---to adapt the work 13 | 14 | Under the following conditions: 15 | 16 | * Attribution---You must attribute the work using "Copyright (c) 17 | Barba group" (but not in any way that suggests that we 18 | endorse you or your use of the work). Where practical, you must 19 | also include a hyperlink to https://github.com/barbagroup/CFDPython. 20 | 21 | With the understanding that: 22 | 23 | * Waiver---Any of the above conditions can be waived if you get 24 | permission from the copyright holder. 25 | * Other Rights---In no way are any of the following rights 26 | affected by the license: 27 | * Your fair dealing or fair use rights; 28 | * The author's moral rights; 29 | * Rights other persons may have either in the work itself or in 30 | how the work is used, such as publicity or privacy rights. * 31 | * Notice---For any reuse or distribution, you must make clear to 32 | others the license terms of this work. The best way to do this is 33 | with a link to http://creativecommons.org/licenses/by/3.0/. 34 | 35 | For the full legal text of this license, please see: 36 | http://creativecommons.org/licenses/by/3.0/legalcode 37 | 38 | 39 | 40 | Software 41 | ========= 42 | 43 | Except where otherwise noted, all software is made available under the 44 | OSI-approved BSD-3-Clause license (https://opensource.org/licenses/BSD-3-Clause): 45 | 46 | Redistribution and use in source and binary forms, with or without modification, 47 | are permitted provided that the following conditions are met: 48 | 49 | 1. Redistributions of source code must retain the above copyright notice, this 50 | list of conditions and the following disclaimer. 51 | 52 | 2. Redistributions in binary form must reproduce the above copyright notice, this 53 | list of conditions and the following disclaimer in the documentation and/or other 54 | materials provided with the distribution. 55 | 56 | 3. Neither the name of the copyright holder nor the names of its contributors may 57 | be used to endorse or promote products derived from this software without specific 58 | prior written permission. 59 | 60 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 61 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 62 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 63 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 64 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 65 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 66 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 67 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 68 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 69 | OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # CFD Python 3 | 4 | > Please cite as: Barba, Lorena A., and Forsyth, Gilbert F. (2018). CFD Python: the 12 steps to Navier-Stokes equations. _Journal of Open Source Education_, **1**(9), 21, https://doi.org/10.21105/jose.00021 5 | 6 | [![DOI](https://jose.theoj.org/papers/10.21105/jose.00021/status.svg)](https://doi.org/10.21105/jose.00021) 7 | 8 | **CFD Python**, a.k.a. the **12 steps to Navier-Stokes**, is a practical module for learning the foundations of Computational Fluid Dynamics (CFD) by coding solutions to the basic partial differential equations that describe the physics of fluid flow. 9 | The module was part of a course taught by [Prof. Lorena Barba](http://lorenabarba.com) between 2009 and 2013 in the Mechanical Engineering department at Boston University (Prof. Barba since moved to the George Washington University). 10 | 11 | The module assumes only basic programming knowledge (in any language) and some background in partial differential equations and fluid mechanics. The "steps" were inspired by ideas of Dr. Rio Yokota, who was a post-doc in Prof. Barba's lab until 2011, and the lessons were refined by Prof. Barba and her students over several semesters teaching the CFD course. 12 | We wrote this set of Jupyter notebooks in 2013 to teach an intensive two-day course in Mendoza, Argentina. 13 | 14 | Guiding students through these steps (without skipping any!), they learn many valuable lessons. The incremental nature of the exercises means they get a sense of achievement at the end of each assignment, and they feel they are learning with low effort. As they progress, they naturally practice code re-use and they incrementally learn programming and plotting techniques. As they analyze their results, they learn about numerical diffusion, accuracy and convergence. 15 | In about four weeks of a regularly scheduled course, they become moderately proficient programmers and are motivated to start discussing more theoretical matters. 16 | 17 | ## How to use this module 18 | 19 | In a regular-session university course, students can complete the **CFD Python** lessons in 4 to 5 weeks. 20 | As an intensive tutorial, the module can be completed in two or three full days, depending on the learner's prior experience. 21 | The lessons can also be used for self study. 22 | In all cases, learners should follow along the worked examples in each lesson by re-typing the code in a fresh Jupyter notebook, maybe taking original notes as they try things out. 23 | 24 | Lessons 25 | ------- 26 | > Launch an interactive session with this module using the Binder service: 27 | [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/barbagroup/CFDPython/master) 28 | 29 | Steps 1–4 are in one spatial dimension. Steps 5–10 are in two dimensions (2D). Steps 11–12 solve the Navier-Stokes equation in 2D. Three "bonus" notebooks cover the CFL condition for numerical stability, array operations with NumPy, and defining functions in Python. 30 | 31 | * [Quick Python Intro](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/00_Quick_Python_Intro.ipynb) 32 | —For Python novices, this lesson introduces the numerical libraries (NumPy and Matplotlib), Python variables, use of whitespace, and slicing arrays. 33 | * [Step 1](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/01_Step_1.ipynb) 34 | —Linear convection with a step-function initial condition (IC) and appropriate boundary conditions (BCs). 35 | * [Step 2](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/02_Step_2.ipynb) 36 | —With the same IC/BCs, _nonlinear_ convection. 37 | * [CFL Condition](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/03_CFL_Condition.ipynb) 38 | —Exploring numerical stability and the Courant-Friedrichs-Lewy (CFL) condition. 39 | * [Step 3](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/04_Step_3.ipynb) 40 | —With the same IC/BCs, _diffusion_ only. 41 | * [Step 4](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/05_Step_4.ipynb) 42 | —Burgers’ equation, with a saw-tooth IC and periodic BCs (with an introduction to Sympy). 43 | * [Array Operations with NumPy](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/06_Array_Operations_with_NumPy.ipynb) 44 | * [Step 5](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/07_Step_5.ipynb) 45 | —Linear convection in 2D with a square-function IC and appropriate BCs. 46 | * [Step 6](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/08_Step_6.ipynb) 47 | —With the same IC/BCs, _nonlinear_ convection in 2D. 48 | * [Step 7](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/09_Step_7.ipynb) 49 | —With the same IC/BCs, _diffusion_ in 2D. 50 | * [Step 8](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/10_Step_8.ipynb) 51 | —Burgers’ equation in 2D 52 | * [Defining Functions in Python](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/11_Defining_Function_in_Python.ipynb) 53 | * [Step 9](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/12_Step_9.ipynb) 54 | —Laplace equation with zero IC and both Neumann and Dirichlet BCs. 55 | * [Step 10](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/13_Step_10.ipynb) 56 | —Poisson equation in 2D. 57 | * [Step 11](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/14_Step_11.ipynb) 58 | —Solves the Navier-Stokes equation for 2D cavity flow. 59 | * [Step 12](http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/15_Step_12.ipynb) 60 | —Solves the Navier-Stokes equation for 2D channel flow. 61 | 62 | 63 | 64 | 65 | ## Dependencies 66 | 67 | To use these lessons, you need Python 3, and the standard stack of scientific Python: NumPy, Matplotlib, SciPy, Sympy. And of course, you need [Jupyter](http://jupyter.org)—an interactive computational environment that runs on a web browser. 68 | 69 | This mini-course is built as a set of [Jupyter notebooks](https://jupyter-notebook.readthedocs.org/en/latest/notebook.html) containing the written materials and worked-out solutions on Python code. To work with the material, we recommend that you start each lesson with a fresh new notebook, and follow along, typing each line of code (don't copy-and-paste!), and exploring by changing parameters and seeing what happens. 70 | 71 | 72 |
73 | Installing via Anaconda 74 |
75 | We *highly* recommend that you install the [Anaconda Python Distribution](http://docs.continuum.io/anaconda/install). It will make your life so much easier. 76 | You can download and install Anaconda on Windows, OSX and Linux. 77 | 78 | After installing, to ensure that your packages are up to date, run the following commands in a terminal: 79 | 80 | ```Bash 81 | conda update conda 82 | conda update jupyter numpy sympy scipy matplotlib 83 | ``` 84 | 85 | If you prefer Miniconda (a mini version of Anaconda that saves you disk space), install all the necessary libraries to follow this course by running the following commands in a terminal: 86 | 87 | ```Bash 88 | conda update conda 89 | conda install jupyter 90 | conda install numpy scipy sympy matplotlib 91 | ``` 92 |
93 | 94 |
95 | Without Anaconda 96 |
97 | If you already have Python installed on your machine, you can install Jupyter using pip: 98 | 99 | ```Bash 100 | pip install jupyter 101 | ``` 102 | 103 | Please also make sure that you have the necessary libraries installed by running 104 | 105 | ```Bash 106 | pip install numpy scipy sympy matplotlib 107 | ``` 108 |
109 | 110 | 111 | 112 | ## How to contribute to CFD Python 113 | 114 | We accept contributions via pull request—in fact, several users have already submitted pull requests making corrections or small improvements. You can also open an issue if you find a bug, or have a suggestion. 115 | 116 | ## Copyright and License 117 | 118 | (c) 2017 Lorena A. Barba, Gilbert F. Forsyth. All content is under Creative Commons Attribution [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/legalcode.txt), and all [code is under BSD-3 clause](https://github.com/engineersCode/EngComp/blob/master/LICENSE) (previously under MIT, and changed on March 8, 2018). 119 | 120 | We are happy if you re-use the content in any way! 121 | 122 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![License: CC BY 4.0](https://img.shields.io/badge/License-CC%20BY%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by/4.0/) 123 | 124 | -------------------------------------------------------------------------------- /course_description.tex: -------------------------------------------------------------------------------- 1 | \documentclass[10pt,a4paper]{report} 2 | \usepackage[utf8]{inputenc} 3 | \usepackage{amsmath} 4 | \usepackage{amsfonts} 5 | \usepackage{amssymb} 6 | \usepackage{graphicx} 7 | \usepackage{booktabs} 8 | \usepackage{units} 9 | \usepackage{fancyvrb} 10 | \fvset{fontsize=\normalsize} 11 | \usepackage[left=2cm,right=2cm,top=2cm,bottom=2cm]{geometry} 12 | \usepackage{multicol} 13 | 14 | \title{CFD Python Modules} 15 | 16 | \begin{document} 17 | \section*{Quick Python Intro} 18 | A \textit{very} quick crash-course in the basics of Python, broadly covering 19 | \begin{itemize} 20 | \item[] Libraries (NumPy and Matplotlib, specifically) 21 | \item[] Variables 22 | \item[] Whitespace 23 | \item[] Array slicing and assigment 24 | \end{itemize} 25 | 26 | \section*{Step 1: 1-D Linear Convection} 27 | 28 | \begin{equation} 29 | \frac{\partial u}{\partial t} + c \frac{\partial u}{\partial x} = 0 30 | \end{equation} 31 | 32 | \subsection*{Math} 33 | This section introduces the reader to the ``parts`` of the PDE and how to discretize them. 34 | \begin{itemize} 35 | \item[] Introduce the idea of a grid 36 | \item[] Expand equation using the definition of a derivative 37 | \item[] Discretize into small chunks 38 | \item[] Re-arrange to solve for $u^{n+1}_i$ 39 | \item[] Initial and boundary conditions 40 | \end{itemize} 41 | 42 | \subsection*{Python} 43 | \begin{itemize} 44 | \item[] Importing libraries 45 | \item[] Assigning variables 46 | \item[] Basic 2D plotting 47 | \item[] Simple for-loops 48 | \end{itemize} 49 | 50 | \vspace{.5cm} 51 | YouTube videos on order of convergence, truncation error, etc... 52 | 53 | \section*{Step 2: 1-D Nonlinear Convection} 54 | 55 | \begin{equation} 56 | \frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} = 0 57 | \end{equation} 58 | 59 | \subsection*{Math} 60 | \begin{itemize} 61 | \item[] Introduce non-linear PDE equation 62 | \item[] Expand equation using definition of derivative 63 | \item[] Discretize 64 | \item[] Solve for $u^{n+1}_i$ 65 | \end{itemize} 66 | 67 | \section*{CFL Condition} 68 | A short side-trip into the CFL condition, order of convergence and blowing things up. 69 | 70 | \subsection*{Math} 71 | \begin{itemize} 72 | \item[] The Courant number 73 | \item[] Explanation of blow-up behavior when wave travels a distance $> dx$ during a time $dt$ 74 | \end{itemize} 75 | 76 | \subsection*{Python} 77 | \begin{itemize} 78 | \item[] Quick introduction to defining a function to use code repeatedly 79 | \end{itemize} 80 | 81 | 82 | \section*{Step 3: 1-D Diffusion} 83 | 84 | \begin{equation} 85 | \frac{\partial u}{\partial t} = \nu \frac{\partial ^2 u}{\partial x^2} 86 | \end{equation} 87 | 88 | \subsection*{Math} 89 | 90 | \begin{itemize} 91 | \item[] Introduce diffusion equation 92 | \item[] Discretize 2nd order derivative using Taylor series expansion 93 | \item[] Discretize time derivative using def. of derivative 94 | \end{itemize} 95 | 96 | \subsection*{Python} 97 | 98 | Nothing new, still no functions being used (yet) 99 | 100 | \section*{Step 4: 1-D Burgers' Equation} 101 | 102 | \begin{equation} 103 | \frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} = \nu \frac{\partial ^2 u}{\partial x^2} 104 | \end{equation} 105 | 106 | \subsection*{Math} 107 | 108 | \begin{itemize} 109 | \item[] Introduce Burgers' Equation 110 | \item[] Note that it is combination of diffusion and non-linear convection 111 | \item[] Introduce different I.C. and B.C. for periodic behavior 112 | \begin{itemize} 113 | \item[] e.g. What does $u^{n}_{i+1}$ \textit{mean} at the end of the frame? 114 | \end{itemize} 115 | \end{itemize} 116 | 117 | \subsection*{Python} 118 | 119 | \begin{itemize} 120 | \item[] Introduce Sympy 121 | \begin{itemize} 122 | \item[] Pretty printing 123 | \item[] Symbolic solving of derivatives 124 | \item[] Usage of Lambdify to make solutions `accessible' to Numpy 125 | \end{itemize} 126 | \item[] Matplotlib 127 | \begin{itemize} 128 | \item[] Plotting multiple lines per plot 129 | \item[] Setting line styles 130 | \item[] Legends 131 | \end{itemize} 132 | \end{itemize} 133 | 134 | \section*{Array Operations} 135 | Another brief interlude to introduce handling calculations with array operations instead of iterating over the entire array. 136 | 137 | \subsection*{Python} 138 | 139 | \begin{itemize} 140 | \item[] Array operations, slicing and copying 141 | \item[] Note about using the \verb|%%timeit| magic function to compare performance 142 | \end{itemize} 143 | 144 | \section*{Step 5: 2D Linear Convection} 145 | 146 | \subsection*{Math} 147 | \begin{itemize} 148 | \item[] Introduction to 2D grid 149 | \item[] Extension of current discretization rules into $i,j$ flatland 150 | \item[] Discretize 2D equation and solve for unknown 151 | \end{itemize} 152 | 153 | \subsection*{Python} 154 | 155 | \begin{itemize} 156 | \item[] meshgrid 157 | \item[] Axes3D 158 | \item[] surf and wireframe plots 159 | \item[] Demonstration that nested for-loop results and array operations results are the same 160 | \end{itemize} 161 | 162 | \section*{Step 6: 2D Nonlinear Convection} 163 | 164 | \subsection*{Math} 165 | \begin{itemize} 166 | \item[] Introduction of coupled PDEs 167 | \item[] Discretization of two equations 168 | \item[] Solving for both $u^{n+1}_{i,j}$ and $v^{n+1}_{i,j}$ 169 | \end{itemize} 170 | 171 | \section*{Step 7: 2D Diffusion} 172 | 173 | \subsection*{Math} 174 | \begin{itemize} 175 | \item[] Introduction to 2D diffusion equation 176 | \item[] Discretization of equation, etc... 177 | \end{itemize} 178 | 179 | \subsection*{Python} 180 | Nothing new, although functions are used to display results (probably should switch this over to \texttt{jsanim}) 181 | 182 | \section*{Step 8: 2D Burgers'} 183 | 184 | \end{document} -------------------------------------------------------------------------------- /lessons/00_Quick_Python_Intro.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Python Crash Course" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Hello! This is a quick intro to programming in Python to help you hit the ground running with the _12 Steps to Navier–Stokes_. \n", 15 | "\n", 16 | "There are two ways to enjoy these lessons with Python:\n", 17 | "\n", 18 | "1. You can download and install a Python distribution on your computer. One option is the free [Anaconda Scientific Python](https://store.continuum.io/cshop/anaconda/) distribution. Another is [Canopy](https://www.enthought.com/products/canopy/academic/), which is free for academic use. Our recommendation is Anaconda.\n", 19 | "\n", 20 | "2. You can run Python in the cloud using [Wakari](https://wakari.io/) web-based data analysis, for which you need to create a free account. (No software installation required!)\n", 21 | "\n", 22 | "In either case, you will probably want to download a copy of this notebook, or the whole AeroPython collection. We recommend that you then follow along each lesson, experimenting with the code in the notebooks, or typing the code into a separate Python interactive session.\n", 23 | "\n", 24 | "If you decided to work on your local Python installation, you will have to navigate in the terminal to the folder that contains the .ipynb files. Then, to launch the notebook server, just type:\n", 25 | "ipython notebook\n", 26 | "\n", 27 | "You will get a new browser window or tab with a list of the notebooks available in that folder. Click on one and start working!" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "## Libraries" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "Python is a high-level open-source language. But the _Python world_ is inhabited by many packages or libraries that provide useful things like array operations, plotting functions, and much more. We can import libraries of functions to expand the capabilities of Python in our programs. \n", 42 | "\n", 43 | "OK! We'll start by importing a few libraries to help us out. First: our favorite library is **NumPy**, providing a bunch of useful array operations (similar to MATLAB). We will use it a lot! The second library we need is **Matplotlib**, a 2D plotting library which we will use to plot our results.\n", 44 | "The following code will be at the top of most of your programs, so execute this cell first:" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 1, 50 | "metadata": { 51 | "collapsed": false 52 | }, 53 | "outputs": [], 54 | "source": [ 55 | "# <-- comments in python are denoted by the pound sign, like this one\n", 56 | "\n", 57 | "import numpy # we import the array library\n", 58 | "from matplotlib import pyplot # import plotting library" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "We are importing one library named `numpy` and we are importing a module called `pyplot` of a big library called `matplotlib`.\n", 66 | "To use a function belonging to one of these libraries, we have to tell Python where to look for it. For that, each function name is written following the library name, with a dot in between.\n", 67 | "So if we want to use the NumPy function [linspace()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html), which creates an array with equally spaced numbers between a start and end, we call it by writing:" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 2, 73 | "metadata": { 74 | "collapsed": false 75 | }, 76 | "outputs": [ 77 | { 78 | "data": { 79 | "text/plain": [ 80 | "array([ 0. , 0.55555556, 1.11111111, 1.66666667, 2.22222222,\n", 81 | " 2.77777778, 3.33333333, 3.88888889, 4.44444444, 5. ])" 82 | ] 83 | }, 84 | "execution_count": 2, 85 | "metadata": {}, 86 | "output_type": "execute_result" 87 | } 88 | ], 89 | "source": [ 90 | "myarray = numpy.linspace(0, 5, 10)\n", 91 | "myarray" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "If we don't preface the `linspace()` function with `numpy`, Python will throw an error." 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 4, 104 | "metadata": { 105 | "collapsed": false 106 | }, 107 | "outputs": [ 108 | { 109 | "ename": "NameError", 110 | "evalue": "name 'linspace' is not defined", 111 | "output_type": "error", 112 | "traceback": [ 113 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 114 | "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", 115 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmyarray\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mlinspace\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m5\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 116 | "\u001b[1;31mNameError\u001b[0m: name 'linspace' is not defined" 117 | ] 118 | } 119 | ], 120 | "source": [ 121 | "myarray = linspace(0, 5, 10)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "The function `linspace()` is very useful. Try it changing the input parameters!\n", 129 | "\n", 130 | "**Import style:**\n", 131 | "\n", 132 | "You will often see code snippets that use the following lines\n", 133 | "```Python\n", 134 | "import numpy as np\n", 135 | "import matplotlib.pyplot as plt\n", 136 | "```\n", 137 | "What's all of this import-as business? It's a way of creating a 'shortcut' to the NumPy library and the pyplot module. You will see it frequently as it is in common usage, but we prefer to keep out imports explicit. We think it helps with code readability.\n", 138 | "\n", 139 | "**Pro tip:**\n", 140 | "\n", 141 | "Sometimes, you'll see people importing a whole library without assigning a shortcut for it (like `from numpy import *`). This saves typing but is sloppy and can get you in trouble. Best to get into good habits from the beginning!\n", 142 | "\n", 143 | "\n", 144 | "To learn new functions available to you, visit the [NumPy Reference](http://docs.scipy.org/doc/numpy/reference/) page. If you are a proficient `Matlab` user, there is a wiki page that should prove helpful to you: [NumPy for Matlab Users](http://wiki.scipy.org/NumPy_for_Matlab_Users)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "## Variables" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "Python doesn't require explicitly declared variable types like C and other languages. " 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 5, 164 | "metadata": { 165 | "collapsed": false 166 | }, 167 | "outputs": [], 168 | "source": [ 169 | "a = 5 #a is an integer 5\n", 170 | "b = 'five' #b is a string of the word 'five'\n", 171 | "c = 5.0 #c is a floating point 5 " 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 6, 177 | "metadata": { 178 | "collapsed": false 179 | }, 180 | "outputs": [ 181 | { 182 | "data": { 183 | "text/plain": [ 184 | "int" 185 | ] 186 | }, 187 | "execution_count": 6, 188 | "metadata": {}, 189 | "output_type": "execute_result" 190 | } 191 | ], 192 | "source": [ 193 | "type(a)" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 7, 199 | "metadata": { 200 | "collapsed": false 201 | }, 202 | "outputs": [ 203 | { 204 | "data": { 205 | "text/plain": [ 206 | "str" 207 | ] 208 | }, 209 | "execution_count": 7, 210 | "metadata": {}, 211 | "output_type": "execute_result" 212 | } 213 | ], 214 | "source": [ 215 | "type(b)" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": 8, 221 | "metadata": { 222 | "collapsed": false 223 | }, 224 | "outputs": [ 225 | { 226 | "data": { 227 | "text/plain": [ 228 | "float" 229 | ] 230 | }, 231 | "execution_count": 8, 232 | "metadata": {}, 233 | "output_type": "execute_result" 234 | } 235 | ], 236 | "source": [ 237 | "type(c)" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "Note that if you divide an integer by an integer that yields a remainder, the result will be converted to a float. (This is *different* from the behavior in Python 2.7, beware!)" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "## Whitespace in Python" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "Python uses indents and whitespace to group statements together. To write a short loop in C, you might use:\n", 259 | "\n", 260 | " for (i = 0, i < 5, i++){\n", 261 | " printf(\"Hi! \\n\");\n", 262 | " }" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": {}, 268 | "source": [ 269 | "Python does not use curly braces like C, so the same program as above is written in Python as follows:" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": 9, 275 | "metadata": { 276 | "collapsed": false 277 | }, 278 | "outputs": [ 279 | { 280 | "name": "stdout", 281 | "output_type": "stream", 282 | "text": [ 283 | "Hi \n", 284 | "\n", 285 | "Hi \n", 286 | "\n", 287 | "Hi \n", 288 | "\n", 289 | "Hi \n", 290 | "\n", 291 | "Hi \n", 292 | "\n" 293 | ] 294 | } 295 | ], 296 | "source": [ 297 | "for i in range(5):\n", 298 | " print(\"Hi \\n\")" 299 | ] 300 | }, 301 | { 302 | "cell_type": "markdown", 303 | "metadata": {}, 304 | "source": [ 305 | "If you have nested for-loops, there is a further indent for the inner loop." 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": 10, 311 | "metadata": { 312 | "collapsed": false 313 | }, 314 | "outputs": [ 315 | { 316 | "name": "stdout", 317 | "output_type": "stream", 318 | "text": [ 319 | "0 0\n", 320 | "0 1\n", 321 | "0 2\n", 322 | "This statement is within the i-loop, but not the j-loop\n", 323 | "1 0\n", 324 | "1 1\n", 325 | "1 2\n", 326 | "This statement is within the i-loop, but not the j-loop\n", 327 | "2 0\n", 328 | "2 1\n", 329 | "2 2\n", 330 | "This statement is within the i-loop, but not the j-loop\n" 331 | ] 332 | } 333 | ], 334 | "source": [ 335 | "for i in range(3):\n", 336 | " for j in range(3):\n", 337 | " print(i, j)\n", 338 | " \n", 339 | " print(\"This statement is within the i-loop, but not the j-loop\")" 340 | ] 341 | }, 342 | { 343 | "cell_type": "markdown", 344 | "metadata": {}, 345 | "source": [ 346 | "## Slicing Arrays" 347 | ] 348 | }, 349 | { 350 | "cell_type": "markdown", 351 | "metadata": {}, 352 | "source": [ 353 | "In NumPy, you can look at portions of arrays in the same way as in `Matlab`, with a few extra tricks thrown in. Let's take an array of values from 1 to 5." 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 11, 359 | "metadata": { 360 | "collapsed": false 361 | }, 362 | "outputs": [ 363 | { 364 | "data": { 365 | "text/plain": [ 366 | "array([1, 2, 3, 4, 5])" 367 | ] 368 | }, 369 | "execution_count": 11, 370 | "metadata": {}, 371 | "output_type": "execute_result" 372 | } 373 | ], 374 | "source": [ 375 | "myvals = numpy.array([1, 2, 3, 4, 5])\n", 376 | "myvals" 377 | ] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": {}, 382 | "source": [ 383 | "Python uses a **zero-based index**, so let's look at the first and last element in the array `myvals`" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": 12, 389 | "metadata": { 390 | "collapsed": false 391 | }, 392 | "outputs": [ 393 | { 394 | "data": { 395 | "text/plain": [ 396 | "(1, 5)" 397 | ] 398 | }, 399 | "execution_count": 12, 400 | "metadata": {}, 401 | "output_type": "execute_result" 402 | } 403 | ], 404 | "source": [ 405 | "myvals[0], myvals[4]" 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": {}, 411 | "source": [ 412 | "There are 5 elements in the array `myvals`, but if we try to look at `myvals[5]`, Python will be unhappy, as `myvals[5]` is actually calling the non-existant 6th element of that array." 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 13, 418 | "metadata": { 419 | "collapsed": false 420 | }, 421 | "outputs": [ 422 | { 423 | "ename": "IndexError", 424 | "evalue": "index 5 is out of bounds for axis 0 with size 5", 425 | "output_type": "error", 426 | "traceback": [ 427 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 428 | "\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)", 429 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmyvals\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m5\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 430 | "\u001b[1;31mIndexError\u001b[0m: index 5 is out of bounds for axis 0 with size 5" 431 | ] 432 | } 433 | ], 434 | "source": [ 435 | "myvals[5]" 436 | ] 437 | }, 438 | { 439 | "cell_type": "markdown", 440 | "metadata": {}, 441 | "source": [ 442 | "Arrays can also be 'sliced', grabbing a range of values. Let's look at the first three elements" 443 | ] 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": 14, 448 | "metadata": { 449 | "collapsed": false 450 | }, 451 | "outputs": [ 452 | { 453 | "data": { 454 | "text/plain": [ 455 | "array([1, 2, 3])" 456 | ] 457 | }, 458 | "execution_count": 14, 459 | "metadata": {}, 460 | "output_type": "execute_result" 461 | } 462 | ], 463 | "source": [ 464 | "myvals[0:3]" 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "metadata": {}, 470 | "source": [ 471 | "Note here, the slice is inclusive on the front end and exclusive on the back, so the above command gives us the values of `myvals[0]`, `myvals[1]` and `myvals[2]`, but not `myvals[3]`." 472 | ] 473 | }, 474 | { 475 | "cell_type": "markdown", 476 | "metadata": {}, 477 | "source": [ 478 | "## Assigning Array Variables" 479 | ] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "metadata": {}, 484 | "source": [ 485 | "One of the strange little quirks/features in Python that often confuses people comes up when assigning and comparing arrays of values. Here is a quick example. Let's start by defining a 1-D array called $a$:" 486 | ] 487 | }, 488 | { 489 | "cell_type": "code", 490 | "execution_count": 15, 491 | "metadata": { 492 | "collapsed": false 493 | }, 494 | "outputs": [], 495 | "source": [ 496 | "a = numpy.linspace(1,5,5)" 497 | ] 498 | }, 499 | { 500 | "cell_type": "code", 501 | "execution_count": 16, 502 | "metadata": { 503 | "collapsed": false 504 | }, 505 | "outputs": [ 506 | { 507 | "data": { 508 | "text/plain": [ 509 | "array([ 1., 2., 3., 4., 5.])" 510 | ] 511 | }, 512 | "execution_count": 16, 513 | "metadata": {}, 514 | "output_type": "execute_result" 515 | } 516 | ], 517 | "source": [ 518 | "a" 519 | ] 520 | }, 521 | { 522 | "cell_type": "markdown", 523 | "metadata": {}, 524 | "source": [ 525 | "OK, so we have an array $a$, with the values 1 through 5. I want to make a copy of that array, called $b$, so I'll try the following:" 526 | ] 527 | }, 528 | { 529 | "cell_type": "code", 530 | "execution_count": 17, 531 | "metadata": { 532 | "collapsed": false 533 | }, 534 | "outputs": [], 535 | "source": [ 536 | "b = a" 537 | ] 538 | }, 539 | { 540 | "cell_type": "code", 541 | "execution_count": 18, 542 | "metadata": { 543 | "collapsed": false 544 | }, 545 | "outputs": [ 546 | { 547 | "data": { 548 | "text/plain": [ 549 | "array([ 1., 2., 3., 4., 5.])" 550 | ] 551 | }, 552 | "execution_count": 18, 553 | "metadata": {}, 554 | "output_type": "execute_result" 555 | } 556 | ], 557 | "source": [ 558 | "b" 559 | ] 560 | }, 561 | { 562 | "cell_type": "markdown", 563 | "metadata": {}, 564 | "source": [ 565 | "Great. So $a$ has the values 1 through 5 and now so does $b$. Now that I have a backup of $a$, I can change its values without worrying about losing data (or so I may think!)." 566 | ] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": 19, 571 | "metadata": { 572 | "collapsed": false 573 | }, 574 | "outputs": [], 575 | "source": [ 576 | "a[2] = 17" 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": 20, 582 | "metadata": { 583 | "collapsed": false 584 | }, 585 | "outputs": [ 586 | { 587 | "data": { 588 | "text/plain": [ 589 | "array([ 1., 2., 17., 4., 5.])" 590 | ] 591 | }, 592 | "execution_count": 20, 593 | "metadata": {}, 594 | "output_type": "execute_result" 595 | } 596 | ], 597 | "source": [ 598 | "a" 599 | ] 600 | }, 601 | { 602 | "cell_type": "markdown", 603 | "metadata": {}, 604 | "source": [ 605 | "Here, the 3rd element of $a$ has been changed to 17. Now let's check on $b$." 606 | ] 607 | }, 608 | { 609 | "cell_type": "code", 610 | "execution_count": 21, 611 | "metadata": { 612 | "collapsed": false 613 | }, 614 | "outputs": [ 615 | { 616 | "data": { 617 | "text/plain": [ 618 | "array([ 1., 2., 17., 4., 5.])" 619 | ] 620 | }, 621 | "execution_count": 21, 622 | "metadata": {}, 623 | "output_type": "execute_result" 624 | } 625 | ], 626 | "source": [ 627 | "b" 628 | ] 629 | }, 630 | { 631 | "cell_type": "markdown", 632 | "metadata": {}, 633 | "source": [ 634 | "And that's how things go wrong! When you use a statement like $a = b$, rather than copying all the values of $a$ into a new array called $b$, Python just creates an alias (or a pointer) called $b$ and tells it to route us to $a$. So if we change a value in $a$ then $b$ will reflect that change (technically, this is called *assignment by reference*). If you want to make a true copy of the array, you have to tell Python to copy every element of $a$ into a new array. Let's call it $c$. " 635 | ] 636 | }, 637 | { 638 | "cell_type": "code", 639 | "execution_count": 22, 640 | "metadata": { 641 | "collapsed": false 642 | }, 643 | "outputs": [], 644 | "source": [ 645 | "c = a.copy()" 646 | ] 647 | }, 648 | { 649 | "cell_type": "markdown", 650 | "metadata": {}, 651 | "source": [ 652 | "Now, we can try again to change a value in $a$ and see if the changes are also seen in $c$. " 653 | ] 654 | }, 655 | { 656 | "cell_type": "code", 657 | "execution_count": 23, 658 | "metadata": { 659 | "collapsed": false 660 | }, 661 | "outputs": [], 662 | "source": [ 663 | "a[2] = 3" 664 | ] 665 | }, 666 | { 667 | "cell_type": "code", 668 | "execution_count": 24, 669 | "metadata": { 670 | "collapsed": false 671 | }, 672 | "outputs": [ 673 | { 674 | "data": { 675 | "text/plain": [ 676 | "array([ 1., 2., 3., 4., 5.])" 677 | ] 678 | }, 679 | "execution_count": 24, 680 | "metadata": {}, 681 | "output_type": "execute_result" 682 | } 683 | ], 684 | "source": [ 685 | "a" 686 | ] 687 | }, 688 | { 689 | "cell_type": "code", 690 | "execution_count": 25, 691 | "metadata": { 692 | "collapsed": false 693 | }, 694 | "outputs": [ 695 | { 696 | "data": { 697 | "text/plain": [ 698 | "array([ 1., 2., 17., 4., 5.])" 699 | ] 700 | }, 701 | "execution_count": 25, 702 | "metadata": {}, 703 | "output_type": "execute_result" 704 | } 705 | ], 706 | "source": [ 707 | "c" 708 | ] 709 | }, 710 | { 711 | "cell_type": "markdown", 712 | "metadata": {}, 713 | "source": [ 714 | "OK, it worked! If the difference between `a = b` and `a = b.copy()` is unclear, you should read through this again. This issue will come back to haunt you otherwise." 715 | ] 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "metadata": {}, 720 | "source": [ 721 | "## Learn More" 722 | ] 723 | }, 724 | { 725 | "cell_type": "markdown", 726 | "metadata": {}, 727 | "source": [ 728 | "There are a lot of resources online to learn more about using NumPy and other libraries. Just for kicks, here we use Jupyter's feature for embedding videos to point you to a short video on YouTube on using NumPy arrays." 729 | ] 730 | }, 731 | { 732 | "cell_type": "code", 733 | "execution_count": 26, 734 | "metadata": { 735 | "collapsed": false 736 | }, 737 | "outputs": [ 738 | { 739 | "data": { 740 | "text/html": [ 741 | "\n", 742 | " \n", 749 | " " 750 | ], 751 | "text/plain": [ 752 | "" 753 | ] 754 | }, 755 | "execution_count": 26, 756 | "metadata": {}, 757 | "output_type": "execute_result" 758 | } 759 | ], 760 | "source": [ 761 | "from IPython.display import YouTubeVideo\n", 762 | "# a short video about using NumPy arrays, from Enthought\n", 763 | "YouTubeVideo('vWkb7VahaXQ')" 764 | ] 765 | }, 766 | { 767 | "cell_type": "code", 768 | "execution_count": 27, 769 | "metadata": { 770 | "collapsed": false 771 | }, 772 | "outputs": [ 773 | { 774 | "data": { 775 | "text/html": [ 776 | "\n", 777 | "\n", 778 | "\n", 779 | "\n", 844 | "\n" 859 | ], 860 | "text/plain": [ 861 | "" 862 | ] 863 | }, 864 | "execution_count": 27, 865 | "metadata": {}, 866 | "output_type": "execute_result" 867 | } 868 | ], 869 | "source": [ 870 | "from IPython.core.display import HTML\n", 871 | "def css_styling():\n", 872 | " styles = open(\"../styles/custom.css\", \"r\").read()\n", 873 | " return HTML(styles)\n", 874 | "css_styling()" 875 | ] 876 | } 877 | ], 878 | "metadata": { 879 | "kernelspec": { 880 | "display_name": "Python 3", 881 | "language": "python", 882 | "name": "python3" 883 | }, 884 | "language_info": { 885 | "codemirror_mode": { 886 | "name": "ipython", 887 | "version": 3 888 | }, 889 | "file_extension": ".py", 890 | "mimetype": "text/x-python", 891 | "name": "python", 892 | "nbconvert_exporter": "python", 893 | "pygments_lexer": "ipython3", 894 | "version": "3.4.3" 895 | } 896 | }, 897 | "nbformat": 4, 898 | "nbformat_minor": 0 899 | } 900 | -------------------------------------------------------------------------------- /lessons/01_Step_1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "raw", 5 | "metadata": {}, 6 | "source": [ 7 | "Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved BSD-3 license. (c) Lorena A. Barba, Gilbert F. Forsyth 2017. Thanks to NSF for support via CAREER award #1149784." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "[@LorenaABarba](https://twitter.com/LorenaABarba)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "12 steps to Navier–Stokes\n", 22 | "======\n", 23 | "***" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "Hello! Welcome to the **12 steps to Navier–Stokes**. This is a practical module that is used in the beginning of an interactive Computational Fluid Dynamics (CFD) course taught by [Prof. Lorena Barba](http://lorenabarba.com) since Spring 2009 at Boston University. The course assumes only basic programming knowledge (in any language) and of course some foundation in partial differential equations and fluid mechanics. The practical module was inspired by the ideas of Dr. Rio Yokota, who was a post-doc in Barba's lab, and has been refined by Prof. Barba and her students over several semesters teaching the course. The course is taught entirely using Python and students who don't know Python just learn as we work through the module.\n", 31 | "\n", 32 | "This [Jupyter notebook](https://jupyter-notebook.readthedocs.io/en/stable/) will lead you through the first step of programming your own Navier–Stokes solver in Python from the ground up. We're going to dive right in. Don't worry if you don't understand everything that's happening at first, we'll cover it in detail as we move forward and you can support your learning with the videos of [Prof. Barba's lectures on YouTube](http://www.youtube.com/playlist?list=PL30F4C5ABCE62CB61).\n", 33 | "\n", 34 | "For best results, after you follow this notebook, prepare your own code for Step 1, either as a Python script or in a clean Jupyter notebook.\n", 35 | "\n", 36 | "To execute this Notebook, we assume you have invoked the notebook server using: `jupyter notebook`." 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "Step 1: 1-D Linear Convection\n", 44 | "-----\n", 45 | "***" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "The 1-D Linear Convection equation is the simplest, most basic model that can be used to learn something about CFD. It is surprising that this little equation can teach us so much! Here it is:\n", 53 | "\n", 54 | "$$\\frac{\\partial u}{\\partial t} + c \\frac{\\partial u}{\\partial x} = 0$$\n", 55 | "\n", 56 | "With given initial conditions (understood as a *wave*), the equation represents the propagation of that initial *wave* with speed $c$, without change of shape. Let the initial condition be $u(x,0)=u_0(x)$. Then the exact solution of the equation is $u(x,t)=u_0(x-ct)$.\n", 57 | "\n", 58 | "We discretize this equation in both space and time, using the Forward Difference scheme for the time derivative and the Backward Difference scheme for the space derivative. Consider discretizing the spatial coordinate $x$ into points that we index from $i=0$ to $N$, and stepping in discrete time intervals of size $\\Delta t$.\n", 59 | "\n", 60 | "From the definition of a derivative (and simply removing the limit), we know that:\n", 61 | "\n", 62 | "$$\\frac{\\partial u}{\\partial x}\\approx \\frac{u(x+\\Delta x)-u(x)}{\\Delta x}$$\n", 63 | "\n", 64 | "Our discrete equation, then, is:\n", 65 | "\n", 66 | "$$\\frac{u_i^{n+1}-u_i^n}{\\Delta t} + c \\frac{u_i^n - u_{i-1}^n}{\\Delta x} = 0 $$\n", 67 | "\n", 68 | "Where $n$ and $n+1$ are two consecutive steps in time, while $i-1$ and $i$ are two neighboring points of the discretized $x$ coordinate. If there are given initial conditions, then the only unknown in this discretization is $u_i^{n+1}$. We can solve for our unknown to get an equation that allows us to advance in time, as follows:\n", 69 | "\n", 70 | "$$u_i^{n+1} = u_i^n - c \\frac{\\Delta t}{\\Delta x}(u_i^n-u_{i-1}^n)$$\n", 71 | "\n", 72 | "Now let's try implementing this in Python. \n", 73 | "\n", 74 | "We'll start by importing a few libraries to help us out.\n", 75 | "\n", 76 | "* `numpy` is a library that provides a bunch of useful matrix operations akin to MATLAB\n", 77 | "* `matplotlib` is a 2D plotting library that we will use to plot our results\n", 78 | "* `time` and `sys` provide basic timing functions that we'll use to slow down animations for viewing" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 1, 84 | "metadata": { 85 | "collapsed": false 86 | }, 87 | "outputs": [], 88 | "source": [ 89 | "# Remember: comments in python are denoted by the pound sign\n", 90 | "import numpy #here we load numpy\n", 91 | "from matplotlib import pyplot #here we load matplotlib\n", 92 | "import time, sys #and load some utilities\n" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 2, 98 | "metadata": { 99 | "collapsed": false 100 | }, 101 | "outputs": [], 102 | "source": [ 103 | "#this makes matplotlib plots appear in the notebook (instead of a separate window)\n", 104 | "%matplotlib inline " 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "Now let's define a few variables; we want to define an evenly spaced grid of points within a spatial domain that is 2 units of length wide, i.e., $x_i\\in(0,2)$. We'll define a variable `nx`, which will be the number of grid points we want and `dx` will be the distance between any pair of adjacent grid points. " 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 3, 117 | "metadata": { 118 | "collapsed": false 119 | }, 120 | "outputs": [], 121 | "source": [ 122 | "nx = 41 # try changing this number from 41 to 81 and Run All ... what happens?\n", 123 | "dx = 2 / (nx-1)\n", 124 | "nt = 25 #nt is the number of timesteps we want to calculate\n", 125 | "dt = .025 #dt is the amount of time each timestep covers (delta t)\n", 126 | "c = 1 #assume wavespeed of c = 1" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "We also need to set up our initial conditions. The initial velocity $u_0$ is given as \n", 134 | "$u = 2$ in the interval $0.5 \\leq x \\leq 1$ and $u = 1$ everywhere else in $(0,2)$ (i.e., a hat function).\n", 135 | "\n", 136 | "Here, we use the function `ones()` defining a `numpy` array which is `nx` elements long with every value equal to 1." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 4, 142 | "metadata": { 143 | "collapsed": false 144 | }, 145 | "outputs": [ 146 | { 147 | "name": "stdout", 148 | "output_type": "stream", 149 | "text": [ 150 | "[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 2. 2. 2. 2. 2. 2. 2. 2.\n", 151 | " 2. 2. 2. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n", 152 | " 1. 1. 1. 1. 1.]\n" 153 | ] 154 | } 155 | ], 156 | "source": [ 157 | "u = numpy.ones(nx) #numpy function ones()\n", 158 | "u[int(.5 / dx):int(1 / dx + 1)] = 2 #setting u = 2 between 0.5 and 1 as per our I.C.s\n", 159 | "print(u)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "Now let's take a look at those initial conditions using a Matplotlib plot. We've imported the `matplotlib` plotting library `pyplot` and the plotting function is called `plot`, so we'll call `pyplot.plot`. To learn about the myriad possibilities of Matplotlib, explore the [Gallery](http://matplotlib.org/gallery.html) of example plots.\n", 167 | "\n", 168 | "Here, we use the syntax for a simple 2D plot: `plot(x,y)`, where the `x` values are evenly distributed grid points:" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 5, 174 | "metadata": { 175 | "collapsed": false 176 | }, 177 | "outputs": [ 178 | { 179 | "data": { 180 | "image/png": "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\nl7QqIj49uP+vkh6PiIeHrI0i3w+nO35cWr26/6vd9DR5+8xnpF//WrrrrqYnAfpsKyIW9Te/6HaN\nB1/D7JZ0le3lttdIulzSgcUMg7lNt3gCvno0eeRk5HaN7V2SOpLW2T4oaZukMUkREdsj4gXb35K0\nX9IJSdsj4vkKZ16S2I+vD3vyyMnIkI+IrQXWfF7S50uZCEOxH18fmjxywhmviaDJ14cmj5wQ8omg\nydeHJo+cEPKJoMnXhyaPnBDyiaDJ14cmj5wQ8omgydeHJo+cEPKJoMnXhyaPnBDyiaDJ14cmj5wQ\n8omYnKTJ12X16v5lDU6caHoS4MwR8omYmqLJ18Vmywb5IOQTwXZNvdiyQS4I+UTwg9d60eSRC0I+\nETT5etHkkQtCPhE0+XrR5JELQj4RNPl60eSRC0I+ETT5etHkkQtCPhE0+XrR5JELQj4RNPl60eSR\nC0I+ETT5etHkkQtCPgEnTvRPs1+9uulJlg6aPHJByCdgeqvGbnqSpYMmj1wQ8glgP75+NHnkgpBP\nAPvx9aPJIxeEfAJo8vWjySMXhHwCaPL1o8kjF4R8Amjy9aPJIxeEfAJo8vWjySMXhHwCaPL1o8kj\nF4R8Amjy9aPJIxcjQ972DtuHbe8fse73bB+3/cHyxoPEh3g3YXyckEceijT5nZK2zLfA9jJJ/yDp\nW2UMhbfiQ7zrt2aNdPSodPJk05MAZ2ZkyEfEXkmvjVj2N5IekvSLMobCW9Hk67d8ubRqlXTkSNOT\nAGfmjPfkbZ8r6YaI+GdJXF2lAjT5ZkxM8MNXpG9FCc/xJUm3z7g/b9B3u91TtzudjjqdTgkj5I0m\n34zpffn165ueBEtNr9dTr9cr5bkcEaMX2RskPRIRG4c89sr0TUnnSJqS9FcRsWfI2ijy/fBWN94o\n3Xyz9EF+pF2rSy+Vvv51aeNpRz1QL9uKiEXtlBRt8tYcDT0ifnPGIDvV/5/BaQGPxaPJN4N32CAH\nI0Pe9i5JHUnrbB+UtE3SmKSIiO2zllPTK8CefDPYk0cORoZ8RGwt+mQR8RdnNg6G4WSoZnBCFHLA\nGa8J4LIGzeDSBsgBIZ8AmnwzaPLIASGfAJp8M2jyyAEh33InT/bPulyzpulJlh6aPHJAyLfc0aP9\n0+uXL296kqWHJo8cEPItx358c2jyyAEh33LsxzeHJo8cEPItR5NvDk0eOSDkW44m3xyaPHJAyLcc\n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\n", 181 | "text/plain": [ 182 | "" 183 | ] 184 | }, 185 | "metadata": {}, 186 | "output_type": "display_data" 187 | } 188 | ], 189 | "source": [ 190 | "pyplot.plot(numpy.linspace(0, 2, nx), u);" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "Why doesn't the hat function have perfectly straight sides? Think for a bit." 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "Now it's time to implement the discretization of the convection equation using a finite-difference scheme. \n", 205 | "\n", 206 | "For every element of our array `u`, we need to perform the operation $u_i^{n+1} = u_i^n - c \\frac{\\Delta t}{\\Delta x}(u_i^n-u_{i-1}^n)$\n", 207 | "\n", 208 | "We'll store the result in a new (temporary) array `un`, which will be the solution $u$ for the next time-step. We will repeat this operation for as many time-steps as we specify and then we can see how far the wave has convected. \n", 209 | "\n", 210 | "We first initialize our placeholder array `un` to hold the values we calculate for the $n+1$ timestep, using once again the NumPy function `ones()`.\n", 211 | "\n", 212 | "Then, we may think we have two iterative operations: one in space and one in time (we'll learn differently later), so we'll start by nesting one loop inside the other. Note the use of the nifty `range()` function. When we write: `for i in range(1,nx)` we will iterate through the `u` array, but we'll be skipping the first element (the zero-th element). *Why?*" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 6, 218 | "metadata": { 219 | "collapsed": false 220 | }, 221 | "outputs": [], 222 | "source": [ 223 | "un = numpy.ones(nx) #initialize a temporary array\n", 224 | "\n", 225 | "for n in range(nt): #loop for values of n from 0 to nt, so it will run nt times\n", 226 | " un = u.copy() ##copy the existing values of u into un\n", 227 | " for i in range(1, nx): ## you can try commenting this line and...\n", 228 | " #for i in range(nx): ## ... uncommenting this line and see what happens!\n", 229 | " u[i] = un[i] - c * dt / dx * (un[i] - un[i-1])\n", 230 | " \n", 231 | " " 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": {}, 237 | "source": [ 238 | "**Note**—We will learn later that the code as written above is quite inefficient, and there are better ways to write this, Python-style. But let's carry on.\n", 239 | "\n", 240 | "Now let's try plotting our `u` array after advancing in time." 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 7, 246 | "metadata": { 247 | "collapsed": false 248 | }, 249 | "outputs": [ 250 | { 251 | "data": { 252 | "image/png": "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\n", 253 | "text/plain": [ 254 | "" 255 | ] 256 | }, 257 | "metadata": {}, 258 | "output_type": "display_data" 259 | } 260 | ], 261 | "source": [ 262 | "pyplot.plot(numpy.linspace(0, 2, nx), u);" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": {}, 268 | "source": [ 269 | "OK! So our hat function has definitely moved to the right, but it's no longer a hat. **What's going on?**" 270 | ] 271 | }, 272 | { 273 | "cell_type": "markdown", 274 | "metadata": {}, 275 | "source": [ 276 | "Learn More\n", 277 | "-----\n", 278 | "***" 279 | ] 280 | }, 281 | { 282 | "cell_type": "markdown", 283 | "metadata": {}, 284 | "source": [ 285 | "For a more thorough explanation of the finite-difference method, including topics like the truncation error, order of convergence and other details, watch **Video Lessons 2 and 3** by Prof. Barba on YouTube." 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 8, 291 | "metadata": { 292 | "collapsed": false 293 | }, 294 | "outputs": [ 295 | { 296 | "data": { 297 | "image/jpeg": "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\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAENpNLfN9CHKMVttJHNlXaST4taTX3lSxEozjx7Ulrmt6A0EOSWtvryRRLE3Pbtlrny+O/1LFQl\nGKTfoy4gLDPHOolbXXFy3ZtRfC9NrfLf2M0JaM1OGqrIyc3JQcmlro5Pe/yA1AAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCQABBIAAAD//2Q==\n", 298 | "text/html": [ 299 | "\n", 300 | " \n", 307 | " " 308 | ], 309 | "text/plain": [ 310 | "" 311 | ] 312 | }, 313 | "execution_count": 8, 314 | "metadata": {}, 315 | "output_type": "execute_result" 316 | } 317 | ], 318 | "source": [ 319 | "from IPython.display import YouTubeVideo\n", 320 | "YouTubeVideo('iz22_37mMkk')" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": 9, 326 | "metadata": { 327 | "collapsed": false 328 | }, 329 | "outputs": [ 330 | { 331 | "data": { 332 | "image/jpeg": "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\nxZhTNr38ETt1CXqaVyfvmkaMJeBzZZCqO6ySjFeLYLZ6Z43ayXtaWMf+pk6nHVyj6s64P4Nmjqsg\nEuXWJVa/POor+Vf8y1VXS/eXS/5cIvjKM1mLTWcEkxq87WSWhUuuo1PymQuz0v8A3Gp/+w2kFYZV\noor/ANW9/wDUZ0tJBf8AqXf72aQBn9Ep3bmm375Mieg0s3mVEH8jQcW2xq2Z+3JRQWW8e4o/s3Sf\n4EPoP7N0n/x4fQvouhfUrIey84+uDqE42LMJKSzjKJka/wBef6zf2bpc/uYfQsjpKYLEa0vkXgZC\n/Jzvmq+4r+6jiWlpl1gn8i5yS6tL4kQnGazCSkvNDE+1/VHoOm/wo/Qh9n6WXWmP0OdTro6aN0pw\nco1bc46vJpnbCuCnOaUW0k2/MuLPk5fryu19BVDs+x6elKzosLnnj9T1ao7aoR8kkdNJ9QTF5fJy\n5cZKkAq1FqopnZL7K6eb8iua0GbS3ynp27V+0g3GaXmv5YLNPfDUQc687VJx580BY2optvCXVme/\nUul1NQUq5yUXLd0z0x5lt8I2UThP2ZRafwPPpljTXJQWIQ76ncs7U08fR5Klr1AYtHqXdq9RDdlR\nUGvpyWOTj2lGOeJ1Pj4P+Yw1pAIIqSG0k23hIKSfRoy9pOfou2Fc7FKSUlBZe3xCVorthbBTrkpR\nfijJ2pa1o591Ke9SS/ZrLTKYWWft416W6Nc03tksYl7vcaezYzr03d2QlGyLxKT+2/MuJu9LdNdG\n+pSi3xw1JYafwMmolLU3z0l8P/DWpwXg20st/wBeR6GEm2l16nlzSn2lXKcn30LXtjnhV4fOP1EK\n16bUxtlGuMWv2an8nwvyNEnti35LJg0tHouvaV3eRshhLj1EnlL8X9DfNZg15oVYr0tkrdNVZPG6\nUU3guM3Z3/l+n/01+RpJSIJKb7u52Lhuc1FJstTz0ChRXq67NXZp453VrLfgXnlae2vT6i+ye6U7\nLWntXEFuws/MRK9YAr76vvu53LvMbtvuCu5SUYuUnhJZbEZKUVJdGsoz9ownZoL4VvEnB4OaI3uu\nE1fGxNJ8wxwEawQSFCH0JIfRgYH2rCOleolFKKzxuWfoadPc7HOE47bIPDS6PyaM9XZ8H2dKlwVd\nlle2ckucmqihUp8uUpPMpPqzVz0zNRffKqyuKpnOMnhyXSPJccWKxuPdySxL1srOUdGWkggkAZNe\n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\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgkAQS\nAAAAAAAAAAAAAAAAAAAAAAAAAAIJAAAAAAAAAAAACCQAAAAAAAAAAAAgABlZxnnqQ5RXVpHFtPeN\nvftzBx4OI6RLPrZWU+VnHOQLyHOMcZfV4RR6JnObZPjCLO5woJP1YvOMdQLCuV9cb40ttTksrjh/\nMsXBRbpXZq6b3ZhVZajjrlNfqBeSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAQCQAIJAEEgAAAB//Z\n", 333 | "text/html": [ 334 | "\n", 335 | " \n", 342 | " " 343 | ], 344 | "text/plain": [ 345 | "" 346 | ] 347 | }, 348 | "execution_count": 9, 349 | "metadata": {}, 350 | "output_type": "execute_result" 351 | } 352 | ], 353 | "source": [ 354 | "YouTubeVideo('xq9YTcv-fQg')" 355 | ] 356 | }, 357 | { 358 | "cell_type": "markdown", 359 | "metadata": {}, 360 | "source": [ 361 | "For a careful walk-through of the discretization of the linear convection equation with finite differences (and also the following steps, up to Step 4), watch **Video Lesson 4** by Prof. Barba on YouTube." 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": 10, 367 | "metadata": { 368 | "collapsed": false 369 | }, 370 | "outputs": [ 371 | { 372 | "data": { 373 | "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkz\nODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2MBERISGBUYLxoaL2NCOEJjY2NjY2NjY2Nj\nY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY//AABEIAWgB4AMBIgACEQED\nEQH/xAAaAAEAAwEBAQAAAAAAAAAAAAAAAQIDBAUG/8QAOBAAAgIBAwEFBgUDBAIDAAAAAAECAxEE\nEiExBRNBUXEUIjJhcoEjNJGhsRXB0UJS8PEz4SRik//EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/E\nABcRAQEBAQAAAAAAAAAAAAAAAAABESH/2gAMAwEAAhEDEQA/APvwAAAK7knhtfqBYEEgAQM84AkE\nbl5r9SQAIbS8SHKKaTay+i8wLArGSlna08cPBIEggkAAAAAAAAAAABBJD4WQBJww16d1UZ7UrFJ8\nPmLXOP5OyM4SxtknlZWH1QFgAAIJMtRb3NTnjPK8fmBoDzb+0YR77daod20k0/jyuPTxOzT25pqV\nlkZWSgm2vHjloDcERalFSTynymiQAAAAEAAc1/4mrpqeduHN4fljH8l9PqIajvNieITcG34tDBuQ\nMpdWY13Z1Ftcmko7WufB/wDQG4II3LLWVleAFgZV31WqTrnGSi8PD6FXqqtrkpZSSlleOegGwOO2\n5WQpvjujstSlGXDWeOf1NoylTXZPUTWNzaflHwCNpPCb6/IrVZG2uM4PMZLKEZwnFSjJNNZTMKPw\ntVbSvhaVkflnOV+q/cDqAAUAAAAAAAAAAFLJONcpJZaTaR4uvTrhTOF7bltdiTWGm+rzzjw4PdOO\nfZtE5qTgnj4cpNx9PIC+leO9rzmNcsR+Sx0KPtLTJ4bn/wDnI6Kq41QUI9F4vq/my4GNGqq1Emq3\nLK65i0YXVQXa2nsS9+UJpvPodpSVMZXQted0E0vv/wBAcOl0dF2ihOccTWWpp4cXl8m1Wra7Khqr\nYvd3ak0vFk+wR7pVO23u087U0s/J/I3sqhbTKqS9ySw0vIDzpvVe1J6rupR7ixxjDP8A9eorlfLt\nKKr7pQWmTimnlHWtFF2b52WTfdutZfRP+/BpDTwharFncoKH2QHB2RddHTaSq7Y3Otzbj8sf5Lan\n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\nAAAB/9k=\n", 374 | "text/html": [ 375 | "\n", 376 | " \n", 383 | " " 384 | ], 385 | "text/plain": [ 386 | "" 387 | ] 388 | }, 389 | "execution_count": 10, 390 | "metadata": {}, 391 | "output_type": "execute_result" 392 | } 393 | ], 394 | "source": [ 395 | "YouTubeVideo('y2WaK7_iMRI')" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "## Last but not least" 403 | ] 404 | }, 405 | { 406 | "cell_type": "markdown", 407 | "metadata": {}, 408 | "source": [ 409 | "**Remember** to rewrite Step 1 as a fresh Python script or in *your own* Jupyter notebook and then experiment by changing the discretization parameters. Once you have done this, you will be ready for [Step 2](./02_Step_2.ipynb).\n", 410 | "\n", 411 | "\n", 412 | "***" 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 11, 418 | "metadata": { 419 | "collapsed": false 420 | }, 421 | "outputs": [ 422 | { 423 | "data": { 424 | "text/html": [ 425 | "\n", 426 | "\n", 427 | "\n", 428 | "\n", 493 | "\n" 508 | ], 509 | "text/plain": [ 510 | "" 511 | ] 512 | }, 513 | "execution_count": 11, 514 | "metadata": {}, 515 | "output_type": "execute_result" 516 | } 517 | ], 518 | "source": [ 519 | "from IPython.core.display import HTML\n", 520 | "def css_styling():\n", 521 | " styles = open(\"../styles/custom.css\", \"r\").read()\n", 522 | " return HTML(styles)\n", 523 | "css_styling()" 524 | ] 525 | }, 526 | { 527 | "cell_type": "markdown", 528 | "metadata": {}, 529 | "source": [ 530 | "> (The cell above executes the style for this notebook. We modified a style we found on the GitHub of [CamDavidsonPilon](https://github.com/CamDavidsonPilon), [@Cmrn_DP](https://twitter.com/cmrn_dp).)" 531 | ] 532 | } 533 | ], 534 | "metadata": { 535 | "kernelspec": { 536 | "display_name": "Python 3", 537 | "language": "python", 538 | "name": "python3" 539 | }, 540 | "language_info": { 541 | "codemirror_mode": { 542 | "name": "ipython", 543 | "version": 3 544 | }, 545 | "file_extension": ".py", 546 | "mimetype": "text/x-python", 547 | "name": "python", 548 | "nbconvert_exporter": "python", 549 | "pygments_lexer": "ipython3", 550 | "version": "3.5.2" 551 | } 552 | }, 553 | "nbformat": 4, 554 | "nbformat_minor": 0 555 | } 556 | -------------------------------------------------------------------------------- /lessons/02_Step_2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "raw", 5 | "metadata": {}, 6 | "source": [ 7 | "Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved BSD-3 license. (c) Lorena A. Barba, Gilbert F. Forsyth 2017. Thanks to NSF for support via CAREER award #1149784." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "[@LorenaABarba](https://twitter.com/LorenaABarba)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "12 steps to Navier–Stokes\n", 22 | "======\n", 23 | "***" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "This Jupyter notebook continues the presentation of the **12 steps to Navier–Stokes**, the practical module taught in the interactive CFD class of [Prof. Lorena Barba](http://lorenabarba.com). You should have completed [Step 1](./01_Step_1.ipynb) before continuing, having written your own Python script or notebook and having experimented with varying the parameters of the discretization and observing what happens.\n" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Step 2: Nonlinear Convection\n", 38 | "-----\n", 39 | "***" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "Now we're going to implement nonlinear convection using the same methods as in step 1. The 1D convection equation is:\n", 47 | "\n", 48 | "$$\\frac{\\partial u}{\\partial t} + u \\frac{\\partial u}{\\partial x} = 0$$\n", 49 | "\n", 50 | "Instead of a constant factor $c$ multiplying the second term, now we have the solution $u$ multiplying it. Thus, the second term of the equation is now *nonlinear*. We're going to use the same discretization as in Step 1 — forward difference in time and backward difference in space. Here is the discretized equation.\n", 51 | "\n", 52 | "$$\\frac{u_i^{n+1}-u_i^n}{\\Delta t} + u_i^n \\frac{u_i^n-u_{i-1}^n}{\\Delta x} = 0$$\n", 53 | "\n", 54 | "Solving for the only unknown term, $u_i^{n+1}$, yields:\n", 55 | "\n", 56 | "$$u_i^{n+1} = u_i^n - u_i^n \\frac{\\Delta t}{\\Delta x} (u_i^n - u_{i-1}^n)$$" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "As before, the Python code starts by loading the necessary libraries. Then, we declare some variables that determine the discretization in space and time (you should experiment by changing these parameters to see what happens). Then, we create the initial condition $u_0$ by initializing the array for the solution using $u = 2\\ @\\ 0.5 \\leq x \\leq 1$ and $u = 1$ everywhere else in $(0,2)$ (i.e., a hat function)." 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 1, 69 | "metadata": { 70 | "collapsed": true 71 | }, 72 | "outputs": [], 73 | "source": [ 74 | "import numpy # we're importing numpy \n", 75 | "from matplotlib import pyplot # and our 2D plotting library\n", 76 | "%matplotlib inline\n", 77 | "\n", 78 | "\n", 79 | "nx = 41\n", 80 | "dx = 2 / (nx - 1)\n", 81 | "nt = 20 #nt is the number of timesteps we want to calculate\n", 82 | "dt = .025 #dt is the amount of time each timestep covers (delta t)\n", 83 | "\n", 84 | "u = numpy.ones(nx) #as before, we initialize u with every value equal to 1.\n", 85 | "u[int(.5 / dx) : int(1 / dx + 1)] = 2 #then set u = 2 between 0.5 and 1 as per our I.C.s\n", 86 | "\n", 87 | "un = numpy.ones(nx) #initialize our placeholder array un, to hold the time-stepped solution" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "The code snippet below is *unfinished*. We have copied over the line from [Step 1](./01_Step_1.ipynb) that executes the time-stepping update. Can you edit this code to execute the nonlinear convection instead?" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": { 101 | "collapsed": false 102 | }, 103 | "outputs": [], 104 | "source": [ 105 | "for n in range(nt): #iterate through time\n", 106 | " un = u.copy() ##copy the existing values of u into un\n", 107 | " for i in range(1, nx): ##now we'll iterate through the u array\n", 108 | " \n", 109 | " ###This is the line from Step 1, copied exactly. Edit it for our new equation.\n", 110 | " ###then uncomment it and run the cell to evaluate Step 2 \n", 111 | " \n", 112 | " ###u[i] = un[i] - c * dt / dx * (un[i] - un[i-1]) \n", 113 | "\n", 114 | " \n", 115 | "pyplot.plot(numpy.linspace(0, 2, nx), u) ##Plot the results" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": {}, 121 | "source": [ 122 | "What do you observe about the evolution of the hat function under the nonlinear convection equation? What happens when you change the numerical parameters and run again?" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "## Learn More" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "For a careful walk-through of the discretization of the convection equation with finite differences (and all steps from 1 to 4), watch **Video Lesson 4** by Prof. Barba on YouTube." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 2, 142 | "metadata": { 143 | "collapsed": false 144 | }, 145 | "outputs": [ 146 | { 147 | "data": { 148 | "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkz\nODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2MBERISGBUYLxoaL2NCOEJjY2NjY2NjY2Nj\nY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY//AABEIAWgB4AMBIgACEQED\nEQH/xAAaAAEAAwEBAQAAAAAAAAAAAAAAAQIDBAUG/8QAOBAAAgIBAwEFBgUDBAIDAAAAAAECAxEE\nEiExBRNBUXEUIjJhcoEjNJGhsRXB0UJS8PEz4SRik//EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/E\nABcRAQEBAQAAAAAAAAAAAAAAAAABESH/2gAMAwEAAhEDEQA/APvwAAAK7knhtfqBYEEgAQM84AkE\nbl5r9SQAIbS8SHKKaTay+i8wLArGSlna08cPBIEggkAAAAAAAAAAABBJD4WQBJww16d1UZ7UrFJ8\nPmLXOP5OyM4SxtknlZWH1QFgAAIJMtRb3NTnjPK8fmBoDzb+0YR77daod20k0/jyuPTxOzT25pqV\nlkZWSgm2vHjloDcERalFSTynymiQAAAAEAAc1/4mrpqeduHN4fljH8l9PqIajvNieITcG34tDBuQ\nMpdWY13Z1Ftcmko7WufB/wDQG4II3LLWVleAFgZV31WqTrnGSi8PD6FXqqtrkpZSSlleOegGwOO2\n5WQpvjujstSlGXDWeOf1NoylTXZPUTWNzaflHwCNpPCb6/IrVZG2uM4PMZLKEZwnFSjJNNZTMKPw\ntVbSvhaVkflnOV+q/cDqAAUAAAAAAAAAAFLJONcpJZaTaR4uvTrhTOF7bltdiTWGm+rzzjw4PdOO\nfZtE5qTgnj4cpNx9PIC+leO9rzmNcsR+Sx0KPtLTJ4bn/wDnI6Kq41QUI9F4vq/my4GNGqq1Emq3\nLK65i0YXVQXa2nsS9+UJpvPodpSVMZXQted0E0vv/wBAcOl0dF2ihOccTWWpp4cXl8m1Wra7Khqr\nYvd3ak0vFk+wR7pVO23u087U0s/J/I3sqhbTKqS9ySw0vIDzpvVe1J6rupR7ixxjDP8A9eorlfLt\nKKr7pQWmTimnlHWtFF2b52WTfdutZfRP+/BpDTwharFncoKH2QHB2RddHTaSq7Y3Otzbj8sf5Lan\n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\nAAAB/9k=\n", 149 | "text/html": [ 150 | "\n", 151 | " \n", 158 | " " 159 | ], 160 | "text/plain": [ 161 | "" 162 | ] 163 | }, 164 | "execution_count": 2, 165 | "metadata": {}, 166 | "output_type": "execute_result" 167 | } 168 | ], 169 | "source": [ 170 | "from IPython.display import YouTubeVideo\n", 171 | "YouTubeVideo('y2WaK7_iMRI')" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 3, 177 | "metadata": { 178 | "collapsed": false 179 | }, 180 | "outputs": [ 181 | { 182 | "data": { 183 | "text/html": [ 184 | "\n", 185 | "\n", 186 | "\n", 187 | "\n", 252 | "\n" 267 | ], 268 | "text/plain": [ 269 | "" 270 | ] 271 | }, 272 | "execution_count": 3, 273 | "metadata": {}, 274 | "output_type": "execute_result" 275 | } 276 | ], 277 | "source": [ 278 | "from IPython.core.display import HTML\n", 279 | "def css_styling():\n", 280 | " styles = open(\"../styles/custom.css\", \"r\").read()\n", 281 | " return HTML(styles)\n", 282 | "css_styling()" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "> (The cell above executes the style for this notebook.)" 290 | ] 291 | } 292 | ], 293 | "metadata": { 294 | "kernelspec": { 295 | "display_name": "Python 3", 296 | "language": "python", 297 | "name": "python3" 298 | }, 299 | "language_info": { 300 | "codemirror_mode": { 301 | "name": "ipython", 302 | "version": 3 303 | }, 304 | "file_extension": ".py", 305 | "mimetype": "text/x-python", 306 | "name": "python", 307 | "nbconvert_exporter": "python", 308 | "pygments_lexer": "ipython3", 309 | "version": "3.5.2" 310 | } 311 | }, 312 | "nbformat": 4, 313 | "nbformat_minor": 0 314 | } 315 | -------------------------------------------------------------------------------- /lessons/04_Step_3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "raw", 5 | "metadata": {}, 6 | "source": [ 7 | "Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved BSD-3 license. (c) Lorena A. Barba, Gilbert F. Forsyth 2017. Thanks to NSF for support via CAREER award #1149784." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "[@LorenaABarba](https://twitter.com/LorenaABarba)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "12 steps to Navier–Stokes\n", 22 | "======\n", 23 | "***" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "You should have completed Steps [1](./01_Step_1.ipynb) and [2](./02_Step_2.ipynb) before continuing. This Jupyter notebook continues the presentation of the **12 steps to Navier–Stokes**, the practical module taught in the interactive CFD class of [Prof. Lorena Barba](http://lorenabarba.com). " 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Step 3: Diffusion Equation in 1-D\n", 38 | "-----\n", 39 | "***" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "The one-dimensional diffusion equation is:\n", 47 | "\n", 48 | "$$\\frac{\\partial u}{\\partial t}= \\nu \\frac{\\partial^2 u}{\\partial x^2}$$\n", 49 | "\n", 50 | "The first thing you should notice is that —unlike the previous two simple equations we have studied— this equation has a second-order derivative. We first need to learn what to do with it!" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "### Discretizing $\\frac{\\partial ^2 u}{\\partial x^2}$" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "The second-order derivative can be represented geometrically as the line tangent to the curve given by the first derivative. We will discretize the second-order derivative with a Central Difference scheme: a combination of Forward Difference and Backward Difference of the first derivative. Consider the Taylor expansion of $u_{i+1}$ and $u_{i-1}$ around $u_i$:\n", 65 | "\n", 66 | "$u_{i+1} = u_i + \\Delta x \\frac{\\partial u}{\\partial x}\\bigg|_i + \\frac{\\Delta x^2}{2} \\frac{\\partial ^2 u}{\\partial x^2}\\bigg|_i + \\frac{\\Delta x^3}{3!} \\frac{\\partial ^3 u}{\\partial x^3}\\bigg|_i + O(\\Delta x^4)$\n", 67 | "\n", 68 | "$u_{i-1} = u_i - \\Delta x \\frac{\\partial u}{\\partial x}\\bigg|_i + \\frac{\\Delta x^2}{2} \\frac{\\partial ^2 u}{\\partial x^2}\\bigg|_i - \\frac{\\Delta x^3}{3!} \\frac{\\partial ^3 u}{\\partial x^3}\\bigg|_i + O(\\Delta x^4)$\n", 69 | "\n", 70 | "If we add these two expansions, you can see that the odd-numbered derivative terms will cancel each other out. If we neglect any terms of $O(\\Delta x^4)$ or higher (and really, those are very small), then we can rearrange the sum of these two expansions to solve for our second-derivative. \n" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "$u_{i+1} + u_{i-1} = 2u_i+\\Delta x^2 \\frac{\\partial ^2 u}{\\partial x^2}\\bigg|_i + O(\\Delta x^4)$\n", 78 | "\n", 79 | "Then rearrange to solve for $\\frac{\\partial ^2 u}{\\partial x^2}\\bigg|_i$ and the result is:\n", 80 | "\n", 81 | "$$\\frac{\\partial ^2 u}{\\partial x^2}=\\frac{u_{i+1}-2u_{i}+u_{i-1}}{\\Delta x^2} + O(\\Delta x^2)$$\n" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "### Back to Step 3" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "We can now write the discretized version of the diffusion equation in 1D:\n", 96 | "\n", 97 | "$$\\frac{u_{i}^{n+1}-u_{i}^{n}}{\\Delta t}=\\nu\\frac{u_{i+1}^{n}-2u_{i}^{n}+u_{i-1}^{n}}{\\Delta x^2}$$\n", 98 | "\n", 99 | "As before, we notice that once we have an initial condition, the only unknown is $u_{i}^{n+1}$, so we re-arrange the equation solving for our unknown:\n", 100 | "\n", 101 | "$$u_{i}^{n+1}=u_{i}^{n}+\\frac{\\nu\\Delta t}{\\Delta x^2}(u_{i+1}^{n}-2u_{i}^{n}+u_{i-1}^{n})$$\n", 102 | "\n", 103 | "The above discrete equation allows us to write a program to advance a solution in time. But we need an initial condition. Let's continue using our favorite: the hat function. So, at $t=0$, $u=2$ in the interval $0.5\\le x\\le 1$ and $u=1$ everywhere else. We are ready to number-crunch!" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 1, 109 | "metadata": { 110 | "collapsed": false 111 | }, 112 | "outputs": [ 113 | { 114 | "data": { 115 | "image/png": "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\nhmgVBC8IIcwAAAAASUVORK5CYII=\n", 116 | "text/plain": [ 117 | "" 118 | ] 119 | }, 120 | "metadata": {}, 121 | "output_type": "display_data" 122 | } 123 | ], 124 | "source": [ 125 | "import numpy #loading our favorite library\n", 126 | "from matplotlib import pyplot #and the useful plotting library\n", 127 | "%matplotlib inline\n", 128 | "\n", 129 | "nx = 41\n", 130 | "dx = 2 / (nx - 1)\n", 131 | "nt = 20 #the number of timesteps we want to calculate\n", 132 | "nu = 0.3 #the value of viscosity\n", 133 | "sigma = .2 #sigma is a parameter, we'll learn more about it later\n", 134 | "dt = sigma * dx**2 / nu #dt is defined using sigma ... more later!\n", 135 | "\n", 136 | "\n", 137 | "u = numpy.ones(nx) #a numpy array with nx elements all equal to 1.\n", 138 | "u[int(.5 / dx):int(1 / dx + 1)] = 2 #setting u = 2 between 0.5 and 1 as per our I.C.s\n", 139 | "\n", 140 | "un = numpy.ones(nx) #our placeholder array, un, to advance the solution in time\n", 141 | "\n", 142 | "for n in range(nt): #iterate through time\n", 143 | " un = u.copy() ##copy the existing values of u into un\n", 144 | " for i in range(1, nx - 1):\n", 145 | " u[i] = un[i] + nu * dt / dx**2 * (un[i+1] - 2 * un[i] + un[i-1])\n", 146 | " \n", 147 | "pyplot.plot(numpy.linspace(0, 2, nx), u);" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "## Learn More" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "For a careful walk-through of the discretization of the diffusion equation with finite differences (and all steps from 1 to 4), watch **Video Lesson 4** by Prof. Barba on YouTube." 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 2, 167 | "metadata": { 168 | "collapsed": false 169 | }, 170 | "outputs": [ 171 | { 172 | "data": { 173 | "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkz\nODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2MBERISGBUYLxoaL2NCOEJjY2NjY2NjY2Nj\nY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY//AABEIAWgB4AMBIgACEQED\nEQH/xAAaAAEAAwEBAQAAAAAAAAAAAAAAAQIDBAUG/8QAOBAAAgIBAwEFBgUDBAIDAAAAAAECAxEE\nEiExBRNBUXEUIjJhcoEjNJGhsRXB0UJS8PEz4SRik//EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/E\nABcRAQEBAQAAAAAAAAAAAAAAAAABESH/2gAMAwEAAhEDEQA/APvwAAAK7knhtfqBYEEgAQM84AkE\nbl5r9SQAIbS8SHKKaTay+i8wLArGSlna08cPBIEggkAAAAAAAAAAABBJD4WQBJww16d1UZ7UrFJ8\nPmLXOP5OyM4SxtknlZWH1QFgAAIJMtRb3NTnjPK8fmBoDzb+0YR77daod20k0/jyuPTxOzT25pqV\nlkZWSgm2vHjloDcERalFSTynymiQAAAAEAAc1/4mrpqeduHN4fljH8l9PqIajvNieITcG34tDBuQ\nMpdWY13Z1Ftcmko7WufB/wDQG4II3LLWVleAFgZV31WqTrnGSi8PD6FXqqtrkpZSSlleOegGwOO2\n5WQpvjujstSlGXDWeOf1NoylTXZPUTWNzaflHwCNpPCb6/IrVZG2uM4PMZLKEZwnFSjJNNZTMKPw\ntVbSvhaVkflnOV+q/cDqAAUAAAAAAAAAAFLJONcpJZaTaR4uvTrhTOF7bltdiTWGm+rzzjw4PdOO\nfZtE5qTgnj4cpNx9PIC+leO9rzmNcsR+Sx0KPtLTJ4bn/wDnI6Kq41QUI9F4vq/my4GNGqq1Emq3\nLK65i0YXVQXa2nsS9+UJpvPodpSVMZXQted0E0vv/wBAcOl0dF2ihOccTWWpp4cXl8m1Wra7Khqr\nYvd3ak0vFk+wR7pVO23u087U0s/J/I3sqhbTKqS9ySw0vIDzpvVe1J6rupR7ixxjDP8A9eorlfLt\nKKr7pQWmTimnlHWtFF2b52WTfdutZfRP+/BpDTwharFncoKH2QHB2RddHTaSq7Y3Otzbj8sf5Lan\n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\nAAAB/9k=\n", 174 | "text/html": [ 175 | "\n", 176 | " \n", 183 | " " 184 | ], 185 | "text/plain": [ 186 | "" 187 | ] 188 | }, 189 | "execution_count": 2, 190 | "metadata": {}, 191 | "output_type": "execute_result" 192 | } 193 | ], 194 | "source": [ 195 | "from IPython.display import YouTubeVideo\n", 196 | "YouTubeVideo('y2WaK7_iMRI')" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 3, 202 | "metadata": { 203 | "collapsed": false 204 | }, 205 | "outputs": [ 206 | { 207 | "data": { 208 | "text/html": [ 209 | "\n", 210 | "\n", 211 | "\n", 212 | "\n", 277 | "\n" 292 | ], 293 | "text/plain": [ 294 | "" 295 | ] 296 | }, 297 | "execution_count": 3, 298 | "metadata": {}, 299 | "output_type": "execute_result" 300 | } 301 | ], 302 | "source": [ 303 | "from IPython.core.display import HTML\n", 304 | "def css_styling():\n", 305 | " styles = open(\"../styles/custom.css\", \"r\").read()\n", 306 | " return HTML(styles)\n", 307 | "css_styling()" 308 | ] 309 | }, 310 | { 311 | "cell_type": "markdown", 312 | "metadata": {}, 313 | "source": [ 314 | "> (The cell above executes the style for this notebook.)" 315 | ] 316 | } 317 | ], 318 | "metadata": { 319 | "kernelspec": { 320 | "display_name": "Python 3", 321 | "language": "python", 322 | "name": "python3" 323 | }, 324 | "language_info": { 325 | "codemirror_mode": { 326 | "name": "ipython", 327 | "version": 3 328 | }, 329 | "file_extension": ".py", 330 | "mimetype": "text/x-python", 331 | "name": "python", 332 | "nbconvert_exporter": "python", 333 | "pygments_lexer": "ipython3", 334 | "version": "3.5.2" 335 | } 336 | }, 337 | "nbformat": 4, 338 | "nbformat_minor": 0 339 | } 340 | -------------------------------------------------------------------------------- /lessons/06_Array_Operations_with_NumPy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "raw", 5 | "metadata": {}, 6 | "source": [ 7 | "Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved BSD-3 license. (c) Lorena A. Barba, Gilbert F. Forsyth 2017. Thanks to NSF for support via CAREER award #1149784." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "[@LorenaABarba](https://twitter.com/LorenaABarba)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "12 steps to Navier–Stokes\n", 22 | "=====\n", 23 | "***" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "This lesson complements the first interactive module of the online [CFD Python](https://github.com/barbagroup/CFDPython) class, by Prof. Lorena A. Barba, called **12 Steps to Navier–Stokes.** It was written with BU graduate student Gilbert Forsyth." 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Array Operations with NumPy\n", 38 | "----------------\n", 39 | "\n", 40 | "For more computationally intensive programs, the use of built-in Numpy functions can provide an increase in execution speed many-times over. As a simple example, consider the following equation:\n", 41 | "\n", 42 | "$$u^{n+1}_i = u^n_i-u^n_{i-1}$$\n", 43 | "\n", 44 | "Now, given a vector $u^n = [0, 1, 2, 3, 4, 5]\\ \\ $ we can calculate the values of $u^{n+1}$ by iterating over the values of $u^n$ with a for loop. " 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 1, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "import numpy" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 2, 59 | "metadata": {}, 60 | "outputs": [ 61 | { 62 | "name": "stdout", 63 | "output_type": "stream", 64 | "text": [ 65 | "1\n", 66 | "1\n", 67 | "1\n", 68 | "1\n", 69 | "1\n" 70 | ] 71 | } 72 | ], 73 | "source": [ 74 | "u = numpy.array((0, 1, 2, 3, 4, 5))\n", 75 | "\n", 76 | "for i in range(1, len(u)):\n", 77 | " print(u[i] - u[i-1])" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "This is the expected result and the execution time was nearly instantaneous. If we perform the same operation as an array operation, then rather than calculate $u^n_i-u^n_{i-1}\\ $ 5 separate times, we can slice the $u$ array and calculate each operation with one command:" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 3, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "data": { 94 | "text/plain": [ 95 | "array([1, 1, 1, 1, 1])" 96 | ] 97 | }, 98 | "execution_count": 3, 99 | "metadata": {}, 100 | "output_type": "execute_result" 101 | } 102 | ], 103 | "source": [ 104 | "u[1:] - u[0:-1]" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "What this command says is subtract the 0th, 1st, 2nd, 3rd, 4th and 5th elements of $u$ from the 1st, 2nd, 3rd, 4th, 5th and 6th elements of $u$. \n", 112 | "\n", 113 | "### Speed Increases\n", 114 | "\n", 115 | "For a 6 element array, the benefits of array operations are pretty slim. There will be no appreciable difference in execution time because there are so few operations taking place. But if we revisit 2D linear convection, we can see some substantial speed increases. \n" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 4, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "nx = 81\n", 125 | "ny = 81\n", 126 | "nt = 100\n", 127 | "c = 1\n", 128 | "dx = 2 / (nx - 1)\n", 129 | "dy = 2 / (ny - 1)\n", 130 | "sigma = .2\n", 131 | "dt = sigma * dx\n", 132 | "\n", 133 | "x = numpy.linspace(0, 2, nx)\n", 134 | "y = numpy.linspace(0, 2, ny)\n", 135 | "\n", 136 | "u = numpy.ones((ny, nx)) ##create a 1xn vector of 1's\n", 137 | "un = numpy.ones((ny, nx)) \n", 138 | "\n", 139 | "###Assign initial conditions\n", 140 | "\n", 141 | "u[int(.5 / dy): int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "With our initial conditions all set up, let's first try running our original nested loop code, making use of the iPython \"magic\" function `%%timeit`, which will help us evaluate the performance of our code. \n", 149 | "\n", 150 | "**Note**: The `%%timeit` magic function will run the code several times and then give an average execution time as a result. If you have any figures being plotted within a cell where you run `%%timeit`, it will plot those figures repeatedly which can be a bit messy. \n", 151 | "\n", 152 | "The execution times below will vary from machine to machine. Don't expect your times to match these times, but you _should_ expect to see the same general trend in decreasing execution time as we switch to array operations." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 5, 158 | "metadata": {}, 159 | "outputs": [ 160 | { 161 | "name": "stdout", 162 | "output_type": "stream", 163 | "text": [ 164 | "3.07 s ± 15.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" 165 | ] 166 | } 167 | ], 168 | "source": [ 169 | "%%timeit\n", 170 | "u = numpy.ones((ny, nx))\n", 171 | "u[int(.5 / dy): int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2\n", 172 | "\n", 173 | "for n in range(nt + 1): ##loop across number of time steps\n", 174 | " un = u.copy()\n", 175 | " row, col = u.shape\n", 176 | " for j in range(1, row):\n", 177 | " for i in range(1, col):\n", 178 | " u[j, i] = (un[j, i] - (c * dt / dx * \n", 179 | " (un[j, i] - un[j, i - 1])) - \n", 180 | " (c * dt / dy * \n", 181 | " (un[j, i] - un[j - 1, i])))\n", 182 | " u[0, :] = 1\n", 183 | " u[-1, :] = 1\n", 184 | " u[:, 0] = 1\n", 185 | " u[:, -1] = 1" 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "metadata": {}, 191 | "source": [ 192 | "With the \"raw\" Python code above, the mean execution time achieved was 3.07 seconds (on a MacBook Pro Mid 2012). Keep in mind that with these three nested loops, that the statements inside the **j** loop are being evaluated more than 650,000 times. Let's compare that with the performance of the same code implemented with array operations:" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 6, 198 | "metadata": {}, 199 | "outputs": [ 200 | { 201 | "name": "stdout", 202 | "output_type": "stream", 203 | "text": [ 204 | "7.38 ms ± 105 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" 205 | ] 206 | } 207 | ], 208 | "source": [ 209 | "%%timeit\n", 210 | "u = numpy.ones((ny, nx))\n", 211 | "u[int(.5 / dy): int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2\n", 212 | "\n", 213 | "for n in range(nt + 1): ##loop across number of time steps\n", 214 | " un = u.copy()\n", 215 | " u[1:, 1:] = (un[1:, 1:] - (c * dt / dx * (un[1:, 1:] - un[1:, 0:-1])) -\n", 216 | " (c * dt / dy * (un[1:, 1:] - un[0:-1, 1:])))\n", 217 | " u[0, :] = 1\n", 218 | " u[-1, :] = 1\n", 219 | " u[:, 0] = 1\n", 220 | " u[:, -1] = 1" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "As you can see, the speed increase is substantial. The same calculation goes from 3.07 seconds to 7.38 milliseconds. 3 seconds isn't a huge amount of time to wait, but these speed gains will increase exponentially with the size and complexity of the problem being evaluated. " 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 7, 233 | "metadata": {}, 234 | "outputs": [ 235 | { 236 | "data": { 237 | "text/html": [ 238 | "\n", 239 | "\n", 240 | "\n", 241 | "\n", 306 | "\n" 321 | ], 322 | "text/plain": [ 323 | "" 324 | ] 325 | }, 326 | "execution_count": 7, 327 | "metadata": {}, 328 | "output_type": "execute_result" 329 | } 330 | ], 331 | "source": [ 332 | "from IPython.core.display import HTML\n", 333 | "def css_styling():\n", 334 | " styles = open(\"../styles/custom.css\", \"r\").read()\n", 335 | " return HTML(styles)\n", 336 | "css_styling()" 337 | ] 338 | } 339 | ], 340 | "metadata": { 341 | "kernelspec": { 342 | "display_name": "Python 3", 343 | "language": "python", 344 | "name": "python3" 345 | }, 346 | "language_info": { 347 | "codemirror_mode": { 348 | "name": "ipython", 349 | "version": 3 350 | }, 351 | "file_extension": ".py", 352 | "mimetype": "text/x-python", 353 | "name": "python", 354 | "nbconvert_exporter": "python", 355 | "pygments_lexer": "ipython3", 356 | "version": "3.6.8" 357 | } 358 | }, 359 | "nbformat": 4, 360 | "nbformat_minor": 1 361 | } 362 | -------------------------------------------------------------------------------- /lessons/11_Defining_Function_in_Python.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "raw", 5 | "metadata": {}, 6 | "source": [ 7 | "Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved BSD-3 license. (c) Lorena A. Barba, Gilbert F. Forsyth 2017. Thanks to NSF for support via CAREER award #1149784." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "[@LorenaABarba](https://twitter.com/LorenaABarba)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "12 steps to Navier–Stokes\n", 22 | "=====\n", 23 | "***" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "This lesson complements the first interactive module of the online [CFD Python](https://github.com/barbagroup/CFDPython) class, by Prof. Lorena A. Barba, called **12 Steps to Navier–Stokes.** The interactive module starts with simple exercises in 1D that at first use little of the power of Python. We now present some new ways of doing the same things that are more efficient and produce prettier code.\n", 31 | "\n", 32 | "This lesson was written with BU graduate student Gilbert Forsyth.\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "Defining Functions in Python \n", 40 | "----" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "In steps 1 through 8, we wrote Python code that is meant to run from top to bottom. We were able to reuse code (to great effect!) by copying and pasting, to incrementally build a solver for the Burgers' equation. But moving forward there are more efficient ways to write our Python codes. In this lesson, we are going to introduce *function definitions*, which will allow us more flexibility in reusing and also in organizing our code. \n", 48 | "\n", 49 | "We'll begin with a trivial example: a function which adds two numbers. \n", 50 | "\n", 51 | "To create a function in Python, we start with the following:\n", 52 | "\n", 53 | " def simpleadd(a,b):\n", 54 | "\n", 55 | "This statement creates a function called `simpleadd` which takes two inputs, `a` and `b`. Let's execute this definition code." 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 1, 61 | "metadata": { 62 | "collapsed": false 63 | }, 64 | "outputs": [], 65 | "source": [ 66 | "def simpleadd(a, b):\n", 67 | " return a+b" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "The `return` statement tells Python what data to return in response to being called. Now we can try calling our `simpleadd` function:" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 2, 80 | "metadata": { 81 | "collapsed": false 82 | }, 83 | "outputs": [ 84 | { 85 | "data": { 86 | "text/plain": [ 87 | "7" 88 | ] 89 | }, 90 | "execution_count": 2, 91 | "metadata": {}, 92 | "output_type": "execute_result" 93 | } 94 | ], 95 | "source": [ 96 | "simpleadd(3, 4)" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "Of course, there can be much more happening between the `def` line and the `return` line. In this way, one can build code in a *modular* way. Let's try a function which returns the `n`-th number in the Fibonacci sequence. " 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 3, 109 | "metadata": { 110 | "collapsed": false 111 | }, 112 | "outputs": [], 113 | "source": [ 114 | "def fibonacci(n):\n", 115 | " a, b = 0, 1\n", 116 | " for i in range(n):\n", 117 | " a, b = b, a + b\n", 118 | " return a\n", 119 | " " 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": 4, 125 | "metadata": { 126 | "collapsed": false 127 | }, 128 | "outputs": [ 129 | { 130 | "data": { 131 | "text/plain": [ 132 | "13" 133 | ] 134 | }, 135 | "execution_count": 4, 136 | "metadata": {}, 137 | "output_type": "execute_result" 138 | } 139 | ], 140 | "source": [ 141 | "fibonacci(7)" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "Once defined, the function `fibonacci` can be called like any of the built-in Python functions that we've already used. For exmaple, we might want to print out the Fibonacci sequence up through the `n`-th value:" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 5, 154 | "metadata": { 155 | "collapsed": false 156 | }, 157 | "outputs": [ 158 | { 159 | "name": "stdout", 160 | "output_type": "stream", 161 | "text": [ 162 | "0\n", 163 | "1\n", 164 | "1\n", 165 | "2\n", 166 | "3\n", 167 | "5\n", 168 | "8\n", 169 | "13\n", 170 | "21\n", 171 | "34\n" 172 | ] 173 | } 174 | ], 175 | "source": [ 176 | "for n in range(10):\n", 177 | " print(fibonacci(n))" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": {}, 183 | "source": [ 184 | "We will use the capacity of defining our own functions in Python to help us build code that is easier to reuse, easier to maintain, easier to share!" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "##### Exercise" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": {}, 197 | "source": [ 198 | "(Pending.)" 199 | ] 200 | }, 201 | { 202 | "cell_type": "markdown", 203 | "metadata": {}, 204 | "source": [ 205 | "Learn more\n", 206 | "-----\n", 207 | "***" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "Remember our short detour on using [array operations with NumPy](./07_Step_5.ipynb)?\n", 215 | "\n", 216 | "Well, there are a few more ways to make your scientific codes in Python run faster. We recommend the article on the Technical Discovery blog about [Speeding Up Python](http://technicaldiscovery.blogspot.com/2011/06/speeding-up-python-numpy-cython-and.html) (June 20, 2011), which talks about NumPy, Cython and Weave. It uses as example the Laplace equation (which we will solve in [Step 9](./12_Step_9.ipynb)) and makes neat use of defined functions.\n", 217 | "\n", 218 | "But a recent new way to get fast Python codes is [Numba](http://numba.pydata.org). We'll learn a bit about that after we finish the **12 steps to Navier–Stokes**.\n", 219 | "\n", 220 | "There are many exciting things happening in the world of high-performance Python right now!\n", 221 | "\n", 222 | "***" 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": 6, 228 | "metadata": { 229 | "collapsed": false 230 | }, 231 | "outputs": [ 232 | { 233 | "data": { 234 | "text/html": [ 235 | "\n", 236 | "\n", 237 | "\n", 238 | "\n", 303 | "\n" 318 | ], 319 | "text/plain": [ 320 | "" 321 | ] 322 | }, 323 | "execution_count": 6, 324 | "metadata": {}, 325 | "output_type": "execute_result" 326 | } 327 | ], 328 | "source": [ 329 | "from IPython.core.display import HTML\n", 330 | "def css_styling():\n", 331 | " styles = open(\"../styles/custom.css\", \"r\").read()\n", 332 | " return HTML(styles)\n", 333 | "css_styling()" 334 | ] 335 | }, 336 | { 337 | "cell_type": "markdown", 338 | "metadata": {}, 339 | "source": [ 340 | "> (The cell above executes the style for this notebook.)" 341 | ] 342 | } 343 | ], 344 | "metadata": { 345 | "kernelspec": { 346 | "display_name": "Python 3", 347 | "language": "python", 348 | "name": "python3" 349 | }, 350 | "language_info": { 351 | "codemirror_mode": { 352 | "name": "ipython", 353 | "version": 3 354 | }, 355 | "file_extension": ".py", 356 | "mimetype": "text/x-python", 357 | "name": "python", 358 | "nbconvert_exporter": "python", 359 | "pygments_lexer": "ipython3", 360 | "version": "3.5.2" 361 | } 362 | }, 363 | "nbformat": 4, 364 | "nbformat_minor": 0 365 | } 366 | -------------------------------------------------------------------------------- /paper.bib: -------------------------------------------------------------------------------- 1 | %% This BibTeX bibliography file was created using BibDesk. 2 | %% http://bibdesk.sourceforge.net/ 3 | 4 | 5 | %% Created for Lorena A Barba at 2018-07-01 11:19:20 -0400 6 | 7 | 8 | %% Saved with string encoding Unicode (UTF-8) 9 | 10 | 11 | 12 | @article{chen2015worked, 13 | Author = {Chen, Ouhao and Kalyuga, Slava and Sweller, John}, 14 | Date-Added = {2018-07-01 15:18:29 +0000}, 15 | Date-Modified = {2018-07-01 15:19:04 +0000}, 16 | Doi = {10.1037/edu0000018}, 17 | Journal = {Journal of Educational Psychology}, 18 | Number = {3}, 19 | Pages = {689}, 20 | Publisher = {American Psychological Association}, 21 | Title = {The worked example effect, the generation effect, and element interactivity.}, 22 | Volume = {107}, 23 | Year = {2015}} 24 | 25 | @article{sweller2006worked, 26 | Author = {Sweller, John}, 27 | Date-Added = {2018-07-01 15:15:07 +0000}, 28 | Date-Modified = {2018-07-01 15:15:42 +0000}, 29 | Doi = {10.1016/j.learninstruc.2006.02.005}, 30 | Journal = {Learning and instruction}, 31 | Volume = {16}, 32 | Number = {2}, 33 | Pages = {165--169}, 34 | Doi = {10.1016/j.learninstruc.2006.02.005}, 35 | Publisher = {Elsevier Science}, 36 | Title = {The worked example effect and human cognition}, 37 | Year = {2006}} 38 | 39 | @book{rossant2018ipython, 40 | Author = {Rossant, Cyrille}, 41 | Date-Added = {2018-07-01 01:48:26 +0000}, 42 | Date-Modified = {2018-07-01 01:49:32 +0000}, 43 | Publisher = {Packt Publishing Ltd.}, 44 | Title = {IPython Interactive Computing and Visualization Cookbook: Over 100 hands-on recipes to sharpen your skills in high-performance numerical computing and data science in the Jupyter Notebook}, 45 | Year = {2018}} 46 | 47 | @inproceedings{ketcheson2014-scipy, 48 | Author = {Ketcheson, David I.}, 49 | Date-Added = {2018-07-01 01:44:36 +0000}, 50 | Date-Modified = {2018-07-01 01:47:12 +0000}, 51 | Note = {http://conference.scipy.org/proceedings/scipy2014/pdfs/ketcheson.pdf}, 52 | Title = {Teaching numerical methods with {IPython} notebooks and inquiry-based learning}, 53 | Year = {2014}, 54 | booktitle = { {P}roceedings of the 13th {P}ython in {S}cience {C}onference }, 55 | pages = { 19 -- 25 }, 56 | url = {http://hdl.handle.net/10754/346689}, 57 | editor = { {S}t\'efan van der {W}alt and {J}ames {B}ergstra }} 58 | -------------------------------------------------------------------------------- /paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'CFD Python: the 12 steps to Navier-Stokes equations' 3 | tags: 4 | - Python 5 | - CFD 6 | - differential equations 7 | - numerical methods 8 | - finite differences 9 | authors: 10 | - name: Lorena A. Barba 11 | orcid: 0000-0001-5812-2711 12 | affiliation: 1 13 | - name: Gilbert F. Forsyth 14 | orcid: 0000-0002-4983-1978 15 | affiliation: "1, 2" 16 | affiliations: 17 | - name: The George Washington University 18 | index: 1 19 | - name: Capital One 20 | index: 2 21 | date: 30 June 2018 22 | bibliography: paper.bib 23 | --- 24 | 25 | # Summary 26 | 27 | The **CFD Python** learning module is a set of Jupyter notebooks, consisting of 12 "core" lessons, 3 "bonus" lessons, and a "lesson zero" as a quick intro to Python for numerical computing. 28 | This practical module takes students through 12 steps, incrementally guiding them to program a solution to the two-dimensional Navier--Stokes equation, using finite differences. 29 | The steps are the following: 30 | 31 | * Steps 1--4 are in one dimension: (i) linear convection with a step-function initial condition (IC) and appropriate boundary conditions (BC); with the same IC/BCs: (ii) nonlinear convection, and (iii) diffusion only; (iv) Burgers’ equation, with a saw-tooth IC and periodic BCs. 32 | * Steps 5--10 are in two dimensions: (v) linear convection with a square function IC and appropriate BCs; with the same IC/BCs: (vi) nonlinear convection, and (vii) diffusion only; (viii) Burgers’ equation; (ix) Laplace equation, with zero IC and both Neumann and Dirichlet BCs; (x) Poisson equation in 2D. 33 | * Steps 11--12 solve the Navier--Stokes equation in 2D: (xi) cavity flow; (xii) channel flow. 34 | 35 | Students learn many valuable lessons as the module guides them through these steps (they should not skip any!). The incremental nature of the exercises means they get a sense of achievement at the end of each lesson, and they feel they are learning with low effort. As they progress, they naturally practice code re-use and they incrementally learn programming and plotting techniques. As they analyze their results, they learn about numerical diffusion, accuracy, and convergence. In about four weeks, they become moderately proficient programmers and are motivated to start discussing more theoretical matters. 36 | 37 | # Statement of need 38 | 39 | Many university courses in computational fluid dynamics (CFD) follow a similar order of topics. As reflected in various textbooks, the course usually starts with the subject of interpolation, going on to numerical integration of ordinary differential equations, and continuing to standard material on partial differential equations. Students encounter theory of stability, order of convergence, and numerical diffusion, but they often do not gain much experience programming. 40 | This learning module places emphasis on practical experience with the _programming_ of numerical solutions to fundamental mathematical models that can represent fluid behavior. 41 | It is unique in its approach of taking a beginner in a step-by-step fashion to complete the solution of a fairly complex numerical problem: two-dimensional cavity flow via the Navier--Stokes equations, discretized with finite differences. 42 | 43 | The "12 steps to Navier--Stokes" lessons have proved effectiveness. They were used in the classroom as part of a university course for four years in a row (Boston University, 2009 to 2013), guiding several dozen students to develop their Navier--Stokes solutions. 44 | Written as a set of Jupyter notebooks, the module was the backbone of an intensive tutorial held as part of the 2013 Latin American Symposium on High-Performance Computing in Mendoza, Argentina ([http://ecar2013.hpclatam.org](https://web.archive.org/web/20181111235234/http://ecar2013.hpclatam.org)). 45 | 46 | The module is complete and can be easily adopted by other instructors who wish to teach CFD using a practical approach. 47 | An instructor can complement the lessons with brief presentations, class discussions, and readings. 48 | For example, after students experiment with different values of the parameters in the first two steps, they will encounter situations when the solution blows up, due to numerical instability. 49 | As they become perplexed of this behavior, the instructor can use the "bonus" lesson about the stability criterion of Courant--Friedrichs--Lewy (CFL), and complement with a brief lecture on the concepts of consistency, stability and convergence. 50 | 51 | We know that instructors around the world have already adopted these lessons: many of them have written us with expressions of gratitude (or with improvement suggestions). The collection was translated to Spanish by a volunteer (Fran Navarro) in 2015 (https://github.com/franktoffel/CFDPython-ES), another user (Manuel Ramsaier) made screencast videos with Matlab versions of the lessons (https://youtu.be/QOeTk6C6dZI), and a professor in Singapore (Claus-Dieter Ohl) incorporates the lessons in his local CFD course (http://cav2012.sg/cdohl/CFD_course/). 52 | After Barba left Boston University, the instructor who took over the CFD course there continued to use the module. She writes: 53 | "The 12 steps are great for building a student's computational toolbox, building her understanding of numerical methods, and providing some benchmark solutions for validating full CFD simulations. I use these 12 steps in the beginning weeks of my graduate level CFD course every year. They help lay the perfect foundation for further analysis and use of available CFD tools." (Prof. Sheryl Grace, 2018.) 54 | 55 | The lessons are also often mentioned in posts on sites like [CFD Online](https://www.cfd-online.com/), [Quora](https://www.quora.com), and others, and they are cited in a SciPy Conference paper [@ketcheson2014-scipy] and a book [@rossant2018ipython]. A portion of the module was translated to use the Devito package (a finite-difference code-generation tool based on SymPy), as part of their [tutorials](http://www.opesci.org/devito/tutorials.html). Various translations of the lessons to other programming languages can be found online. 56 | 57 | # Notes on instructional design 58 | 59 | Based on the experience of the authors after developing the **CFD Python** learning module, and other modules following a similar approach, we adopted this basic design pattern for creating lessons using _computable content_: 60 | 61 | 1. Break it down into small steps 62 | 2. Chunk small steps into bigger steps 63 | 3. Add narrative and connect 64 | 4. Link out to documentation 65 | 5. Interleave easy exercises 66 | 6. Spice with challenge questions/tasks 67 | 7. Publish openly online 68 | 69 | Regarding the inclusion of step-by-step, incremental code exposition, some critics are hesitant with "giving the solution" to the learners, and propose an alternative approach where the learners have to struggle and build their code solutions from scratch. 70 | When teaching novices, however, several studies show significant learning improvements when using worked examples, versus problem-solving with no guidance. This is called the "worked-example effect," and it is a widely studied cognitive-load effect [@sweller2006worked, @chen2015worked]. 71 | It applies particularly when teaching complex material to novice learners, resulting in heavy working-memory load: strong guidance is likely to result in enhanced performance, in this case. 72 | 73 | Other learning modules following this design pattern include the AeroPython lessons in classical aerodynamics (https://github.com/barbagroup/AeroPython) and the five modules of the course "Practical Numerical Methods with Python," a.k.a., Numerical MOOC (https://github.com/numerical-mooc/numerical-mooc). 74 | 75 | # References 76 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ipywidgets==7.2.1 2 | jupyter==1.0.0 3 | numpy==1.14.3 4 | matplotlib==2.2.2 5 | scipy==1.1.0 6 | sympy==1.1.1 7 | -------------------------------------------------------------------------------- /styles/custom.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 69 | 84 | --------------------------------------------------------------------------------